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);
    }

结果:

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天的时期

获取上月及当前月

YearMonth yearMonth = YearMonth.now();
System.out.println(yearMonth.toString());

YearMonth lastYearMonth = YearMonth.now().minusMonths(1);
System.out.println(lastYearMonth.toString());

结果:

2020-09
2020-08

LocalDate 获取上月对象

LocalDate date = LocalDate.now().minusMonths(1);

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    比较两个日期对象是否相等

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值