Java 8 时间日期详细用法
一、概述
java8 重新定义了一套语义非常清晰的日期、时间Api,位于java.time包中。该包中的所有类都是不可变且线程安全的。
其实,以上所有类都是基于java8 Clock类实现的,只是由于场景不同而显示方式不一样而已。而Clock又是基于System.currentTimeMillis() 实现的,所以以上时间api都是跟本主机时间关联的。
二、时间操作
以上虽然有很多个时间、日期类,但由于他们实现相同的接口,所以方法总体上是相通。下面就以LocalDateTime为例子:
- 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);
- getXXX(),获取某个时间字段
LocalDateTime now = LocalDateTime.now();
//获取年份 2018
int year = now.getYear();
//获取周几 7(星期天)
int dayOfWeek = now.getDayOfWeek().getValue();
//获取小时 14
int hour = now.getHour();
- 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);
- 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);
- 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 包
- 时间调整器: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);
- 日期、时间查询器: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
上一篇: docker-docker安装
下一篇: SAP“斗法”甲骨文 或将决战大数据领域