欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 8 时间日期详细用法

程序员文章站 2022-05-27 17:05:12
...

一、概述

java8 重新定义了一套语义非常清晰的日期、时间Api,位于java.time包中。该包中的所有类都是不可变且线程安全的。
Java 8 时间日期详细用法
其实,以上所有类都是基于java8 Clock类实现的,只是由于场景不同而显示方式不一样而已。而Clock又是基于System.currentTimeMillis() 实现的,所以以上时间api都是跟本主机时间关联的。

二、时间操作

以上虽然有很多个时间、日期类,但由于他们实现相同的接口,所以方法总体上是相通。下面就以LocalDateTime为例子:

  1. now(),创建当前时间,可指定时区
//当前本地时间:2018-12-16T13:45:11.232
LocalDateTime localDateTime = LocalDateTime.now();
 
//当前美国洛杉矶时间:2018-12-15T21:45:11.251
LocalDateTime losAngelesTime = LocalDateTime.now(ZoneId.of("America/Los_Angeles"));
LocalDateTime losAngelesTime2 = LocalDateTime.now(Clock.system(ZoneId.of("America/Los_Angeles")));

2.of(),创建指定的时间

// 2019-01-01T12:00:01 注:月份是从1开始算的了,不想Calendar是从0开始的
LocalDateTime time = LocalDateTime.of(2019, 1, 1, 12, 0, 1);
//另外月份也可以使用Month枚举类
LocalDateTime time1 = LocalDateTime.of(2019, Month.AUGUST, 1, 12, 1, 1);
  1. getXXX(),获取某个时间字段
LocalDateTime now = LocalDateTime.now();
//获取年份   2018
int year = now.getYear();
//获取周几   7(星期天)
int dayOfWeek = now.getDayOfWeek().getValue();
//获取小时   14
int hour = now.getHour();
  1. withXXX(),跳到指定的时间
//2018-12-16T14:12:32.162
LocalDateTime now = LocalDateTime.now();
//2019-02-16T14:10:32.162
LocalDateTime withTime = now.withYear(2019).withMonth(2).withMinute(10);
  1. plusXXX() / minusXXX() ,加 / 减 某个时间
//2018-12-16T14:18:01.005
LocalDateTime now = LocalDateTime.now();
//2019-01-01T14:18:01.005
LocalDateTime plusTime = now.plusDays(16);
//2018-12-16T04:18:01.005
LocalDateTime minusTime = now.minusHours(10);
  1. parse() / format(),解析时间 /格式化时间
//ofPattern 是自定义自己的格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String timeStr="2018-12-31 23:59:59";
LocalDateTime parseTime = LocalDateTime.parse(timeStr, dateTimeFormatter);
 
//DateTimeFormatter也提供一些自带的格式
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
LocalDateTime now = LocalDateTime.now();
String formatTime = now.format(formatter); //2018年12月16日 星期日

像LocalDate、LocalTime、LocalDateTime以及Instant这样表示时间点的日期-时间类提供了大量通用的方法

方法名 是否是静态方法 描述
from 依据传入的Temporal创建对象实例
now 依据系统时钟创建Temporal对象
of 由Temporal对象的某个部分创建该对象的实例
parse 由字符串创建Temporal对象的实例
atOffset 将Temporal对象和某个时区偏移相结合
atZone 将Temporal对象和某个时区相结合
format 使用某个指定的格式器将Temporal对象转换为字符串(Instant类不提供该方法)
get 读取Temporal对象的某一部分的值
minus 创建Temporal对象的一个副本,通过将当前Temporal对象的值减去一定的时长创建该副本
plus 创建Temporal对象的一个副本,通过将当前Temporal对象的值加上一定的时长创建该副本
with 以该Temporal对象为模板,对某些状态进行修改创建该对象的副本

三、Duration 或 Period

都是表示一段时间长度(时间量),只是Duration偏向“时间”方面的,如 10秒,1小时。而Period则偏向于“日期”方面的,如 1个月,1年。

//Duration 可以获取到两个时间相差的天数、小时、分钟、秒、毫秒
LocalDateTime ldt1 = LocalDateTime.of(2018, 10, 10, 12, 10, 10);
LocalDateTime ldt2 = LocalDateTime.of(2019, 5, 2, 2, 10, 10);
Duration duration = Duration.between(ldt1, ldt2);
System.out.println(duration.getSeconds());  // 17589600
System.out.println(duration.toHours());    //  4886
System.out.println(duration.toDays());    //   203
 
 
//Period 可以获取到两个时间相差的年数和月数
LocalDate ld1 = LocalDate.of(2017, 12, 26);
LocalDate ld2 = LocalDate.of(2019, 3, 27);
Period period = Period.between(ld1,ld2);
System.out.println(period.getYears());       // 1
System.out.println(period.toTotalMonths());  // 15
 
//特别注意以下两个getXXX()
System.out.println(period.getDays());    // 1
System.out.println(period.getMonths()); //  3

另外,这个类中的 ofXXX(),withXXX(),plusXXX(),minsXXX()等方法的含义都是跟上面的时间日期类相似。

方法名 是否是静态方法 方法描述
between 创建两个时间点之间的interval
from 由一个临时时间点创建interval
of 由它的组成部分创建interval的实例
parse 由字符串创建interval的实例
addTo 创建该interval的副本,并将其叠加到某个指定的temporal对象
get 读取该interval的状态
isNegative 检查该interval是否为负值,不包含零
isZero 检查该interval的时长是否为零
minus 通过减去一定的时间创建该interval的副本
multipliedBy 将interval的值乘以某个标量创建该interval的副本
negated 以忽略某个时长的方式创建该interval的副本
plus 以增加某个指定的时长的方式创建该interval的副本
substractFrom 从指定的temporal对象中减去该interval

四、java.time.tempral 包

Java 8 时间日期详细用法

  1. 时间调整器:TemporalAdjuster 和 TemporalAdjusters
//获取下个工作日
TemporalAdjuster nextWorkDayAdjuster = (temporal) -> {
    int dayOfWeek = temporal.get(ChronoField.DAY_OF_WEEK);
    if (dayOfWeek == DayOfWeek.FRIDAY.getValue()) {
        return temporal.plus(Period.ofDays(3));
    } else if (dayOfWeek == DayOfWeek.SATURDAY.getValue()) {
        return temporal.plus(Period.ofDays(2));
    } else {
        return temporal.plus(Period.ofDays(1));
    }
};
 
LocalDateTime now = LocalDateTime.now();
LocalDateTime nextWorkDay = now.with(nextWorkDayAdjuster);
System.out.println(nextWorkDay);
 
 
//获取下个月的第一天
LocalDateTime firstDayOfNextMonth = now.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(firstDayOfNextMonth);
  1. 日期、时间查询器:TemporalQuery 和 TemporalQuerys
//查询到过年还剩下几天
TemporalQuery<Long> newYearQuery = (temporalAccessor) -> {
    Temporal day = (Temporal) temporalAccessor;
    LocalDateTime newYear = LocalDateTime.of(2019, 2, 4, 0, 0, 0);
    //Duration duration = Duration.between(day, newYear);
    return ChronoUnit.DAYS.between(day, newYear);
};
 
LocalDateTime now = LocalDateTime.now();
Long remainDay = now.query(newYearQuery);
System.out.println(remainDay);
 
//只查询日期部分
LocalDate localDate = now.query(TemporalQueries.localDate());
System.out.println(localDate);

TemporalAdjuster中包含的工厂方法列表

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

五、LocalDateTime 与 Date 互转

/**
 * 将localDateTime 转为 date
 */
public static Date localDateTime2Date(LocalDateTime localDateTime) {
    ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
    Instant instant = zonedDateTime.toInstant();
    return Date.from(instant);
}
 
/**
 * 将date转为LocalDatetTime
 */
public static LocalDateTime date2LocalDateTime(Date date) {
    Instant instant = date.toInstant();
    ZoneId zoneId = ZoneId.systemDefault();
    return instant.atZone(zoneId).toLocalDateTime();
}

六、解析日期-时间对象

处理日期和时间对象时,格式化以及解析日期-时间对象时另一个非常重要的功能。新的java.time.format包就是特别为这个目的而设计的。这个包中,最重要的类时DateTimeFormatter。创建格式器最简单的方法是通过它的静态工厂方法以及常量。像BASIC_ISO_DATE和ISO_LOCAL_DATE这样的常量是DateTimeFormatter类的预定义实例。所有的DateTimeFormatter实例都能用于以一定格式创建代表特定日期或时间的字符串。

LocalDate date=LocalDate.of(2014,3,18);
String s1=date.format(DateTimeFormatter.BASIC_ISO_DATE);//20140318
String s2=date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2014-03-18

可以通过解析代表日期或时间的字符串重新创建该日期对象。所有的日期和时间API都提供了表示时间点或者时间段的工厂方法,你可以使用工厂方法parse达到重创该日期对象的目的。

LocalDate date1=LocalDAte.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2=LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);

和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。所以,你能够以单例模式创建格式器实例,就像DateTimeFormatter所定义的那些常量,并能在多个线程间共享这些实例。DateTimeFormatter类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器。

DateTimeFormatter formatter=DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1=LocalDate.of(2014,3,18);
String formattedDate=date1.format(formatters);
LocalDate date2=LocalDate.parse(formattedDate,formatter);

LocalDate的formate方法使用指定的模式生成了一个代表该日期的字符串。紧接着,静态的parse方法使用同样的格式器解析了刚才生成的字符串,并重建了该日期对象。

ofPattern方法也提供了一个重载的版本,使用它你可以创建某个Locale的格式器

DateTimeFormatter italianFormatter=
                DateTimeFormatter.ofPattern("d. MMMM yyyy",Locale.ITALIAN);
LocalDate date1=LocalDate.of(2014,3,18);
String fromattedDate=date.format(italianFormatter);//18. marzo 2014
LocalDate date2=LocalDate.parse(formattedDate,italianFormatter);

最后,如果还需要更加细粒度的控制,DateTimeFormatterBuilder类还提供了更复杂的格式器,可以选择恰当的方法,一步一步地构造自己的格式器。另外,它还提供了非常强大的解析功能,比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不精确地匹配指定的模式)、填充,以及在格式器中指定可选节。

比如,可以通过DateTimeFormatterBuilder自己编程实现我们在代码清单中使用的italianFormatter

DateTimeFormatter italianFormatter=new DateTimeFormatterBuilder()
        .appendText(ChronoField.DAY_OF_MONTH)
        .appendLiteral(". ")
        .appendText(ChronoField.MONTH_OF_YEAR)
        .appendLiteral(" ")
        .appendText(ChronoField.YEAR)
        .parseCaseInsensitive()
        .toFormatter(Locale.ITALIAN);

七、补充:

纳秒: 表示一秒内的某一瞬间,范围是:0~999,999,999,java中用int类型存放。

在我们的普遍观念中,一年就是地球绕太阳一圈,时长为:365(+1)2460*60秒。这是理想中的世界时(UT),但实际上由于地球的不均匀和长期变慢性,而让时间出现了 ± 1秒的偏差,也称为闰秒。为了调整这个偏差,引入了协调世界时(UTC),它会在闰秒出现的时候,将那一分钟调整为59秒或61秒。

UTC正是我们手机电脑中的时间,它跟GMT可看做是同一东西。

zone offset:指距离UTC 0 偏移的时间,目前偏移量的范围是:-12:00 到 +14:00,而java中为了其扩展性,而将范围定为:-18:00 到 +18:00。

服务在实现app本地时间功能时,建议不要用手机的offset second来做,因为有些地方由于冬令时和夏令时的存在,而导致偏移量是不固定的。(可能你会想更新这个偏移量,但不建议这么做,毕竟用户可以随便修改这个值)所以我个人建议是存一个zoneId,(如:America/Los_Angeles),因为它能自动帮我们实现夏令时和冬令时。

参考文章:

https://blog.csdn.net/y_k_y/article/details/84633119

https://blog.csdn.net/zsx157326/article/details/80887673?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

相关标签: Java8新特征