Java 8 要偏移DateTime的日期字符串
我正在将日期/日期时间字符串转换为Java 8 要偏移DateTime的日期字符串,java-8,java-time,datetimeoffset,datetime-parsing,Java 8,Java Time,Datetimeoffset,Datetime Parsing,我正在将日期/日期时间字符串转换为OffsetDateTime,并且我有一个日期时间格式,它可能有以下值之一 yyyy-MM-dd, yyyy/MM/dd 有时有时间,有时没有时间,我需要将其转换为OffsetDateTime 我试过下面的代码 // for format yyyy-MM-dd DateTimeFormatter DATE_FORMAT = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd")
OffsetDateTime
,并且我有一个日期时间格式,它可能有以下值之一
yyyy-MM-dd, yyyy/MM/dd
有时有时间,有时没有时间,我需要将其转换为OffsetDateTime
我试过下面的代码
// for format yyyy-MM-dd
DateTimeFormatter DATE_FORMAT = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.toFormatter();
由于没有时间,我将其设置为默认值,但当我尝试解析时
OffsetDateTime.parse("2016-06-06", DATE_FORMAT)
这是一个错误
线程“main”java.time.format.DateTimeParseException中的异常:无法分析文本“2016-06-06”:无法从TemporalAccessor获取OffsetDateTime:{},ISO解析为2016-06-06T00:00,类型为java.time.format.parsed
有人能帮我解决这个问题吗?
OffsetDateTime
需要知道时区与UTC的偏移量,因此您需要为此提供一个默认值。为计时字段提供默认值。偏移量\u秒
:
DateTimeFormatter DATE_FORMAT = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
或者,您拥有的格式足以让
LocalDateTime
创建OffsetDateTime
,您需要日期(日、月和年)、时间(小时、分钟、秒和纳秒)以及(与的差异)
您的输入只有日期,因此您必须构建其余的,或者假设它们的默认值
要解析这两种格式(yyyy-MM-dd
和yyy/MM/dd
),您可以使用带有可选模式的DateTimeFormatter
(由[]
分隔)并解析为LocalDate
(因为您只有日期字段):
您也可以使用它(稍微复杂一点,但工作方式相同):
然后,您可以设置构建LocalDateTime
的时间:
// set time to midnight
LocalDateTime ldt = dt.atStartOfDay();
// set time to 2:30 PM
LocalDateTime ldt = dt.atTime(14, 30);
// most common case: just one valid offset
OffsetDateTime odt = ldt.atOffset(validOffsets.get(0));
您还可以选择使用
parseDefaulting
,如中所述:
请注意,我必须将小时和分钟设置为零。您还可以将秒(ChronoField.SECOND\u OF u MINUTE
)和纳秒(ChronoField.NANO\u OF u SECOND
)设置为零,但设置小时和分钟足以将所有其他字段设置为零
您说过要使用系统的默认偏移量。这有点棘手 “系统的默认偏移量”将取决于系统的默认时区。一个时区可以有多个偏移,这取决于您在时间线上的时间 我将使用系统的默认时区(
美国/圣保罗
)作为示例。在下面的代码中,我使用的是ZoneId.systemDefault()
,但请记住,这在每个系统/环境中都是不同的对于下面的所有示例,请记住ZoneId.systemDefault()
返回美国/圣保罗
时区。如果你想得到一个特定的,你应该使用ZoneId.of(“zone\u 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);
其他情况(零或两个有效偏移)通常由于夏令时更改(DST)而发生
在圣保罗时区,DST将于2017年10月15日开始:午夜时分,时钟前移至凌晨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
zdt
变量将为2017-10-15T01:00-02:00[美国/圣保罗]
-时间和偏移量将在-02:00
偏移量时自动调整为凌晨1点
还有两个有效偏移量的情况。在圣保罗,DST将于2018年2月18日结束:午夜时分,时钟将从17日晚上11点向后移动1小时,偏移量从
-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
但是,如果创建一个ZoneDateTime
,它将使用第一个偏移作为默认值:
// 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-03:00[美国/圣保罗]
-它使用偏移量-03:00
(DST结束后)
只要提醒一下,即使在运行时,系统的默认时区也可以更改,最好使用特定的时区名称(如
ZoneId.of(“America/SaoèPaulo”)
)。通过调用ZoneId.getAvailableZoneIds()可以获得可用时区的列表(并选择最适合您的系统的时区)
的可能副本的可能副本是否有任何方法将系统默认偏移量添加为ChronoField.offset\u seconds这是一种快速的技巧,因为由于夏时制,系统上的当前偏移量可能与日期的正确偏移量不同。如果我想要ZonedDateTime而不是OffsetdateTime,请多谢。如何做到这一点?
// 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
// October 15th 2017 at midnight, DST starts in Sao Paulo
LocalDateTime ldt = LocalDateTime.parse("2017-10-15", parser);
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
// 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
// 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));
// 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());
// get offset after DST ends
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()).withLaterOffsetAtOverlap();