zoukankan      html  css  js  c++  java
  • Java-小技巧-004-jdk时间Date,jdk8时间,joda,calendar,获取当前时间前一周、前一月、前一年的时间

    一、常用的Date【不推荐】

    1.1、基础用法

    示例

        @Test
        public void testDate() throws Exception {
            Date date=new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateStr="2019-07-23 08:17:01";
            //Date→ String
            String format = sdf.format(date);
            System.out.println(format);
            //String →  Date
            Date date1 = sdf.parse(dateStr);
            System.out.println(date1);
    //        2019-07-23 08:25:03
    //        Tue Jul 23 08:17:01 CST 2019
        }

    1.2、线程不安全示例

        @Test
        public void testParse() {
            ExecutorService executorService = Executors.newCachedThreadPool();
            List<String> dateStrList = Arrays.asList(
                    "2018-04-01 10:00:01",
                    "2018-04-02 11:00:02",
                    "2018-04-03 12:00:03",
                    "2018-04-04 13:00:04",
                    "2018-04-05 14:00:05"
            );
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            for (String str : dateStrList) {
                executorService.execute(() -> {
                    try {
                        simpleDateFormat.parse(str);
                        TimeUnit.SECONDS.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
        }

    输出

    java.lang.NumberFormatException: multiple points
        at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
        at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
        at java.lang.Double.parseDouble(Double.java:538)
        at java.text.DigitList.getDouble(DigitList.java:169)
        at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
        at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)

    线程不安全说明:  

    SimpleDateFormat继承了DateFormat,公用了一个变量
    public abstract class DateFormat extends Format {
    
        protected Calendar calendar;

    在parse方法的最后,会调用CalendarBuilder的establish方法,入参就是SimpleDateFormat维护的Calendar实例,在establish方法中会调用calendar的clear方法,如下:

    parsedDate = calb.establish(calendar).getTime();
        Calendar establish(Calendar cal) {
            boolean weekDate = isSet(WEEK_YEAR)
                                && field[WEEK_YEAR] > field[YEAR];
            if (weekDate && !cal.isWeekDateSupported()) {
                // Use YEAR instead
                if (!isSet(YEAR)) {
                    set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
                }
                weekDate = false;
            }
    
            cal.clear();

    可知SimpleDateFormat维护的用于format和parse方法计算日期-时间的calendar被清空了,如果此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题!

    1.3、解决线程安全的方案

    1.3.1、加synchronized锁

    以synchronized同步SimpleDateFormat对象。

        static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        public static synchronized Date parse(String s) throws ParseException {
            return sdf.parse(s);
        }

    串行执行,在大批量调用效率很低,不推荐

    高并发时,使用该对象会出现阻塞,当前使用者使用时,其他使用者等待,尽管结果是对的,但是并发成了排队,实际上并没有解决问题,还会对性能以及效率造成影响。

    1.3.2、每次调用创建一个对象

    public Date parse(String s) throws ParseException {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return sdf.parse(s);
        }

    缺点,耗内存,GC压力增大,每次创建对象销毁,分配和销毁内存空间,整理内存,GC。不推荐

    SimpleDateFormat中使用了Calendar对象,由于该对象相当重,在高并发的情况下会大量的new SimpleDateFormat以及销毁SimpleDateFormat,极其耗费资源。

    1.3.3、每个线程创建一个

    static ThreadLocal<SimpleDateFormat> localSdf= new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue(){
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }

    推荐使用ThreadLocal,每个线程一个对象,提高format对象利用率

    注意:使用ThreadLocal的Thread均持有ThreadLocal自定义的map对象,key是ThreadLocal对象的弱引用,随ThreadLocal的GC或者内存不足就会GC,value是我们创建的对象是强引用,可能一直存在。因此ThreadLocal调用结束后,需要显示调用remove()方法

    使用ThreadLocal时,如果执行原子任务的过程是每一个线程执行一个任务,那么这样的声明基本和每次使用前创建实例对象是没区别的;如果使用的是多线程加任务队列,举个例子,tomcat有m个处理线程,外部有n个待处理任务请求,那么当执行n个任务时,其实只会创建m个SimpleDateFormat实例,对于单一的处理线程,执行任务是有序的,所以对于当前线程而言,不存在并发。

    二、java8 localdate【推荐】

    LocalDateTime,Date,String 互转

        @Test
        // 01. java.util.Date --> java.time.LocalDateTime
        public void UDateToLocalDateTime() {
            java.util.Date date = new java.util.Date();
            Instant instant = date.toInstant();
            ZoneId zone = ZoneId.systemDefault();
            LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        }
    
        @Test
        // 02. java.util.Date --> java.time.LocalDate
        public void UDateToLocalDate() {
            java.util.Date date = new java.util.Date();
            Instant instant = date.toInstant();
            ZoneId zone = ZoneId.systemDefault();
            LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
            LocalDate localDate = localDateTime.toLocalDate();
        }
        @Test
        // 03. java.util.Date --> java.time.LocalTime
        public void UDateToLocalTime() {
            java.util.Date date = new java.util.Date();
            Instant instant = date.toInstant();
            ZoneId zone = ZoneId.systemDefault();
            LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
            LocalTime localTime = localDateTime.toLocalTime();
        }
    
        @Test
        // 04. java.time.LocalDateTime --> java.util.Date
        public void LocalDateTimeToUdate() {
            LocalDateTime localDateTime = LocalDateTime.now();
            ZoneId zone = ZoneId.systemDefault();
            Instant instant = localDateTime.atZone(zone).toInstant();
            java.util.Date date = Date.from(instant);
        }
    
    
        @Test
        // 05. java.time.LocalDate --> java.util.Date
        public void LocalDateToUdate() {
            LocalDate localDate = LocalDate.now();
            ZoneId zone = ZoneId.systemDefault();
            Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
            java.util.Date date = Date.from(instant);
        }
    
        @Test
        // 06. java.time.LocalTime --> java.util.Date
        public void LocalTimeToUdate() {
            LocalTime localTime = LocalTime.now();
            LocalDate localDate = LocalDate.now();
            LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
            ZoneId zone = ZoneId.systemDefault();
            Instant instant = localDateTime.atZone(zone).toInstant();
            java.util.Date date = Date.from(instant);
        }
    
        @Test
        // 07. java.time.LocalDateTime --> String
        public void localDateTimeToString() {
            LocalDateTime localDateTime = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String format = localDateTime.format(formatter);
            System.out.println(format);
        }
    
    
        @Test
        // 08. String - - > java.time.LocalDateTime
        public void stringTolocalDateTime() {
            String dateStr="2019-07-23 09:49:19";
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
            System.out.println(dateTime);
        }

    时间加减

    LocalDateTime localDateTime3 = LocalDateTime.now();
    LocalDate.now();
    LocalTime.now();
    localDateTime3.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    LocalDateTime localDateTime4 = localDateTime3.minus(23,ChronoUnit.MONTHS);
    localDateTime4.atZone(ZoneId.systemDefault());
    localDateTime4 = localDateTime4.withHour(3);
    localDateTime4 = localDateTime4.withYear(2016);
    localDateTime4 = localDateTime4.with(ChronoField.MONTH_OF_YEAR,3);

    间隔计算

    使用Duration进行 day,hour,minute,second等的计算
    使用Period进行Year,Month的计算

    Duration duration = Duration.between(localDateTime,localDateTime4);
    duration.toDays();
    duration.toHours();
    duration.toMinutes();
    Period period2 = Period.between(localDateTime.toLocalDate(),localDateTime4.toLocalDate());
    period2.getYears();
    period2.getMonths();
    period2.toTotalMonths(); 

    更多介绍、推荐使用java8 localdate等 线程安全 支持较好  地址

    三、joda

    3.1、问题背景

    3.1.1、使用jdk1.8 推荐使用二中方式

    3.1.2、在没有jdk1.8的time类库时

    1》使用SampleDateFormat存在问题

    2》Apache的 DateFormatUtils 与 FastDateFormat

      使用org.apache.commons.lang.time.FastDateFormat 与 org.apache.commons.lang.time.DateFormatUtils。

      存在的问题:

        apache保证是线程安全的,并且更高效。但是DateFormatUtils与FastDateFormat这两个类中只有format()方法,所有的format方法只接受long,Date,Calendar类型的输入,转换成时间串,目前不存在parse()方法,可由时间字符串转换为时间对象。

    3.2、Joda-Time

      使用Joda-Time类库。

    存在的问题:暂无

    1、使用maven包

    <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>2.9.9</version>
    </dependency>

    2、使用

    Joda-Time — 面向 Java 应用程序的日期/时间库的替代选择,Joda-Time 令时间和日期值变得易于管理、操作和理解。事实上,易于使用是 Joda 的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。并且 Joda 与 JDK 是百分之百可互操作的,因此您无需替换所有 Java 代码,只需要替换执行日期/时间计算的那部分代码。

     1.创建一个用时间表示的某个随意的时刻 — 比如,2015年12月21日0时0分

    DateTime dt = new DateTime(2015, 12, 21, 0, 0, 0, 333);// 年,月,日,时,分,秒,毫秒 

    2.格式化时间输出

    DateTime dateTime = new DateTime(2015, 12, 21, 0, 0, 0, 333);
    System.out.println(dateTime.toString("yyyy/MM/dd HH:mm:ss EE"));

    3.解析文本格式时间

    DateTimeFormatter format = DateTimeFormat .forPattern("yyyy-MM-dd HH:mm:ss");  
    DateTime dateTime = DateTime.parse("2015-12-21 23:22:45", format); 
    System.out.println(dateTime.toString("yyyy/MM/dd HH:mm:ss EE"));

    4.在某个日期上加上90天并输出结果

    DateTime dateTime = new DateTime(2016, 1, 1, 0, 0, 0, 0);
    System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");

    注意:plus后原值没变,返回一个新的Datetime

    5.到新年还有多少天 

    public Days daysToNewYear(LocalDate fromDate) {
      LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);
      return Days.daysBetween(fromDate, newYear);
    }

    6.与JDK日期对象的转换

    DateTime dt = new DateTime();  
    
    //转换成java.util.Date对象  
    Date d1 = new Date(dt.getMillis());  
    Date d2 = dt.toDate(); 

    7.时区

    //默认设置为日本时间  
    DateTimeZone.setDefault(DateTimeZone.forID("Asia/Tokyo"));  
    DateTime dt1 = new DateTime();  
    System.out.println(dt1.toString("yyyy-MM-dd HH:mm:ss"));
    
    //伦敦时间  
    DateTime dt2 = new DateTime(DateTimeZone.forID("Europe/London"));
    System.out.println(dt2.toString("yyyy-MM-dd HH:mm:ss"));

    8.计算间隔和区间

    DateTime begin = new DateTime("2015-02-01");  
    DateTime end = new DateTime("2016-05-01");  
    
    //计算区间毫秒数  
    Duration d = new Duration(begin, end);  
    long millis = d.getMillis();  
    
    //计算区间天数  
    Period p = new Period(begin, end, PeriodType.days());  
    int days = p.getDays();  
    
    //计算特定日期是否在该区间内  
    Interval interval = new Interval(begin, end);  
    boolean contained = interval.contains(new DateTime("2015-03-01")); 

    9.日期比较

    DateTime d1 = new DateTime("2015-10-01"); 
    DateTime d2 = new DateTime("2016-02-01"); 
    
    //和系统时间比  
    boolean b1 = d1.isAfterNow();  
    boolean b2 = d1.isBeforeNow();  
    boolean b3 = d1.isEqualNow();  
    
    //和其他日期比  
    boolean f1 = d1.isAfter(d2);  
    boolean f2 = d1.isBefore(d2);  
    boolean f3 = d1.isEqual(d2);  

    资料:

    Joda-Time 简介(中文)https://www.ibm.com/developerworks/cn/java/j-jodatime.html
    Joda-Time 文档(英文)http://joda-time.sourceforge.net/

    3、传统方式

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Calendar c = Calendar.getInstance();
             
            //过去七天
            c.setTime(new Date());
            c.add(Calendar.DATE, - 7);
            Date d = c.getTime();
            String day = format.format(d);
            System.out.println("过去七天:"+day);
             
            //过去一月
            c.setTime(new Date());
            c.add(Calendar.MONTH, -1);
            Date m = c.getTime();
            String mon = format.format(m);
            System.out.println("过去一个月:"+mon);
             
            //过去三个月
            c.setTime(new Date());
            c.add(Calendar.MONTH, -3);
            Date m3 = c.getTime();
            String mon3 = format.format(m3);
            System.out.println("过去三个月:"+mon3);
             
            //过去一年
            c.setTime(new Date());
            c.add(Calendar.YEAR, -1);
            Date y = c.getTime();
            String year = format.format(y);
            System.out.println("过去一年:"+year);
  • 相关阅读:
    Time Zone 【模拟时区转换】(HDU暑假2018多校第一场)
    HDU 1281 棋盘游戏 【二分图最大匹配】
    Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS换根 || 树形dp】
    Codeforces Round #527 (Div. 3) D2. Great Vova Wall (Version 2) 【思维】
    Codeforces Round #527 (Div. 3) D1. Great Vova Wall (Version 1) 【思维】
    Codeforces Round #528 (Div. 2, based on Technocup 2019 Elimination Round 4) C. Connect Three 【模拟】
    Avito Cool Challenge 2018 E. Missing Numbers 【枚举】
    Avito Cool Challenge 2018 C. Colorful Bricks 【排列组合】
    005 如何分析问题框架
    004 如何定义和澄清问题
  • 原文地址:https://www.cnblogs.com/bjlhx/p/6927138.html
Copyright © 2011-2022 走看看