嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:
本文编写的单元测试是基于java11,具体的版本号是:11.0.19
OffsetDateTime是Java 8中引入的一个不可变且线程安全的日期时间数据类型,用于表示带有时区偏移的日期和时间。因为它是一种不可变的数据类型,所以在进行比较或传递时不会发生变化。
OffsetDateTime可以表示从本地时间线LocalDateTime到即时时间线Instant之间的日期和时间。两个时间线之间的差异是UTC /格林威治的偏移量,由ZoneOffset表示。在两个时间线之间转换涉及使用从ZoneId访问的ZoneId计算偏移量。在处理带有时区偏移的日期和时间时,使用OffsetDateTime可以方便地表示和操作具有特定时区偏移的日期和时间,避免时区转换带来的问题。
而时区是为了克服时间上的混乱,在1884年在华盛顿召开的一次国际经度会议上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1-12区,西1-12区。每个时区横跨经度15度,时间正好是1小时。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。
中国横跨东五区至东九区5个时区,自西向东可依次分为东六区、东七区、东八区、东九区、东十区。为了使用方便,中国采用首都北京所在的东八区的区时作为全国统一使用时间,也就是北京时间。
OffsetDateTime#now()是Java 8中 java.time.OffsetDateTime类的一个非常简单方法,这个方法不需要任何参数,返回表示当前日期和时间的 OffsetDateTime 对象。
@Test
public void test() {
OffsetDateTime offsetDateTime = OffsetDateTime.now();
System.out.println(offsetDateTime);//输出结果:2023-11-27T18:15:42.924290700+08:00
}
OffsetDateTime#of(...)和OffsetDateTime#ofInstant(...)都是用于创建和操作具有偏移量的日期和时间对象的工具,区别在于接受参数类型上:
@Test
public void test2() {
LocalDateTime localDateTime = LocalDateTime.of(2023, 11, 27, 18, 42, 56);
//北京属于东八区
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(8));
//纽约属于西五区
OffsetDateTime offsetDateTime1 = OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(-5));
System.out.println(offsetDateTime);
//输出结果:2023-11-27T18:42:56+08:00
Duration between = Duration.between(offsetDateTime, offsetDateTime1);
long hours = between.toHours();
System.out.println(hours);
//以美国纽约时间与中国北京时间相差13个小时,输出结果:13
Instant instant = Instant.ofEpochSecond(1L);
System.out.println(instant);
//输出结果:1970-01-01T00:00:01Z
OffsetDateTime offsetDateTime2 = OffsetDateTime.ofInstant(instant, ZoneId.systemDefault());
System.out.println(offsetDateTime2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//输出结果:1970-01-01 08:00:01,这与上面的输出结果相差了8个小时,原因就在于ZoneId.systemDefault()获取了系统默认时区,中国采用东八区时区,因此与格林尼治时间相差了8小时
}
OffsetDateTime#from(...)可以将其他类型的日期和时间对象转换为 OffsetDateTime 对象。这个方法接受一个 TemporalAccessor 对象作为参数,并根据该对象中的日期和时间信息创建一个新的 OffsetDateTime 对象。当你需要将其他类型的日期和时间对象转换为 OffsetDateTime 对象时,可以使用这个方法。
@Test
public void test3() {
LocalDateTime localDateTime = LocalDateTime.of(2023, 11, 27, 18, 42, 56);
ZoneOffset zoneOffset = ZoneOffset.ofHours(8);
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneOffset);
OffsetDateTime from = OffsetDateTime.from(zonedDateTime);
System.out.println(from);//2023-11-27T18:42:56+08:00
}
OffsetDateTime#parse() 的主要功能是将字符串解析为 OffsetDateTime 对象,主要是用于将字符串表示的日期和时间转换为 OffsetDateTime 对象时,它接受一个字符串作为参数,并尝试将其解析为 OffsetDateTime,解析过程会考虑本地时区,并将字符串解析为具有偏移量的日期和时间。
@Test
public void test4() {
String dateTimeStr = "2023-11-28T09:45:56+08:00";
OffsetDateTime from = OffsetDateTime.parse(dateTimeStr);
System.out.println(from);//输出结果:2023-11-28T09:45:56+08:00
}
OffsetDateTime#isSupported()用于检查是否支持特定的单位或字段。如果支持,方法将返回 true;如果不支持,方法将返回 false。主要的使用场景:在执行日期时间运算之前,可以使用该方法检查是否支持所使用的单位或字段,以避免因操作不支持的单位或字段而引发异常。
@Test
public void test5() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
boolean supported1 = offsetDateTime.isSupported(ChronoField.YEAR);
boolean supported2 = offsetDateTime.isSupported(ChronoUnit.HOURS);
System.out.println(supported1);//输出结果:true
System.out.println(supported2);//输出结果:true
}
OffsetDateTime#range()方法是用于获取特定字段的范围的ValueRange对象。该方法的功能作用是根据作为参数传递的字段,获取该字段的有效值范围。具体来说,该方法接受一个参数,即要查询范围的字段,然后返回一个ValueRange对象,该对象表示该字段的有效值范围。如果此方法不支持该字段,则可能引发异常。
使用场景包括需要确定特定字段的范围和有效值的场景。例如,在数据库查询中,可以使用该方法来获取特定字段的取值范围,从而构建更精确的查询条件。在日期时间处理中,可以使用该方法获取特定日期时间字段的有效范围,以便进行日历计算、时间间隔计算等操作。
需要注意的是,该方法仅对OffsetDateTime对象支持的字段返回ValueRange对象。因此,如果查询的字段不受支持,此方法可能会引发异常。在使用时需要确保查询的字段是受支持的,以避免出现异常情况。
@Test
public void test6() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
ValueRange range = offsetDateTime.range(ChronoField.DAY_OF_MONTH);
System.out.println(range.getMinimum());//输出结果:1
System.out.println(range.getMaximum());//输出结果:30
}
这些方法主要用于获取OffsetDateTime对象中指定字段的值,需要注意的是,该方法仅对受支持的字段返回有效值,对于不支持的字段可能会引发异常。因此,在使用时需要确保查询的字段是受支持的,以避免出现异常情况。
@Test
public void test7() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
int year = offsetDateTime.get(ChronoField.YEAR);
System.out.println(year);//输出结果:2023
int month = offsetDateTime.get(ChronoField.MONTH_OF_YEAR);
System.out.println(month);//输出结果:11
int day = offsetDateTime.get(ChronoField.DAY_OF_MONTH);
System.out.println(day);//输出结果:28
int hour = offsetDateTime.get(ChronoField.HOUR_OF_DAY);
System.out.println(hour);//输出结果:9
int minutes = offsetDateTime.get(ChronoField.MINUTE_OF_HOUR);
System.out.println(minutes);//输出结果:46
int second = offsetDateTime.getSecond();
System.out.println(second);//输出结果:56
int year1 = offsetDateTime.getYear();
System.out.println(year1);//输出结果:2023
int month1 = offsetDateTime.getMonthValue();
System.out.println(month1);//输出结果:11
int hour1 = offsetDateTime.getHour();
System.out.println(hour1);//输出结果:9
int dayOfYear = offsetDateTime.getDayOfYear();
int dayOfMonth = offsetDateTime.getDayOfMonth();
int dayOfWeek = offsetDateTime.getDayOfWeek().getValue();
System.out.println(dayOfYear);//输出结果:332
System.out.println(dayOfMonth);//输出结果:28
System.out.println(dayOfWeek);//输出结果:2
}
需要注意的是,这些方法都可能抛出 DateTimeException 异常,如果输入的 OffsetDateTime 对象包含无效的日期或时间信息,或者不支持某些字段。因此,在使用这些方法时,需要确保输入的 OffsetDateTime 对象是有效和受支持的。
@Test
public void test8() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
LocalDateTime localDateTime = offsetDateTime.toLocalDateTime();
System.out.println(localDateTime);//输出结果:2023-11-28T09:46:56
LocalDate localDate = offsetDateTime.toLocalDate();
System.out.println(localDate);//输出结果:2023-11-28
LocalTime localTime = offsetDateTime.toLocalTime();
System.out.println(localTime);//输出结果:09:46:56
}
这些方法都允许开发者以特定的粒度(年、月、日、小时、分钟、秒和纳米秒)调整一个给定的OffsetDateTime实例。这些方法使得开发者能够根据特定的需求(如改变日期或时间)来操作和修改日期和时间数据。
具体如下:
需要注意的是,这些方法大多数情况下不会改变原始的OffsetDateTime实例,而是返回一个新的实例,原始实例依然保持不变。这是由于Java中大多数不可变类(如String、Integer等)的设计思路是一致的。
@Test
public void test9() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
OffsetDateTime with = offsetDateTime.with(ChronoField.MONTH_OF_YEAR, 10);
System.out.println(with);//输出结果:2023-10-28T09:46:56+08:00
TemporalAdjuster temporalAdjuster = item -> item.plus(1, ChronoUnit.HOURS);
OffsetDateTime with1 = offsetDateTime.with(temporalAdjuster);
System.out.println(with1);//输出结果:2023-11-28T10:46:56+08:00
OffsetDateTime offsetDateTime1 = offsetDateTime.withYear(2020);
System.out.println(offsetDateTime1);//输出结果:2020-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime2 = offsetDateTime.withMonth(2);
System.out.println(offsetDateTime2);//输出结果:2023-02-28T09:46:56+08:00
OffsetDateTime offsetDateTime3 = offsetDateTime.withDayOfMonth(25);
System.out.println(offsetDateTime3);//输出结果:2023-11-25T09:46:56+08:00
OffsetDateTime offsetDateTime4 = offsetDateTime.withOffsetSameInstant(ZoneOffset.ofHours(-13));
System.out.println(offsetDateTime4);
//输出结果:2023-11-27T12:46:56-13:00,什么意思呢?意思是:同一时刻北京时间是2023-11-28T09:46:56+08:00,而同一时刻的纽约时间是:2023-11-27T12:46:56-13:00
OffsetDateTime offsetDateTime5 = offsetDateTime.withOffsetSameLocal(ZoneOffset.ofHours(-13));
System.out.println(offsetDateTime5);
//输出结果:2023-11-28T09:46:56-13:00
}
OffsetDateTime#truncatedTo()用于将当前OffsetDateTime对象的时间部分截断到给定的时间单位,例如分钟、小时、天等。截断后的OffsetDateTime对象将不再包含被截断单位之前的时间信息。
使用场景:
@Test
public void test10() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
//输出结果:2023-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime1 = offsetDateTime.truncatedTo(ChronoUnit.DAYS);
System.out.println(offsetDateTime1);
//输出结果:2023-11-28T00:00+08:00
}
OffsetDateTime#plus()可以在当前OffsetDateTime对象的基础上加上指定的时间长度。这个方法有很多重载版本,可以支持不同的时间单位,包括年(Years)、月(Months)、周(Weeks)、天(Days)、小时(Hours)、分钟(Minutes)、秒(Seconds)和纳秒(Nanos)。这些方法都返回一个新的OffsetDateTime对象,表示添加了指定时间长度后的日期和时间。它们不会改变原始的OffsetDateTime对象。如果添加的时间长度为负数,则会相应地减去时间长度。注意,如果程序超出支持的数据和时间范围,则可能会引发DateTimeException异常。
具体如下:
@Test
public void test11() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
OffsetDateTime offsetDateTime1 = offsetDateTime.plus(1, ChronoUnit.YEARS);
System.out.println(offsetDateTime1);//输出结果:2024-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime2 = offsetDateTime.plus(Period.ofYears(2));
System.out.println(offsetDateTime2);//输出结果:2025-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime3 = offsetDateTime.plusYears(2);
System.out.println(offsetDateTime3);//输出结果:2025-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime4 = offsetDateTime.plusMonths(1);
System.out.println(offsetDateTime4);//输出结果:2023-12-28T09:46:56+08:00
OffsetDateTime offsetDateTime5 = offsetDateTime.plusDays(3);
System.out.println(offsetDateTime5);//输出结果:2023-12-01T09:46:56+08:00
OffsetDateTime offsetDateTime6 = offsetDateTime.plusHours(1);
System.out.println(offsetDateTime6);//输出结果:2023-11-28T10:46:56+08:00
OffsetDateTime offsetDateTime7 = offsetDateTime.plusMinutes(4);
System.out.println(offsetDateTime7);//输出结果:2023-11-28T09:50:56+08:00
OffsetDateTime offsetDateTime9 = offsetDateTime.plusSeconds(4);
System.out.println(offsetDateTime9);//输出结果:2023-11-28T09:47+08:00
OffsetDateTime offsetDateTime8 = offsetDateTime.plusWeeks(1);
System.out.println(offsetDateTime8);//输出结果:2023-12-05T09:46:56+08:00
}
这些方法用于在日期时间对象中减去指定的时间单位。具体如下:
这些方法都返回一个新生成的 OffsetDateTime 对象,而不是修改原始对象。使用这些方法可以进行精确的日期和时间计算,适用于各种场景,如日程安排、时间间隔计算等。需要注意的是,当减去负值时,将会增加相应的时间单位。例如,调用 minusYears(-1) 将增加一年。
@Test
public void test12() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
OffsetDateTime offsetDateTime1 = offsetDateTime.minus(1, ChronoUnit.YEARS);
System.out.println(offsetDateTime1);//输出结果:2022-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime2 = offsetDateTime.minus(Period.ofYears(2));
System.out.println(offsetDateTime2);//输出结果:2021-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime3 = offsetDateTime.minusYears(2);
System.out.println(offsetDateTime3);//输出结果:2021-11-28T09:46:56+08:00
OffsetDateTime offsetDateTime4 = offsetDateTime.minusMonths(1);
System.out.println(offsetDateTime4);//输出结果:2023-10-28T09:46:56+08:00
OffsetDateTime offsetDateTime5 = offsetDateTime.minusDays(3);
System.out.println(offsetDateTime5);//输出结果:2023-11-25T09:46:56+08:00
OffsetDateTime offsetDateTime6 = offsetDateTime.minusHours(1);
System.out.println(offsetDateTime6);//输出结果:2023-11-28T08:46:56+08:00
OffsetDateTime offsetDateTime7 = offsetDateTime.minusMinutes(4);
System.out.println(offsetDateTime7);//输出结果:2023-11-28T09:42:56+08:00
OffsetDateTime offsetDateTime9 = offsetDateTime.minusSeconds(4);
System.out.println(offsetDateTime9);//输出结果:2023-11-28T09:46:52+08:00
OffsetDateTime offsetDateTime8 = offsetDateTime.minusWeeks(1);
System.out.println(offsetDateTime8);//输出结果:2023-11-21T09:46:56+08:00
}
OffsetDateTime#query() 接受一个TemporalQuery对象作为参数,并使用该查询对象对当前 OffsetDateTime 实例进行查询。TemporalQuery 是一个函数式接口,它接受一个 Temporal 对象并返回一个查询结果。你可以使用 OffsetDateTime#query() 方法进行各种自定义查询。
@Test
public void test13() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
TemporalQuery temporalQuery = item -> item.get(ChronoField.YEAR);
Integer year = offsetDateTime.query(temporalQuery);
System.out.println(year);//输出结果:2023
}
OffsetDateTime#until()用于计算当前OffsetDateTime对象,以指定的单位到另一个日期时间为止的时间量。
@Test
public void test14() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2020, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
OffsetDateTime offsetDateTime2 = OffsetDateTime.of(2023, 12, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
long until = offsetDateTime.until(offsetDateTime2, ChronoUnit.MONTHS);
System.out.println(until);//输出结果:37
}
OffsetDateTime#format() 用于将当前的日期时间对象格式化为指定的字符串表示形式。
@Test
public void test15() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2020, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
String format = offsetDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(format);//输出结果:2020-11-28 09:46:56
}
这两个方法的使用场景如下:
需要注意的是,这两个方法都会创建一个新的ZonedDateTime对象,而不会修改原始的OffsetDateTime对象。因此,在使用这些方法时需要注意对新的ZonedDateTime对象的处理。
@Test
public void test16() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2020, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
ZonedDateTime zonedDateTime = offsetDateTime.atZoneSimilarLocal(ZoneOffset.ofHours(-13));
System.out.println(zonedDateTime);//输出结果:2020-11-28T09:46:56-13:00
ZonedDateTime zonedDateTime1 = offsetDateTime.atZoneSameInstant(ZoneOffset.ofHours(-13));
System.out.println(zonedDateTime1);//输出结果:2020-11-27T12:46:56-13:00
}
@Test
public void test17() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2020, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
OffsetTime offsetTime = offsetDateTime.toOffsetTime();
System.out.println(offsetTime);//输出结果:09:46:56+08:00
ZonedDateTime zonedDateTime = offsetDateTime.toZonedDateTime();
System.out.println(zonedDateTime);//输出结果:2020-11-28T09:46:56+08:00
Instant instant = offsetDateTime.toInstant();
System.out.println(instant);//输出结果:2020-11-28T01:46:56Z
long epochSecond = offsetDateTime.toEpochSecond();
System.out.println(epochSecond);//输出结果:1606528016
}
@Test
public void test18() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(2020, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(8));
OffsetDateTime offsetDateTime2 = OffsetDateTime.of(2020, 11, 28, 9, 46, 56, 0, ZoneOffset.ofHours(-5));
int compareTo = offsetDateTime.compareTo(offsetDateTime2);
System.out.println(compareTo);//输出结果:-1
boolean before = offsetDateTime.isBefore(offsetDateTime2);
System.out.println(before);//输出结果:true
boolean after = offsetDateTime.isAfter(offsetDateTime2);
System.out.println(after);//输出结果:false
boolean equal = offsetDateTime.isEqual(offsetDateTime2);
System.out.println(equal);//输出结果:false
}
OffsetDateTime的使用场景非常广泛,主要包括以下几个方面:
总之,OffsetDateTime在各种应用场景下都可以发挥重要作用,特别是在需要处理全球化日期时间、时区转换、精确时间戳等需求的场景下。