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);
  • 相关阅读:
    [CQOI2007]涂色
    NOI.AC NOIP模拟赛 第五场 游记
    AGC018D Tree and Hamilton Path
    AGC001E BBQ Hard
    LOJ6089 小Y的背包计数问题
    UOJ272 【清华集训2016】石家庄的工人阶级队伍比较坚强
    Gym102538A Airplane Cliques
    AT5762 Preserve Diameter
    CF1030G Linear Congruential Generator
    CF1149E Election Promises
  • 原文地址:https://www.cnblogs.com/bjlhx/p/6927138.html
Copyright © 2011-2022 走看看