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日期串,当然也可以用日期格式串来解析,用日期格式串解析日期的代码略长。

3. Apache-DateUtils  解析ISO8601的格式串和Joda一样是 "yyyy-MM-dd'T'HH:mm:ss.SSSZZ",解析代码相对Joda-Time更简洁。

吐槽一下:SimpleDateFormat用XXX来表示时区真是太变态了,浪费我半天时间才搞定,特此记录。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容