Java 8 Date-Time API 详解

从Java版本1.0开始就支持日期和时间,主要通过java.util.Date类。 但是,Date类设计不佳。 例如,Date中的月份从1开始,但从日期却从0开始。在JDK 1.1中使用它的许多方法已经废弃,同时java.util.Calendar被引入来接管Date中的一些功能。 这两个是处理日期和时间的主要类,直到JDK 1.7,尽管他们被认为是不足够并且不容易处理,导致许多人诉诸第三方替代品,例如Joda Time(http://joda.org)。 JDK 1.8中的新日期和时间API解决了旧API中的许多问题,并且与Joda Time API类似。

这里介绍JDK 1.8中的日期 - 时间的API。

JDK 8

Overview

新的日期和时间API使得使用日期和时间非常容易。java.time包中包含API中的核心类。 另外,还有其他四个包,其成员使用较少:java.time.chronojava.time.formatjava.time.temporaljava.time.zone

java.time包中,Instant类表示时间线上的一个点,通常用于对时间进行操作。 LocalDate类为没有时间和时区部分的日期建模,例如,用于表示生日。

如果你需要日期和时间,那么LocalDateTime就是为你准备的。 例如,订单发货日期可能需要一个日期以外的时间来使订单更容易跟踪。 如果你需要一段时间但不关心日期,那么可以使用LocalTime

如果时区很重要,日期和时间API提供ZonedDateTime类。 顾名思义,这个类表示带有时区日期时间。 例如,你可以使用此类来计算位于不同时区的两个机场之间的飞行时间。

然后有两个类来测量时间总计,即Duration类和Period类。 这两个类是相似的,除了Duration是基于时间,但而Period是基于日期的Duration提供了纳秒精度的时间量。 例如,可以模拟飞行时间,因为它通常以小时数和分钟数表示。 另一方面,如果只关心天数,月数或年数,例如计算一个人的年龄,则Period更为适用。

java.time包也带有两个枚举DayOfWeekMonthDayOfWeek表示从一周的一天,从周一开始到周日。 Month枚举代表这一年的十二个月,从1月到12月。

处理日期和时间通常涉及解析和格式。 日期和时间API通过在所有主要类中提供parseformat方法来解决这两个问题。 另外,java.time.format包含一个用于格式化日期和时间的DateTimeFormatter类。


Instant类

Instant实例表示时间线上的一个点。 参考点是标准的Java纪元(epoch),即1970-01-01T00:00:00Z(1970年1月1日00:00 GMT)。 Instant类的EPOCH属性返回表示Java纪元的Instant`实例。 在纪元之后的时间是正值,而在此之前的时间即是负值。

Instant的静态now方法返回一个表示当前时间的Instant对象:

Instant now = Instant.now();

getEpochSecond方法返回自纪元以来经过的秒数。 getNano方法返回自上一秒开始以来的纳秒数。

Instant类的一个常用用途是用来操作时间,如以下代码所示。

import java.time.Duration;

import java.time.Instant;

public class InstantDemo1 {

    public static void main(String[] args) {

        Instant start = Instant.now();

        // do something here

        Instant end = Instant.now();

        System.out.println(Duration.between(start, end).toMillis());

    }

}

如上面代码所示,Duration类用于返回两个Instant之间时间数量的差异。


LocalDate类

LocalDate类只包括日期没有时间的部分。 它也没有时区。 下表显示了LocalDate中一些重要的方法。

方法 描述
now 静态方法,返回今天的日期
of 从指定年份,月份和日期创建LocalDate的静态方法
getDayOfMonth, getMonthValue, getYear 以int形式返回此LocalDate的日,月或年
getMonth 以Month枚举常量返回此LocalDate的月份
plusDays, minusDays 给LocalDate添加或减去指定的天数
plusWeeks, minusWeeks 给LocalDate添加或减去指定的星期数
plusMonths, minusMonths 给LocalDate添加或减去指定的月份数
plusYears, minusYears 给LocalDate添加或减去指定的年数
isLeapYear 检查LocalDate指定的年份是否为闰年
isAfter, isBefore 检查此LocalDate是在给定日期之后还是之前
lengthOfMonth 返回此LocalDate中月份的天数
withDayOfMonth 返回此LocalDate的拷贝,将月份中的某天设置为给定值
withMonth 返回此LocalDate的拷贝,其月份设置为给定值
withYear 返回此LocalDate的拷贝,并将年份设置为给定值

LocalDate提供了各种创建日期的方法。 例如,要创建代表今天日期的LocalDate,使用静态now方法。

LocalDate today = LocalDate.now();

要创建代表特定年,月和日的LocalDate,使用of方法,该方法也是静态的。 例如,以下代码创建了一个代表2018年3月7日的LocalDate实例。

LocalDate date = LocalDate.of(2018, 3, 7);

还有一个接受java.time.Month枚举的常量作为第二个参数的of方法。 例如,下面是使用第二种方法重载构造相同日期的代码。

LocalDate date = LocalDate.of(2018, Month.MARCH, 7);

还有获取LocalDate的日,月或年的方法,例如getDayOfMonthgetMonthgetMonthValuegetYear。 他们都没有任何参数,并返回一个int或Month的枚举常量。 另外,还有一个get方法,它接受一个TemporalField并返回这个LocalDate的一部分。 例如,传递ChronoField.YEAR以获取LocalDate的年份部分。

int year = localDate.get(ChronoField.YEAR);

ChronoField是一个实现TemporalField接口的枚举,因此可以传递一个ChronoField常量来获取。 TemporalFieldChronoField都是java.time.temporal包的一部分。 但是,并非ChronoField中的所有常量都可以get获取,因为并非所有常量都受支持。 例如,传递ChronoField.SECOND_OF_DAY以引发异常。 因此,取而代之,最好使用getMonthgetYear或类似方法来获取LocalDate的组件。

此外,还有拷贝LocalDate的方法,例如plusDaysplusYearsminusMonths等等。 例如,要获取表示明天的LocalDate,可以创建一个代表今天的LocalDate,然后调用其plusDays方法。

LocalDate tomorrow = LocalDate.now().plusDays(1);

要获取昨天表示的LocalDate,可以使用minusDays方法。

LocalDate yesterday = LocalDate.now().minusDays(1);

另外,还有plusminus方法以更通用的方式获得LocalDate的拷贝。 两者都接受一个int参数和一个TemporalUnit参数。 这些方法的签名如下。

public LocalDate plus(long amountToAdd,

        java.time.temporal.TemporalUnit unit)

public LocalDate minus(long amountToSubtract,

        java.time.temporal.TemporalUnit unit)

例如,获得一个从今天开始前20年的LocalDate,可以使用这段代码。

LocalDate pastDate = LocalDate.now().minus(2, ChronoUnit.DECADES);

ChronoUnit是一个实现TemporalUnit的枚举,因此可以将ChronoUnit常量传递给plusminus方法。

LocalDate是不可变的,因此无法更改。 任何返回LocalDate的方法都返回LocalDate的新实例。

以下是使用LocalDate的例子。

import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;

public class LocalDateDemo1 {

    public static void main(String[] args) {
        LocalDate today = LocalDate.now();
         LocalDate tomorrow = today.plusDays(1);
         LocalDate oneDecadeAgo = today.minus(1, ChronoUnit.DECADES);

         System.out.println("Day of month: " + today.getDayOfMonth());
         System.out.println("Today is " + today);
         System.out.println("Tomorrow is " + tomorrow);
         System.out.println("A decade ago was " + oneDecadeAgo);
         System.out.println("Year : " + today.get(ChronoField.YEAR));
         System.out.println("Day of year:" + today.getDayOfYear());
    }
}

Period类

Period类基于日期的时间数量构建,例如五天,一周或三年。 下面列出了一些重要的方法。

方法 描述
between 在两个LocalDates之间创建一个Period示例
ofDays, ofWeeks, ofMonths, ofYears 创建代表给定天数/周/月/年的Period实例
of 根据给定的年数,月数和天数创建一个Period实例
getDays, getMonths, getYears 以int形式返回此Period的天数/月/年
isNegative 如果此Period的三个部分中的任何一个为负数,则返回true。 否则返回false
isZero 如果此Period的所有三个部分均为零,则返回true。 否则,返回false
plusDays, minusDays 在此Period上添加或减去给定的天数
plusMonths, minusMonths 在此Period上增加或减去给定的月数
plusYears, minusYears 在此Period增加或减去给定的年数
withDays 以指定的天数返回此Period的拷贝
withMonths 以指定的月数返回此Period的拷贝
withYears 以指定的年数返回此Period的拷贝

创建一个Period很简单,这要感谢between, of,ofDays / ofWeeks / ofMonths / ofYears等静态工厂方法。 例如,以下是如何创建代表两周的Period实例。

Period twoWeeks = Period.ofWeeks(2);

要创建代表一年两个月三天的Period实例,请使用of方法。

Period p = Period.of(1, 2, 3);

要获取某个期间的年/月/日组件,调用其getYears / getMonths / getDays方法。 例如,以下代码中的howManyDays变量的值是14。

Period twoWeeks = Period.ofWeeks(2);

int howManyDays = twoWeeks.getDays();

最后,可以使用plusXXXminusXXX方法以及withXXX方法来创建Period的拷贝。 Period是不可变的,所以这些方法返回新的Period实例。

例如,下面的代码显示了一个计算个人年龄的年龄计算器。 它从两个LocalDate创建一个Period并调用它的getDaysgetMonthsgetYears方法。

import java.time.LocalDate;
import java.time.Period;

public class PeriodDemo1 {
    public static void main(String[] args) {
        LocalDate dateA = LocalDate.of(1978, 8, 26);
        LocalDate dateB = LocalDate.of(1988, 9, 28);
        Period period = Period.between(dateA, dateB);
        System.out.printf("Between %s and %s"
                + " there are %d years, %d months"
                + " and %d days%n", dateA, dateB,
                period.getYears(),
                period.getMonths(),
                period.getDays());
    }
}

运行PeriodDemo1类打印下面字符串。

Between 1978-08-26 and 1988-09-28 there are 10 years, 1 months and 2 days

LocalDateTime类

LocalDateTime类是一个没有时区的日期时间的构建。 下表显示了LocalDateTime中一些重要的方法。 这些方法类似于LocalDate的方法,以及用于修改时间部分的一些其他方法,例如在LocalDate中不可用的plusHoursplusMinutesplusSeconds

方法 描述
now 返回当前日期和时间的静态方法。
of 从指定年份,月份,日期,小时,分钟,秒和毫秒创建LocalDateTime的静态方法。
getYear, getMonthValue, getDayOfMonth, getHour, getMinute, getSecond 以int形式返回此LocalDateTime的年,月,日,小时,分钟或秒部分。
plusDays, minusDays 给当前LocalDateTime添加或减去指定的天数。
plusWeeks, minusWeeks 给当前LocalDateTime添加或减去指定的周数。
plusMonths, minusMonths 给当前LocalDateTime添加或减去指定的月数。
plusYears, minusYears 给当前LocalDateTime添加或减去指定的年数。
plusHours, minusHours 给当前LocalDateTime添加或减去指定的小时数
plusMinutes, minusMinutes 给当前LocalDateTime添加或减去指定的分钟数
plusSeconds, minusSeconds 给当前LocalDateTime添加或减去指定的秒数
IsAfter, isBefore 检查此LocalDateTime是否在指定的日期时间之后或之前
withDayOfMonth 返回此LocalDateTime的拷贝,并将月份中的某天设置为指定值
withMonth, withYear 返回此LocalDateTime的拷贝,其月或年设置为指定值
withHour, withMinute, withSecond 返回此LocalDateTime的拷贝,其小时/分钟/秒设置为指定值

LocalDateTime提供了各种静态方法来创建日期时间。 该方法现在带有三个重载方法返回当前的日期时间。 无参的方法是最容易使用的:

LocalDateTime now = LocalDateTime.now();

要创建具有特定日期和时间的LocalDateTime,请使用of方法。 此方法有多个重载,并允许传递日期时间或LocalDateLocalTime的单个部分。 以下是一些方法的签名。

public static LocalDateTime of(int year, int month, int dayOfMonth,
        int hour, int minute)

public static LocalDateTime of(int year, int month, int dayOfMonth,
        int hour, int minute)

public static LocalDateTime of(int year, Month month,
        int dayOfMonth, int hour, int minute)

public static LocalDateTime of(int year, Month month,
        int dayOfMonth, int hour, int minute)

public static LocalDateTime of(LocalDate date, LocalTime time)

例如,下面的代码段创建一个LocalDateTime,代表2015年12月31日早上八点。

LocalDateTime endOfYear = LocalDateTime.of(2015, 12, 31, 8, 0);

可以使用plusXXXminusXXX方法创建LocalDateTime的拷贝。 例如,此代码创建一个LocalDateTime,它表示明天的同一时间。

LocalDateTime now = LocalDateTime.now();

LocalDateTime sameTimeTomorrow = now.plusHours(24);

Time Zones

互联网数字分配机构(IANA)维护一个可从此网页下载的时区数据库:

[http://www.iana.org/time-zones](http://www.iana.org/time-zones)

但为了便于查看,可以访问此Wikipedia页面:
http://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Java日期和时间API也适用于时区。 抽象类ZoneId(在java.time包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds的静态方法,它返回所有区域标识符。 下面展示了如何使用这种方法打印所有时区的排序列表。

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class TimeZoneDemo1 {
    public static void main(String[] args) {
        Set<String> allZoneIds = ZoneId.getAvailableZoneIds();
        List<String> zoneList = new ArrayList<>(allZoneIds);
        Collections.sort(zoneList);      
        for (String zoneId : zoneList) {
            System.out.println(zoneId);
        }
        // alternatively, you can use this line of code to
        // print a sorted list of zone ids
        // ZoneId.getAvailableZoneIds().stream().sorted().
        //        forEach(System.out::println);
    }
}

getAvailableZoneIds返回字符串的Set集合。 可以使用Collections.sort()或更优雅地通过调用它的stream方法对Set进行排序。 可以编写此代码对区域标识符进行排序。

ZoneId.getAvailableZoneIds().stream().sorted()
        .forEach(System.out::println);

getAvailableZoneIds返回586个区域标识符的Set集合。 以下是上述代码中的一部分区域标识符。

Africa/Cairo
Africa/Johannesburg
America/Chicago
America/Los_Angeles
America/Mexico_City
America/New_York
America/Toronto
Antarctica/South_Pole
Asia/Hong_Kong
Asia/Shanghai
Asia/Tokyo
Australia/Melbourne
Australia/Sydney
Canada/Atlantic
Europe/Amsterdam
Europe/London
Europe/Paris
US/Central
US/Eastern
US/Pacific

ZonedDateTime

ZonedDateTime类以一个时区为日期时间的构建。例如,以下是一个时区的日期时间:

2015-12-31T10:59:59+01:00 Europe/Paris

ZonedDateTime始终是不可变的,时间分量的存储精度为纳秒。

ZonedDateTIme中一些重要方法的使用与LocalDateTime类似,只是多了一个时区的概念。可自行查阅API。

LocalDateTime一样,ZonedDateTime类现在提供静态nowof方法,并构造一个ZonedDateTime实例。 now方法创建一个ZonedDateTime代表执行的日期和时间。 无参now方法会使用计算机的默认时区创建ZonedDateTime

ZonedDateTime now = ZonedDateTime.now();

now的另一个重载方法允许传递区域标识符:

ZonedDateTime parisTime =
        ZonedDateTime.now(ZoneId.of("Europe/Paris"));

of方法也有好几个重载的方法。在所有情况下,都需要传递区域标识符。 第一个重载方法允许传递时区日期时间的每个部分,从年份到纳秒。

public static ZonedDateTime of(int year, int month, int dayOfMonth,
        int hour, int minute, int second, int nanosecond,
        ZoneId zone)

of方法的第二个重载方法需要LocalDateLocalTimeZoneId参数:

public static ZonedDateTime of(LocalDate date, LocalTime time,
        ZoneId zone)

of方法的最后一个重载方法需要LocalDateTimeZoneId参数。

public static ZonedDateTime of(LocalDateTime datetime, ZoneId zone)

LocalDateLocalDateTime一样,ZonedDateTime提供了使用plusXXXminusXXXwithXXX方法创建实例拷贝的方法。

例如,下面代码行创建一个带默认时区的ZonedDateTime,并调用它的minusDays方法以在三天前创建相同的ZonedDateTime

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime threeDaysEarlier = now.minusDays(3);

Duration

Duration类是基于时间的持续时间的构建。 它与Period类似,不同之处在于Duration的时间分量为纳秒精度,并考虑了ZonedDateTime实例之间的时区。 下表显示了Duration中重要的方法。

方法 描述
between 在两个时差的对象之间创建一个Duration实例,例如在两个LocalDateTime或两个ZonedDateTime之间。
ofYears, ofMonths, ofWeeks, ofDays, ofHours, ofMinutes, ofSeconds, ofNano 创建给定年数/月/周/天/小时/分钟/秒/纳秒的Duration实例
of 根据指定数量的时间单位创建Duration实例
toDays, toHours, toMinutes 以int形式返回此Duration的天数/小时/分钟数
isNegative 如果此Duration为负,则返回true。 否则返回false。
isZero 如果此Duration长度为零,则返回true。 否则,返回false
plusDays, minusDays 在此Duration内添加或减去指定的天数。
plusMonths, minusMonths 在此Duration内添加或减去指定的月数。
plusYears, minusYears 在Duration内添加或减去指定的年数
withSeconds 以指定的秒数返回此Duration的拷贝。

可以通过调用静态方法betweenof来创建Duration。 下面的代码会在2015年1月26日11:10至2015年1月26日12:40之间创建两个LocalDateTimeDuration

import java.time.Duration;
import java.time.LocalDateTime;

public class DurationDemo1 {
    public static void main(String[] args) {
        LocalDateTime dateTimeA = LocalDateTime
                .of(2015, 1, 26, 8, 10, 0, 0);
        LocalDateTime dateTimeB = LocalDateTime
                .of(2015, 1, 26, 11, 40, 0, 0);
        Duration duration = Duration.between(
                dateTimeA, dateTimeB);

        System.out.printf("There are %d hours and %d minutes.%n",
                duration.toHours(),
                duration.toMinutes() % 60);
    }
}

运行DurationDemo1类的结果是这样的。

There are 3 hours and 30 minutes.

下面的代码在两个ZoneDateTime之间创建一个Duration,具有相同的日期和时间,但时区不同。

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class DurationDemo2 {
    public static void main(String[] args) {
        ZonedDateTime zdt1 = ZonedDateTime.of(
                LocalDateTime.of(2015, Month.JANUARY, 1,
                        8, 0),
                ZoneId.of("America/Denver"));
        ZonedDateTime zdt2 = ZonedDateTime.of(
                LocalDateTime.of(2015, Month.JANUARY, 1,
                        8, 0),
                ZoneId.of("America/Toronto"));

        Duration duration = Duration.between(zdt1, zdt2);
        System.out.printf("There are %d hours and %d minutes.%n",
                duration.toHours(),
                duration.toMinutes() % 60);
    }
}

运行DurationDemo2类在控制台上打印如下结果。

There are -2 hours and 0 minutes.

这是预料之中的,因为时区America/DenverAmerica/Toronto之间有两个小时的差异。

作为一个更复杂的例子,下面的代码显示了一个公交车旅行时间计算器。 它有一个方法calculateTravelTime,它需要一个离开的ZonedDateTime实例和一个到达的ZonedDateTime实例。 该代码调用calculateTravelTime方法两次。 这两次公交车都在丹佛早上8点从科罗拉多州丹佛出发,并于多伦多时间第二天早上8点抵达多伦多。 公交车首次于2014年3月8日启程,第二次于2014年3月18日启程。

两种情况下的旅行时间是多少?

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class TravelTimeCalculator {
    public Duration calculateTravelTime(
            ZonedDateTime departure, ZonedDateTime arrival) {
        return Duration.between(departure, arrival);
    }

    public static void main(String[] args) {
        TravelTimeCalculator calculator =
                new TravelTimeCalculator();
        ZonedDateTime departure1 = ZonedDateTime.of(
                LocalDateTime.of(2014, Month.MARCH, 8,
                        8, 0),
                ZoneId.of("America/Denver"));
        ZonedDateTime arrival1 = ZonedDateTime.of(
                LocalDateTime.of(2014, Month.MARCH, 9,
                        8, 0),
                ZoneId.of("America/Toronto"));
        Duration travelTime1 = calculator
                .calculateTravelTime(departure1, arrival1);
        System.out.println("Travel time 1: "
                + travelTime1.toHours() + " hours");

        ZonedDateTime departure2 = ZonedDateTime.of(
                LocalDateTime.of(2014, Month.MARCH, 18,
                        8, 0),
                ZoneId.of("America/Denver"));
        ZonedDateTime arrival2 = ZonedDateTime.of(
                LocalDateTime.of(2014, Month.MARCH, 19,
                        8, 0),
                ZoneId.of("America/Toronto"));
        Duration travelTime2 = calculator
                .calculateTravelTime(departure2, arrival2);
        System.out.println("Travel time 2: "
                + travelTime2.toHours() + " hours");
    }
}

运行结果为:

Travel time 1: 21 hours

Travel time 2: 22 hours

为什么有这个区别? 因为2014年的夏令时从3月9日星期日凌晨2点开始。 因此,在2014年3月8日至2014年3月9日之间“失去”了一小时。


Formatting A Date-Time

可以使用java.time.format.DateTimeFormatter格式化本地或时区日期时间。LocalDateLocalDateTimeLocalTimeZoneDateTime类提供具有以下签名的格式方法。

public java.lang.String format(java.time.format.DateTimeFormatter
        formatter)

很明显,要格式化日期或时间,必须首先创建DateTimeFormatter实例。

下面的代码使用两个格式化实例格式化当前日期。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

public class DateTimeFormatterDemo1 {
    public static void main(String[] args) {
        DateTimeFormatter formatter1 = DateTimeFormatter
                .ofLocalizedDateTime(FormatStyle.MEDIUM);
        LocalDateTime example = LocalDateTime.of(
                2000, 3, 19, 10, 56, 59);
        System.out.println("Format 1: " + example
                .format(formatter1));       

        DateTimeFormatter formatter2 = DateTimeFormatter
                .ofPattern("MMMM dd, yyyy HH:mm:ss");
        System.out.println("Format 2: " +
                example.format(formatter2));
    }
}

运行结果如下:(第一个结果取决于你的区域设置)。

Format 1: 19-Mar-2000 10:56:59 AM

Format 2: March 19, 2000 10:56:59

Parsing A Date-Time

在Java Date和Time API的许多类中有两种parse方法。第一个需要格式化实例,第二个则不需要。后一个方法会根据默认模式解析日期时间。要使用自己的格式化模式,请使用DateTimeFormatter。如果传递的字符串不能被解析,那么解析方法将抛出一个DateTimeParseException

import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Scanner;

public class AgeCalculator {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-d");
    public Period calculateAge(LocalDate birthday) {
        LocalDate today = LocalDate.now();
        return Period.between(birthday, today);
    }

    public LocalDate getBirthday() {
        Scanner scanner = new Scanner(System.in);
        LocalDate birthday;
        while (true) {
            System.out.println("Please enter your birthday "
                    + "in yyyy-MM-dd format (e.g. 1980-9-28): ");
            String input = scanner.nextLine();
            try {
                birthday = LocalDate.parse(input, formatter);
                return birthday;
            } catch(DateTimeParseException e) {
                System.out.println("Error! Please try again");
            }
        }
    }

    public static void main(String[] args) {
        AgeCalculator ageCalculator = new AgeCalculator();
        LocalDate birthday = ageCalculator.getBirthday();
        Period age = ageCalculator.calculateAge(birthday);
        System.out.printf("Today you are %d years, %d months"
                + " and %d days old%n",
                age.getYears(), age.getMonths(), age.getDays());
    }
}

AgeCalculator类有两个方法,getBirthdaycalculateAgegetBirthday方法使用Scanner类来读取用户输入,并使用DateTimeFormatter类将输入解析到LocalDate中。 getBirthday方法一直请求一个日期,直到用户输入正确格式的日期,在这种情况下,方法返回。 calculateAge方法需要一个生日,并在生日和今天的日期之间创建一个Period实例。

如果运行这个例子,会在控制台上看到这个。

Please enter your birthday in yyyy-MM-dd format (e.g. 1980-9-28):

如果以正确的格式输入日期,则程序将打印计算的年龄,如下所示。

Today you are 79 years, 0 months and 15 days old

Summary

Java 8带来了全新的Date-Time API来替代以java.util.Date类为中心的旧的API。 通过本篇文章,学习如何使用新API中的核心类,如InstantLocalDateLocalDateTimeZonedDateTimePeriodDuration,以及学习如何格式化和解析日期时间。

posted @ 2018-03-10 14:03  林本托  阅读(5499)  评论(0编辑  收藏  举报