要创建OffsetDateTime
,你需要的日期(日,月,年),在时间(小时,分,秒和纳秒)和offset(一般为从UTC差)。
您的输入仅包含日期,因此您必须构建其余部分或为其设定默认值。
要分析这两种格式(yyyy-MM-dd
和yyyy/MM/dd
),您可以使用可选模式的DateTimeFormatter
(由[]
分隔),并解析到LocalDate
(因为你只有日期字段):
// parse yyyy-MM-dd or yyyy/MM/dd
DateTimeFormatter parser = DateTimeFormatter.ofPattern("[yyyy-MM-dd][yyyy/MM/dd]");
// parse yyyy-MM-dd
LocalDate dt = LocalDate.parse("2016-06-06", parser);
// or parse yyyy/MM/dd
LocalDate dt = LocalDate.parse("2016/06/06", parser);
你也可以使用(更复杂一点,但它的工作方式相同)这样的:
// year followed by - or /, followed by month, followed by - or /, followed by day
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy[-][/]MM[-][/]dd");
然后,你可以设置为建立一个LocalDateTime
时间:
// set time to midnight
LocalDateTime ldt = dt.atStartOfDay();
// set time to 2:30 PM
LocalDateTime ldt = dt.atTime(14, 30);
或者,您也可以使用parseDefaulting
,如@greg's answer已经解释:
// parse yyyy-MM-dd or yyyy/MM/dd
DateTimeFormatter parser = new DateTimeFormatterBuilder().appendPattern("[yyyy-MM-dd][yyyy/MM/dd]")
// set hour to zero
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
// set minute to zero
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
// create formatter
.toFormatter();
// parse the LocalDateTime, time will be set to 00:00
LocalDateTime ldt = LocalDateTime.parse("2016-06-06", parser);
注意,我不得不小时和分钟设置为零。您还可以将秒(ChronoField.SECOND_OF_MINUTE
)和纳秒(ChronoField.NANO_OF_SECOND
)设置为零,但设置小时和分钟就足以将所有其他字段设置为零。
您告诉您要使用系统的默认偏移量。这有点棘手。
“系统的默认偏移量”取决于系统的默认时区。并且时区可以有多个偏移量,具体取决于您在时间轴上的时的。
我将以我的系统的默认时区(America/Sao_Paulo
)为例。在下面的代码中,我使用的是ZoneId.systemDefault()
,但请记住,这在每个系统/环境中都会有所不同。 对于以下所有示例,请记住ZoneId.systemDefault()
返回America/Sao_Paulo
时区。如果你想得到一个特定的,你应该使用ZoneId.of("zone_name")
- 实际上这是首选。
首先,你必须得到有效补偿的列表为LocalDateTime
,在指定的时区:
// using the parser with parseDefaulting
LocalDateTime ldt = LocalDateTime.parse("2016-06-06", parser);
// get all valid offsets for the date/time, in the specified timezone
List<ZoneOffset> validOffsets = ZoneId.systemDefault().getRules().getValidOffsets(ldt);
据javadoc,该validOffsets
列表大小,对于任何给定的本地日期时间,可以是零,一个或两个。
对于大多数情况下,只会有一个有效的偏移量。在这种情况下,它是简单明了,以获得OffsetDateTime
:
// most common case: just one valid offset
OffsetDateTime odt = ldt.atOffset(validOffsets.get(0));
的其他情况下(0或2个的有效偏移)通常是由于夏令时更改(DST)发生。
在圣保罗时区,DST将开始在10月15日日 2017:在午夜,时钟向前移动凌晨1点和偏移从-03:00
到-02:00
变化。这意味着所有当地时间00:00至00:59都不存在 - 您也可以认为时钟从23:59直接变为01:00。
因此,在圣保罗时区,这个日期就没有有效的抵消:
// October 15th 2017 at midnight, DST starts in Sao Paulo
LocalDateTime ldt = LocalDateTime.parse("2017-10-15", parser);
// system's default timezone is America/Sao_Paulo
List<ZoneOffset> validOffsets = ZoneId.systemDefault().getRules().getValidOffsets(ldt);
System.out.println(validOffsets.size()); // zero
有没有有效的补偿,所以你必须决定如何在这样的情况下做的(使用“默认”一个?抛出异常?)。
即使你的时区今天没有DST,它可能在过去(过去的日期可能是这种情况),或者它可能在未来(因为任何国家的DST和偏移量都是由政府和法律界定,并且不能保证将来没有人会改变)。
如果创建一个ZonedDateTime
,不过,其结果将是不同的:
// October 15th 2017 at midnight, DST starts in Sao Paulo
LocalDateTime ldt = LocalDateTime.parse("2017-10-15", parser);
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
的zdt
变量将是2017-10-15T01:00-02:00[America/Sao_Paulo]
- 时间和偏移量自动调整为凌晨1点在-02:00
偏移。
并且存在两个有效偏移量的情况。在圣保罗,DST将于2月18日结束th 2018:在午夜,时钟将会转移1小时回到11 PM 17 th,并且偏移量从-02:00
变为-03:00
。这意味着在23:00和23:59之间的所有当地时间都将存在两次,两次偏移。
当我将默认时间设置为午夜时,将只有一个有效偏移。 但是假设我决定在23:00使用默认时间:
// parse yyyy-MM-dd or yyyy/MM/dd
parser = new DateTimeFormatterBuilder().appendPattern("[yyyy-MM-dd][yyyy/MM/dd]")
// *** set hour to 11 PM ***
.parseDefaulting(ChronoField.HOUR_OF_DAY, 23)
// set minute to zero
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
// create formatter
.toFormatter();
// February 18th 2018 at midnight, DST ends in Sao Paulo
// local times from 23:00 to 23:59 at 17th exist twice
LocalDateTime ldt = LocalDateTime.parse("2018-02-17", parser);
// system's default timezone is America/Sao_Paulo
List<ZoneOffset> validOffsets = ZoneId.systemDefault().getRules().getValidOffsets(ldt);
System.out.println(validOffsets.size()); // 2
会有为LocalDateTime
2个有效偏移。在这种情况下,你必须选择其中之一:
// DST offset: 2018-02-17T23:00-02:00
OffsetDateTime dst = ldt.atOffset(validOffsets.get(0));
// non-DST offset: 2018-02-17T23:00-03:00
OffsetDateTime nondst = ldt.atOffset(validOffsets.get(1));
如果创建一个ZonedDateTime
,但是,它会使用作为默认的第一偏移:
// February 18th 2018 at midnight, DST ends in Sao Paulo
LocalDateTime ldt = LocalDateTime.parse("2018-02-17", parser);
// system's default timezone is America/Sao_Paulo
List<ZoneOffset> validOffsets = ZoneId.systemDefault().getRules().getValidOffsets(ldt);
// by default it uses DST offset
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
zdt
将2018-02-17T23:00-02:00[America/Sao_Paulo]
- 记它默认使用DST偏移量(-02:00
)。
如果你想抵消DST结束后,你可以这样做:
// get offset after DST ends
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()).withLaterOffsetAtOverlap();
zdt
将2018-02-17T23:00-03:00[America/Sao_Paulo]
- 它采用了偏移-03:00
(DST结束后)。
只是提醒的是系统默认的时区甚至可以在运行时改变,而且最好使用特定的区域名称(如ZoneId.of("America/Sao_Paulo")
)。您可以通过拨打ZoneId.getAvailableZoneIds()
获取可用时区列表(并选择最适合您的系统的时区)。
可能的重复https://stackoverflow.com/questions/44297939/convert-string-to-offsetdatetime-in-java –
[将字符串转换为Java中的OffsetDateTime](https://stackoverflow.com/问题/ 44297939/convert-string-to-offsetdatetime-in-java) –