JDK8【新的日期时间 API】

旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

  • 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
  • 设计很差− Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
  • 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

  • Local(本地) − 简化了日期时间的处理,没有时区的问题。
  • Zoned(时区) − 通过制定的时区处理日期时间。

新的java.time包涵盖了所有处理日期时间日期/时间时区时刻(instants)过程(during)时钟(clock)的操作。


在java8中,java.time包下主要包含下面几个主要的类:

Instant:时间戳,相当于java.util的Date
LocalDate:只包含日期,比如:2016-10-20
LocalTime:只包含时间,比如:23:12:10
LocalDateTime:包含日期和时间,比如:2016-10-20 23:14:21
Duration:`【持续时间】`,计算两个“时间”的间隔
Period:`【一段时间】`,用于计算两个“日期”的间隔
ZoneOffset:时区偏移量,比如:+8:00
ZonedDateTime:可以得到特定时区的日期/时间
Clock:时钟,比如获取目前美国纽约的时间

时间格式化以DateTimeFormatter代替SimpleDateFormat

DateTimeFormatter:时间格式化

方法前缀的含义,统一了api:

of:静态工厂方法(用类名去调用)。
parse:静态工厂方法,关注于解析(用类名去调用)。
now: 静态工厂方法,用当前时间创建实例(用类名去调用)
get:获取某些东西的值。
is:检查某些东西的是否是true。
with:返回一个部分状态改变了的时间日期对象拷贝(单独一个with方法,参数为TemporalAdjusters类型)。
plus:返回一个时间增加了的、时间日期对象拷贝(如果参数是负数也能够有minus方法的效果)。
minus:返回一个时间减少了的、时间日期对象拷贝。
to:把当前时间日期对象转换成另外一个,可能会损失部分状态。
at:把这个对象与另一个对象组合起来,例如: date.atTime(time)。
format :根据某一个DateTimeFormatter格式化为字符串。

通过以上一系列方法,java.time完成了加、减、格式化、解析、从日期/时间中提取单独部分等任务。

java.time包里面的类实例如果用了上面的方法而被修改了,那么会返回一个新的实例过来,而不像Calendar那样可以在同一个实例进行不同的修改,体现了不可变。

具体使用

1. Instant 【瞬间】:时间戳,相当于java.util的Date

Instant用于表示一个时间戳,它与我们常使用的System.currentTimeMillis()有些类似,不过Instant可以精确到纳秒(Nano-Second),System.currentTimeMillis()方法只精确到毫秒(Milli-Second)。如果查看Instant源码,发现它的内部使用了两个常量,seconds表示从1970-01-01 00:00:00开始到现在的秒数,nanos表示纳秒部分(nanos的值不会超过999,999,999)。
JDK8获取时间戳特别简单。Instant类由一个静态的工厂方法now()可以返回当前时间戳。

        Instant instant = Instant.now();
        System.out.println("当前时间戳是:" + instant);//当前时间戳是:2018-09-06T10:14:29.460Z
        Date date = Date.from(instant);
        System.out.println("当前时间戳是:" + date);//当前时间戳是:Thu Sep 06 18:14:29 CST 2018
        instant = date.toInstant();

可以看到,当前时间戳是包含日期和时间的,与java.util.Date很类似,事实上Instant就是JDK8以前的Date,可以使用这两个类中的方法在这两个类型之间进行转换。

2. LocalDate

LocalDate类获取日期信息。格式为 2018-09-06

        LocalDate nowDate = LocalDate.now();
        System.out.println("今天的日期:" + nowDate);//今天的日期:2018-09-06

        int year = nowDate.getYear();//年:一般用这个方法获取年
        System.out.println("year:" + year);//year:2018

        int month = nowDate.getMonthValue();//月:一般用这个方法获取月
        System.out.println("month:" + month);//month:9

        int day = nowDate.getDayOfMonth();//日:当月的第几天,一般用这个方法获取日
        System.out.println("day:" + day);//day:6

        int dayOfYear = nowDate.getDayOfYear();//日:当年的第几天
        System.out.println("dayOfYear:" + dayOfYear);//dayOfYear:249

        //星期
        System.out.println(nowDate.getDayOfWeek());//THURSDAY
        System.out.println(nowDate.getDayOfWeek().getValue());//4
        //月份
        System.out.println(nowDate.getMonth());//SEPTEMBER
        System.out.println(nowDate.getMonth().getValue());//9

3. LocalTime

LocalTime类获取时间信息。格式为 15:33:56.749

        LocalTime nowTime = LocalTime.now();
        System.out.println("今天的时间:" + nowTime);//今天的时间:15:33:56.749

        int hour = nowTime.getHour();//时
        System.out.println("hour:" + hour);//hour:15

        int minute = nowTime.getMinute();//分
        System.out.println("minute:" + minute);//minute:33

        int second = nowTime.getSecond();//秒
        System.out.println("second:" + second);//second:56

        int nano = nowTime.getNano();//纳秒
        System.out.println("nano:" + nano);//nano:749000000

4. LocalDateTime

LocalDateTime类获取日期时间信息。格式为 2018-09-06T15:33:56.750

        LocalDateTime nowDateTime = LocalDateTime.now();
        System.out.println("今天是:" + nowDateTime);//今天是:2018-09-06T15:33:56.750

        System.out.println(nowDateTime.getYear());//年
        System.out.println(nowDateTime.getMonthValue());//月
        System.out.println(nowDateTime.getDayOfMonth());//日
        System.out.println(nowDateTime.getHour());//时
        System.out.println(nowDateTime.getMinute());//分
        System.out.println(nowDateTime.getSecond());//秒
        System.out.println(nowDateTime.getNano());//纳秒
        //日:当年的第几天
        System.out.println("dayOfYear:" + nowDateTime.getDayOfYear());//dayOfYear:249
        //星期
        System.out.println(nowDateTime.getDayOfWeek());//THURSDAY
        System.out.println(nowDateTime.getDayOfWeek().getValue());//4
        //月份
        System.out.println(nowDateTime.getMonth());//SEPTEMBER
        System.out.println(nowDateTime.getMonth().getValue());//9

5. 获取指定的时间日期

LocalDate

        System.out.println(LocalDate.of(1991, 11, 11));//直接传入对应的年月日
        System.out.println(LocalDate.of(1991, Month.NOVEMBER, 11));//相对上面只是把月换成了枚举
        LocalDate birDay = LocalDate.of(1991, 11, 11);
        System.out.println(LocalDate.ofYearDay(1991, birDay.getDayOfYear()));//第一个参数为年,第二个参数为当年的第多少天
        System.out.println(LocalDate.ofEpochDay(birDay.toEpochDay()));//参数为距离1970-01-01的天数

        System.out.println(LocalDate.parse("1991-11-11"));
        System.out.println(LocalDate.parse("19911111",DateTimeFormatter.ofPattern("yyyyMMdd")));
image

LocalTime

        System.out.println(LocalTime.of(8, 20));//时分
        System.out.println(LocalTime.of(8, 20, 30));//时分秒
        System.out.println(LocalTime.of(8, 20, 30, 150));//时分秒纳秒
        LocalTime mTime = LocalTime.of(8, 20, 30, 150);
        System.out.println(LocalTime.ofSecondOfDay(mTime.toSecondOfDay()));//参数为距离当天零时的秒数
        System.out.println(LocalTime.ofNanoOfDay(mTime.toNanoOfDay()));//参数为距离当天零时的纳秒数

        System.out.println(LocalTime.parse("08:20:30"));
        System.out.println(LocalTime.parse("082030", DateTimeFormatter.ofPattern("HHmmss")));

image.png

LocalDateTime
这里的 birDay 和 mTime 复用上面的变量。

        System.out.println(LocalDateTime.of(birDay, mTime));//参数为LocalDate和LocalTime
        System.out.println(LocalDateTime.of(1991, 11, 11, 8, 20));
        System.out.println(LocalDateTime.of(1991, Month.NOVEMBER, 11, 8, 20));
        System.out.println(LocalDateTime.of(1991, 11, 11, 8, 20, 30));
        System.out.println(LocalDateTime.of(1991, Month.NOVEMBER, 11, 8, 20, 30));
        System.out.println(LocalDateTime.of(1991, 11, 11, 8, 20, 30, 150));
        System.out.println(LocalDateTime.of(1991, Month.NOVEMBER, 11, 8, 20, 30, 150));

        System.out.println(LocalDateTime.parse("1991-11-11T08:20:30"));
        System.out.println(LocalDateTime.parse("1991-11-11 08:20:30", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
image.png

6. 日期时间的比较

以LocalDate为例:

1         LocalDate myDate = LocalDate.of(2018, 9, 5);
2         LocalDate nowDate = LocalDate.now();
3         System.out.println("今天是2018-09-06吗? " + nowDate.equals(myDate));//今天是2018-09-06吗? false
4         System.out.println(myDate + "是否在" + nowDate + "之前? " + myDate.isBefore(nowDate));//2018-09-05是否在2018-09-06之前? true
5         System.out.println(myDate + "是否在" + nowDate + "之后? " + myDate.isAfter(nowDate));//2018-09-05是否在2018-09-06之后? false

如何在java中判断是否是某个节日或者重复事件,使用MonthDay类。这个类由月日组合,不包含年信息,可以用来代表每年重复出现的一些日期。它和新的日期库中的其他类一样也都是不可变线程安全的。

1         LocalDate birDate = LocalDate.of(1991, 10, 1);
2         LocalDate nowDate = LocalDate.now();
3         MonthDay nowDay = MonthDay.of(birDate.getMonthValue(), birDate.getDayOfMonth());
4         //MonthDay monthDay = MonthDay.of(birDate.getMonth(), birDate.getDayOfMonth());
5         MonthDay birDay = MonthDay.from(nowDate);
6         System.out.println("今天是你的生日吗? " + nowDay.equals(birDay));//今天是你的生日吗? false

YearMonth表示固定的日期。Year表示年。

1         YearMonth yearMonth = YearMonth.of(2004, 2);
2         System.out.println(yearMonth + "有多少天? " + yearMonth.lengthOfMonth());//2004-02有多少天? 29
3         Year year = Year.of(2004);
4         System.out.println(year + "有多少天? " + year.length());//2004有多少天? 366
5         System.out.println(year + "是否是闰年? " + year.isLeap());//2004是否是闰年? true

7. 对日期时间的修改

LocalDateTime 综合了 LocalDate 和 LocalTime 里面的方法,所以下面只用 LocalDate 和 LocalTime 来举例。
这些方法返回的是一个新的实例引用,因为LocalDateTime 、LocalDate 、LocalTime 都是不可变的。

        LocalDate nowDate = LocalDate.now();
        System.out.println(nowDate);//当前日期
        System.out.println(nowDate.minusYears(1));//一年前
        System.out.println(nowDate.minusMonths(1));//一月前
        System.out.println(nowDate.minusWeeks(1));//一周前
        System.out.println(nowDate.minusDays(1));//一天前

        System.out.println(nowDate.plusYears(1));//一年后
        System.out.println(nowDate.plusMonths(1));//一月后
        System.out.println(nowDate.plusWeeks(1));//一周后
        System.out.println(nowDate.plusDays(1));//一天后
image.png
        LocalTime nowTime = LocalTime.now();
        System.out.println(nowTime);//当前时间
        System.out.println(nowTime.minusHours(1));//一小时前
        System.out.println(nowTime.minusMinutes(1));//一分钟前
        System.out.println(nowTime.minusSeconds(1));//一秒前
        System.out.println(nowTime.minusNanos(1));//一纳秒前

        System.out.println(nowTime.plusHours(1));//一小时后
        System.out.println(nowTime.plusMinutes(1));//一分钟后
        System.out.println(nowTime.plusSeconds(1));//一秒后
        System.out.println(nowTime.plusNanos(1));//一纳秒后

image.png

还可以直接使用 minusplus 方法来增加和减少日期时间。LocalDateTime 、LocalDate 、LocalTime 都是类似的。

    System.out.println(nowDate.minus(1, ChronoUnit.YEARS));
    System.out.println(nowDate.plus(1, ChronoUnit.YEARS));

ChronoUnit 用来表示时间单位, ChronoUnit 后面只需要替换成相应的年、月、周、日、时、分、秒、纳秒,就能减少或增加相应的单位。

8. 日期时间格式化

在JDK8之前,时间日期的格式化非常麻烦,经常使用SimpleDateFormat来进行格式化,但是SimpleDateFormat并不是线程安全的。在JDK8中,引入了一个全新的线程安全的日期与时间格式器DateTimeFormatter。

        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);//2018-09-06T18:22:47.366
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String ldtStr = ldt.format(dtf);
        System.out.println(ldtStr);//2018-09-06 18:22:47
        String ldtStr1 = dtf.format(ldt);
        System.out.println(ldtStr1);//2018-09-06 18:22:47

正反都能调用format方法。

9. 计算日期时间差

在Java8中,我们可以使用以下类来计算日期时间差异:

java.time.Period

主要是 Period 类方法 getYears(),getMonths() 和 getDays() 来计算。只能精确到年月日。
用于 LocalDate 之间的比较。

        LocalDate today = LocalDate.now();
        System.out.println(today);//2018-09-06
        LocalDate birthDate = LocalDate.of(1992, 1, 11);
        System.out.println(birthDate);//1990-10-01

        Period period = Period.between(birthDate, today);//第二个参数减第一个参数
        System.out.printf("年龄 : %d 年 %d 月 %d 日", period.getYears(), period.getMonths(), period.getDays());//年龄 : 27 年 11 月 5 日

java.time.Duration

提供了使用基于时间的值测量时间量的方法。
用于 LocalDateTime 之间的比较。也可用于 Instant 之间的比较。

        LocalDateTime today = LocalDateTime.now();
        System.out.println(today);
        LocalDateTime birthDate = LocalDateTime.of(1990,10,1,10,50,30);
        System.out.println(birthDate);

        Duration duration = Duration.between(birthDate, today);//第二个参数减第一个参数
        System.out.println(duration.toDays());//两个时间差的天数
        System.out.println(duration.toHours());//两个时间差的小时数
        System.out.println(duration.toMinutes());//两个时间差的分钟数
        System.out.println(duration.toMillis());//两个时间差的毫秒数
        System.out.println(duration.toNanos());//两个时间差的纳秒数
image.png

java.time.temporal.ChronoUnit

ChronoUnit类可用于在单个时间单位内测量一段时间,这个工具类是最全的了,可以用于比较所有的时间单位。

        LocalDateTime today = LocalDateTime.now();
        System.out.println(today);
        LocalDateTime birthDate = LocalDateTime.of(1990,10,1,10,50,30);
        System.out.println(birthDate);

        System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));
        System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today));
        System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));
        System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));
        System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));
        System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));
        System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));
        System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));
        System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));
        System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));

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