Java8的新时间API和解析ISO8601日期字符串的异同

http://blog.csdn.net/chenleixing/article/details/44408875
https://segmentfault.com/q/1010000007862493?_ea=1478023
https://zhuanlan.zhihu.com/p/28133858

现有API存在的问题:

  • 线程安全: Date和Calendar不是线程安全的,你需要编写额外的代码处理线程安全问题
  • API设计和易用性: 由于Date和Calendar的设计不当你无法完成日常的日期操作
  • ZonedDate和Time: 你必须编写额外的逻辑处理时区和那些旧的逻辑
  • 好在JSR 310规范中为Java8添加了新的API,
    在java.time包中,新的API纠正了过去的缺陷
    新的日期API

        ZoneId: 时区ID,用来确定Instant和LocalDateTime互相转换的规则
        Instant: 用来表示时间线上的一个点
        LocalDate: 表示没有时区的日期, LocalDate是不可变并且线程安全的
        LocalTime: 表示没有时区的时间, LocalTime是不可变并且线程安全的
        LocalDateTime: 表示没有时区的日期时间, LocalDateTime是不可变并且线程安全的
        Clock: 用于访问当前时刻、日期、时间,用到时区
        Duration: 用秒和纳秒表示时间的数量
    

    最常用的就是LocalDate、LocalTime、LocalDateTime了,从它们的名字就可以看出是操作日期
    和时间的。这些类是主要用于当时区不需要显式地指定的上下文。在本章节中我们将讨论最常用的api。
    LocalDate:

    // 根据字符串取:
    LocalDate localDate= LocalDate.parse("2014-02-28"); // 严格按照ISO yyyy-MM-dd验证,02写成2都不行,当然也有一个重载方法允许自己定义格式
    // 获取当天的日期
    LocalDate today = LocalDate.now(); System.out.println("Today's Local date : " + today); 
    //获取当前的年月日
    LocalDate today = LocalDate.now(); 
    int year = today.getYear(); 
    int month = today.getMonthValue(); 
    int day = today.getDayOfMonth(); 
    System.out.printf("Year : %d Month : %d day : %d \t %n", year, month, day); 
    Output 
    Today's Local date : 2014-01-14 
    Year : 2014 Month : 1 day : 14
    // 获取某个特定的日期
    LocalDate dateOfBirth = LocalDate.of(2010, 01, 14); 
    System.out.println("Your Date of birth is : " + dateOfBirth); 
    Output : Your Date of birth is : 2010-01-14
    // 取本月第1天:
    LocalDate firstDayOfThisMonth = localDate.with(TemporalAdjusters.firstDayOfMonth()); 
    // 取本月第2天:
    LocalDate secondDayOfThisMonth = localDate.withDayOfMonth(2); 
    // 取本月最后一天
    LocalDate lastDayOfThisMonth = localDate.with(TemporalAdjusters.lastDayOfMonth()); 
    // 取当前时间的第一个周一
    LocalDate firstMondayOfThisTime = LocalDate.parse("2017-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2017-01-02
    // 取当前时间的前一天
    LocalDate beforeDayOfThisTime = LocalDate.parse("2017-01-01").minusDays(1);
    // 取当前时间的后一天
    LocalDate afterDayOfThisTime = LocalDate.parse("2017-01-01").plusDays(1);
    //Date转LocalDate
    Date date = new Date();
    LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    //LocalDate 转 Date
    LocalDateTime localDateTime = LocalDateTime.now();
    Date date = Date.from(localDateTime.toInstant(ZoneOffset.UTC));
    //检查闰年
    if(today.isLeapYear()){ 
        System.out.println("This year is Leap year"); 
    }else { 
        System.out.println("2014 is not a Leap year"); 
    Output: 2014 is not a Leap year
    //返回两个日期之间的区间并以字符串集合方式返回
    public static List<String> collectLocalDates(String timeStart, String timeEnd){
            LocalDate start=LocalDate.parse(timeStart);LocalDate end=LocalDate.parse(timeEnd);
            // 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
            return Stream.iterate(start, localDate -> localDate.plusDays(1))
                    // 截断无限流,长度为起始时间和结束时间的差+1个
                    .limit(ChronoUnit.DAYS.between(start, end) + 1)
                    // 由于最后要的是字符串,所以map转换一下
                    .map(LocalDate::toString)
                    // 把流收集为List
                    .collect(Collectors.toList());
    ====================================LocalDate API==================================
    getYear()    int    获取当前日期的年份
    getMonth()    Month    获取当前日期的月份对象
    getMonthValue()    int    获取当前日期是第几月
    getDayOfWeek()    DayOfWeek    表示该对象表示的日期是星期几
    getDayOfMonth()    int    表示该对象表示的日期是这个月第几天
    getDayOfYear()    int    表示该对象表示的日期是今年第几天
    withYear(int year)    LocalDate    修改当前对象的年份
    withMonth(int month)    LocalDate    修改当前对象的月份
    withDayOfMonth(int dayOfMonth)    LocalDate    修改当前对象在当月的日期
    isLeapYear()    boolean    是否是闰年
    lengthOfMonth()    int    这个月有多少天
    lengthOfYear()    int    该对象表示的年份有多少天(365或者366)
    plusYears(long yearsToAdd)    LocalDate    当前对象增加指定的年份数
    plusMonths(long monthsToAdd)    LocalDate    当前对象增加指定的月份数
    plusWeeks(long weeksToAdd)    LocalDate    当前对象增加指定的周数
    plusDays(long daysToAdd)    LocalDate    当前对象增加指定的天数
    minusYears(long yearsToSubtract)    LocalDate    当前对象减去指定的年数
    minusMonths(long monthsToSubtract)    LocalDate    当前对象减去注定的月数
    minusWeeks(long weeksToSubtract)    LocalDate    当前对象减去指定的周数
    minusDays(long daysToSubtract)    LocalDate    当前对象减去指定的天数
    compareTo(ChronoLocalDate other)    int    比较当前对象和other对象在时间上的大小,返回值如果为正,则当前对象时间较晚,
    isBefore(ChronoLocalDate other)    boolean    比较当前对象日期是否在other对象日期之前
    isAfter(ChronoLocalDate other)    boolean    比较当前对象日期是否在other对象日期之后
    isEqual(ChronoLocalDate other)    boolean    比较两个日期对象是否相等
    

    SimpleDateFormat Joda-Time Apache-DateUtils 解析ISO8601日期字符串的异同
    https://www.cnblogs.com/harryzhang/archive/2016/06/07/SimpleDateFormat_ISO8601_FormatString.html

     import java.util.Date;
     import java.text.SimpleDateFormat;
     import org.apache.commons.lang3.time.DateUtils;
     import org.joda.time.DateTime;
     import org.joda.time.format.DateTimeFormat;
     String datestr1 = "2016-06-07T14:08:09.235+08:00";  //ISO8601
     String datestr2 = "2016-06-07 14:08:09";        //Without Millis
     String datestr3 = "2016-06-07 14:08:09.235";      //With Millis
     //Joda Time
     Date date0 = new DateTime(datestr1).toDate();
     Date date1 = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").parseDateTime(datestr1).toDate();
     Date date2 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime(datestr2).toDate();
     Date date3 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS").parseDateTime(datestr3).toDate();
     //JDK
     date1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse(datestr1);    //XXX means Timezone for Java SimpleDateFormat
     date2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(datestr2);
     date3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(datestr3);
     //Apache
     date1 = DateUtils.parseDate(datestr1, "yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
     date2 = DateUtils.parseDate(datestr2, "yyyy-MM-dd HH:mm:ss");
     date3 = DateUtils.parseDate(datestr3, "yyyy-MM-dd HH:mm:ss.SSS");
    测试中使用了三种常见格式的日期字符串,其中包括Json转换默认的ISO8601格式。经测试SimpleDateFormat Joda-Time Apache-DateUtils三种方式都能实现对这三种日期格式的解析,其中:
    1. SimpleDateFormat 解析ISO8601的格式串为 "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",其中XXX表示时区,与通用的ZZ不一样。
    2. Joda-Time 默认格式就是ISO8601,故可以直接用 new DateTime(...) 的方式直接解析ISO8601日期串,当然也可以用日期格式串来解析,用日期格式串解析日期的代码略长。