Java8 时间处理 - LocalDate,LocalDateTime,Instant

Java 8通过发布新的Date-Time API来进一步加强对日期与时间的处理。 旧版的 Java 中,日期时间 API 存在诸多问题 :

非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,
设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。
时区处理麻烦 − 日期类并不提供国际化,没有时区支持
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API

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

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

Java 8 Date Time API是JSR-310实现。它旨在克服传统日期时间实现中的所有缺陷。新Date Time API的一些设计原则是:

  1. 不可变性:新Date Time API中的所有类都是不可变的,适用于多线程环境。
  2. 关注点分离:新API明确区分人类可读日期时间和机器时间(unix时间戳)。它为Date,Time,DateTime,Timestamp,Timezone等定义了单独的类。
  3. 清晰度:方法明确定义,并在所有类中执行相同的操作。例如,要获取当前实例,我们有now()方法。在所有这些类中定义了format()和parse()方法,而不是为它们设置单独的类。

    所有类都使用工厂模式策略模式来更好地处理。一旦你在其中一个类中使用了这些方法,那么使用其他类并不难。

  4. 实用程序操作:所有新的Date Time API类都带有执行常见任务的方法,例如加号,减号,格式,解析,在日期/时间中获取单独的部分等。
  5. 可扩展:新的Date Time API适用于ISO-8601日历系统,但我们也可以将其与其他非ISO日历一起使用。

Java 8 Date Time API包

Java 8 Date Time API包含以下包。

  1. java.time包:这是新的Java Date Time API的基础包。所有主要的基类是该计划的一部分,比如LocalDateLocalTimeLocalDateTimeInstantPeriodDuration等所有这些类是不可变的和线程安全的。大多数情况下,这些类足以满足常见要求。
  2. java.time.chrono包:此包定义非ISO日历系统的通用API。我们可以扩展AbstractChronology类来创建我们自己的日历系统。
  3. java.time.format包:此包包含用于格式化和解析日期时间对象的类。大多数情况下,我们不会直接使用它们,因为java.time包中的主要类提供了格式化和解析方法。
  4. java.time.temporal包:此包包含临时对象,我们可以使用它来查找与日期/时间对象相关的特定日期或时间。例如,我们可以使用这些来查找该月的第一天或最后一天。您可以轻松识别这些方法,因为它们始终具有“withXXX”格式。
  5. java.time.zone包:此包包含用于支持不同时区及其规则的类。

Java 8 Date Time API示例

我们已经研究了Java Date Time API的大部分重要部分。现在是时候通过示例查看最重要的Date Time API类。

LOCALDATE

LocalDate只提供日期不提供时间信息。它是不可变类且线程安全的。

LocalDate是一个不可变类,表示默认格式为yyyy-MM-dd的Date。我们可以使用now()方法来获取当前日期。我们还可以提供年,月和日的输入参数来创建LocalDate实例。此类为now()提供重载方法,我们可以通过ZoneId获取特定时区的日期。该类提供与以下相同的功能java.sql.Date。让我们看一个简单的例子来说明它的用法。

    public static void main(String[] args) {

        //获取当前时间
        LocalDate today = LocalDate.now();
        System.out.println("Current Date="+today);

        //根据传入的参数构建年月日
        LocalDate firstDay_2019 = LocalDate.of(2019, Month.JANUARY, 1);
        System.out.println("Specific Date="+firstDay_2019);

        //或
        LocalDate firstDayOfNove_2019 = LocalDate.of(2019, 11, 1);
        System.out.println("Specific Date="+firstDayOfNove_2019);

        //Try creating date by providing invalid inputs
        //LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
        //Exception in thread "main" java.time.DateTimeException:
        //Invalid date 'February 29' as '2014' is not a leap year

        //Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
        // now(ZoneId) : 从指定时区的系统时钟中获取当前日期
        LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
        System.out.println("Current Date in IST="+todayKolkata);

        //java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
        //LocalDate todayIST = LocalDate.now(ZoneId.of("IST"));

        //Getting date from the base date i.e 01/01/1970,
        // 基于1970-01-01时代转换的大纪元日。从1970年开始获取指定天数的LocalDate实例
        LocalDate dateFromBase = LocalDate.ofEpochDay(730);
        System.out.println("730th day from base date= "+dateFromBase);
        //ofYearDay(int year,int dyaOfYear) : 指定某年中的指定天数获取LocalDate实例,比如2019年的第100天是几年几月几日
        LocalDate hundredDay2019 = LocalDate.ofYearDay(2019, 100);
        System.out.println("100th day of 2019="+hundredDay2019);
    }

结果:

扫描二维码关注公众号,回复: 11197040 查看本文章
Current Date=2019-10-23
Specific Date=2019-01-01
Specific Date=2019-11-01
Current Date in IST=2019-10-23
730th day from base date= 1972-01-01
100th day of 2019=2019-04-10

LocalDateTime

LocalDateTime是一个不可变的日期时间对象,表示日期时间,默认格式为yyyy-MM-dd-HH-mm-ss.zzz。它提供了一个工厂方法,用于获取LocalDateLocalTime输入参数以创建LocalDateTime实例。

 public static void main(String[] args) {

        //Current Date
        LocalDateTime today = LocalDateTime.now();
        System.out.println("Current DateTime="+today);

        //Current Date using LocalDate and LocalTime
        today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
        System.out.println("Current DateTime="+today);

        //Creating LocalDateTime by providing input arguments
        LocalDateTime specificDate = LocalDateTime.of(2019, Month.JANUARY, 1, 10, 10, 30);
        System.out.println("Specific Date="+specificDate);


        //Try creating date by providing invalid inputs
        //LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
        //Exception in thread "main" java.time.DateTimeException:
        //Invalid value for HourOfDay (valid values 0 - 23): 25


        //Current date in "Asia/Kolkata", you can get it from ZoneId javadoc
        LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
        System.out.println("Current Date in IST="+todayKolkata);

        //java.time.zone.ZoneRulesException: Unknown time-zone ID: IST
        //LocalDateTime todayIST = LocalDateTime.now(ZoneId.of("IST"));

        //Getting date from the base date i.e 01/01/1970
        LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
        System.out.println("10000th second time from 01/01/1970= "+dateFromBase);

    }

在所有这三个例子中,我们已经看到如果我们为创建Date / Time提供了无效的参数,那么它抛出的java.time.DateTimeException是RuntimeException,所以我们不需要显式地捕获它。

我们还看到我们可以通过传递获取日期/时间数据ZoneId,您可以从它的javadoc获取支持的ZoneId值列表。当我们在上面运行时,我们得到以下输出。

Current DateTime=2019-10-23T19:30:19.334
Current DateTime=2019-10-23T19:30:19.335
Specific Date=2019-01-01T10:10:30
Current Date in IST=2019-10-23T17:00:19.336
10000th second time from 01/01/1970= 1970-01-01T02:46:40

Instant 时间戳

Instant类用于处理机器可读的时间格式,它将日期时间存储在unix时间戳中。

    public static void main(String[] args) {
        //Current timestamp
        //Instant.now()使用等是UTC时间Clock.systemUTC().instant()
        Instant timestamp = Instant.now();
        System.out.println("Current Timestamp = "+timestamp);
        //通过上述方式获取的时间戳与北京时间相差8个时区,需要修正为北京时间
        Instant now = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
        System.out.println("Beijing时间为:"+now);
        System.out.println("秒数:"+now.getEpochSecond());
        System.out.println("毫秒数:"+now.toEpochMilli());

        //Instant from timestamp
        Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
        System.out.println("Specific Time = "+specificTime);

    }

结果:

Current Timestamp = 2019-10-23T11:38:45.004Z
Beijing时间为:2019-10-23T19:38:45.149Z
秒数:1571859525
毫秒数:1571859525149
Specific Time = 2019-10-23T11:38:45.004Z

java 8 的 Period 和 Duration 类

Period 和 Duration。两个类看表示时间量或两个日期之间的差,两者之间的差异为:Period基于日期值,而Duration基于时间值。

Period.between方法坑及注意事项

    public static void main(String[] args) {
        LocalDate startDate = LocalDate.of(2018, 6, 25);
        LocalDate endDate = LocalDate.of(2019, 8, 24);
        //使用between()方法获取两个日期之间的差作为Period 对象返回
        Period period = Period.between(startDate, endDate);
        System.out.println("相差年数:" + period.getYears() );
        //月份数是1.。。。。。。。。。。。。。。。。。。。。并不跨年,而且少于2个月就会等于为1个月
        System.out.println("相差月数:" + period.getMonths());
        //不跨年月
        System.out.println("相差天数:"+period.getDays());

        Period fromWeeks = Period.ofWeeks(40);
        System.out.println(fromWeeks.getDays());
    }

结果:可以看到有坑,相差天数明显不是我们想要的结果

相差年数:1
相差月数:1
相差天数:30
280

Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性。我们能使用between()方法比较两个瞬间的差:

Instant start = Instant.parse("2019-11-03T10:15:30.00Z");
Instant end = Instant.parse("2019-11-03T10:16:30.00Z");

Duration duration = Duration.between(start, end);
System.out.println(duration.getSeconds());

结果:

60

计算两个时间间隔多少天:

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse("2019-01-01", formatter);
        LocalDate endDate = LocalDate.parse("2020-01-01", formatter);
        // 日期区间
        long days = ChronoUnit.DAYS.between(startDate, endDate);
        //月
        long month = ChronoUnit.MONTHS.between(startDate, endDate);
        //年
        long year = ChronoUnit.YEARS.between(startDate, endDate);

LocalDate与String相互转化

LocalDate date = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = date.format(fmt);
System.out.println("LocalDate转String:"+dateStr);
String timeStr = "2019-11-05";
LocalDate localDate = LocalDate.parse(timeStr, fmt);
System.out.println("String转LocalDate:"+localDate);

结果:

LocalDate转String:2019-10-23
String转LocalDate:2019-11-05

TemporalAdjuster

有的时候,你需要进行一些更加复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的with方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象

@FunctionalInterface
public interface TemporalAdjuster {

        Temporal adjustInto(Temporal temporal);
}

这里一些常用的操作在TemporalAdjusters类已经预定义了
 TemporalAdjusters的一些预定义方法
 dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天
 firstDayOfMonth 创建一个新的日期,它的值为当月的第一天
 firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天
 firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天
 firstDayOfYear 创建一个新的日期,它的值为当年的第一天
 firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值
 lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天
 lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天
 lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天
 lastDayOfYear 创建一个新的日期,它的值为今年的最后一天
 lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值
 next/previous
 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期
 nextOrSame/previousOrSame创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星
 期几要求的日期,如果该日期已经符合要求,直接返回该对象

LocalDate today = LocalDate.now();

        System.out.println("today"+today);
        //判断是否为闰年
        System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());

        //Compare two LocalDate for before and after
        System.out.println("Today is before 01/01/2019? "+today.isBefore(LocalDate.of(2019,1,1)));

        //Create LocalDateTime from LocalDate
        System.out.println("Current Time="+today.atTime(LocalTime.now()));

        //plus and minus operations
        System.out.println("10 days after today will be "+today.plusDays(10));
        System.out.println("3 weeks after today will be "+today.plusWeeks(3));
        System.out.println("20 months after today will be "+today.plusMonths(20));

        System.out.println("10 days before today will be "+today.minusDays(10));
        System.out.println("3 weeks before today will be "+today.minusWeeks(3));
        System.out.println("20 months before today will be "+today.minusMonths(20));

        //Temporal adjusters for adjusting the dates
        System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
        LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
        System.out.println("Last date of this year= "+lastDayOfYear);
        LocalDate lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last date of this month= "+lastDayOfMonth);
        //本月中的最后一个星期五
        LocalDate lastInMonth = today.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
        System.out.println("Last friday of this month= "+lastInMonth);

        //获取月份值
        int month = today.getMonthValue();
        System.out.println("Month of today= "+ month);

        Month month1 = today.getMonth();
        System.out.println("Month of today= "+month1);

        Period period = today.until(lastDayOfYear);
        System.out.println("Period Format= "+period);
        System.out.println("Months remaining in the year= "+period.getMonths());

结果:

today2019-10-23
Year 2019 is Leap Year? false
Today is before 01/01/2019? false
Current Time=2019-10-23T20:34:02.658
10 days after today will be 2019-11-02
3 weeks after today will be 2019-11-13
20 months after today will be 2021-06-23
10 days before today will be 2019-10-13
3 weeks before today will be 2019-10-02
20 months before today will be 2018-02-23
First date of this month= 2019-10-01
Last date of this year= 2019-12-31
Last date of this month= 2019-10-31
Last friday of this month= 2019-10-25
Month of today= 10
Month of today= OCTOBER
Period Format= P2M8D
Months remaining in the year= 2

其中输出PeriodDuration使用时toString(),将根据ISO-8601标准使用特殊格式。一个期间使用的模式是PnYnMnD,其中n定义了期间内存在的年数,月数或天数。这意味着P1Y2M3D定义了1年,2个月和3天的时期

原创文章 317 获赞 416 访问量 112万+

猜你喜欢

转载自blog.csdn.net/u014082714/article/details/102708710