Skip to content

Commit eaa4197

Browse files
committed
8272473: Parsing epoch seconds at a DST transition with a non-UTC parser is wrong
Reviewed-by: phh Backport-of: fe7d70886cc9985478c5810eff0790648a9aae41
1 parent 15f3e0e commit eaa4197

File tree

2 files changed

+16
-7
lines changed

2 files changed

+16
-7
lines changed

src/java.base/share/classes/java/time/format/Parsed.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -349,10 +349,11 @@ private void resolveInstantFields() {
349349
}
350350

351351
private void resolveInstantFields0(ZoneId selectedZone) {
352-
Instant instant = Instant.ofEpochSecond(fieldValues.remove(INSTANT_SECONDS));
352+
Instant instant = Instant.ofEpochSecond(fieldValues.get(INSTANT_SECONDS));
353353
ChronoZonedDateTime<?> zdt = chrono.zonedDateTime(instant, selectedZone);
354354
updateCheckConflict(zdt.toLocalDate());
355355
updateCheckConflict(INSTANT_SECONDS, SECOND_OF_DAY, (long) zdt.toLocalTime().toSecondOfDay());
356+
updateCheckConflict(INSTANT_SECONDS, OFFSET_SECONDS, (long) zdt.getOffset().getTotalSeconds());
356357
}
357358

358359
//-----------------------------------------------------------------------
@@ -593,9 +594,9 @@ private void resolveFractional() {
593594
}
594595

595596
private void resolveInstant() {
596-
// add instant seconds if we have date, time and zone
597+
// add instant seconds (if not present) if we have date, time and zone
597598
// Offset (if present) will be given priority over the zone.
598-
if (date != null && time != null) {
599+
if (!fieldValues.containsKey(INSTANT_SECONDS) && date != null && time != null) {
599600
Long offsetSecs = fieldValues.get(OFFSET_SECONDS);
600601
if (offsetSecs != null) {
601602
ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue());

test/jdk/java/time/test/java/time/format/TestDateTimeParsing.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -82,12 +82,14 @@
8282
import org.testng.annotations.Test;
8383

8484
/**
85-
* Test parsing of edge cases.
85+
* @test
86+
* @summary Test parsing of edge cases.
87+
* @bug 8272473
8688
*/
87-
@Test
8889
public class TestDateTimeParsing {
8990

9091
private static final ZoneId PARIS = ZoneId.of("Europe/Paris");
92+
private static final ZoneId NEW_YORK = ZoneId.of("America/New_York");
9193
private static final ZoneOffset OFFSET_0230 = ZoneOffset.ofHoursMinutes(2, 30);
9294

9395
private static final DateTimeFormatter LOCALFIELDS = new DateTimeFormatterBuilder()
@@ -102,6 +104,7 @@ public class TestDateTimeParsing {
102104
.appendInstant().toFormatter();
103105
private static final DateTimeFormatter INSTANT_WITH_PARIS = INSTANT.withZone(PARIS);
104106
private static final DateTimeFormatter INSTANT_WITH_0230 = INSTANT.withZone(OFFSET_0230);
107+
private static final DateTimeFormatter INSTANT_WITH_NEW_YORK = INSTANT.withZone(NEW_YORK);
105108
private static final DateTimeFormatter INSTANT_OFFSETID = new DateTimeFormatterBuilder()
106109
.appendInstant().appendLiteral(' ').appendOffsetId().toFormatter();
107110
private static final DateTimeFormatter INSTANT_OFFSETSECONDS = new DateTimeFormatterBuilder()
@@ -114,6 +117,7 @@ public class TestDateTimeParsing {
114117
private static final DateTimeFormatter INSTANTSECONDS_NOS_WITH_PARIS = INSTANTSECONDS_NOS.withZone(PARIS);
115118
private static final DateTimeFormatter INSTANTSECONDS_OFFSETSECONDS = new DateTimeFormatterBuilder()
116119
.appendValue(INSTANT_SECONDS).appendLiteral(' ').appendValue(OFFSET_SECONDS).toFormatter();
120+
private static final DateTimeFormatter INSTANTSECONDS_WITH_NEW_YORK = INSTANTSECONDS.withZone(NEW_YORK);
117121

118122
@DataProvider(name = "instantZones")
119123
Object[][] data_instantZones() {
@@ -125,11 +129,15 @@ Object[][] data_instantZones() {
125129
{LOCALFIELDS_WITH_0230, "2014-06-30 01:02:03", ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, OFFSET_0230)},
126130
{INSTANT_WITH_PARIS, "2014-06-30T01:02:03Z", ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, ZoneOffset.UTC).withZoneSameInstant(PARIS)},
127131
{INSTANT_WITH_0230, "2014-06-30T01:02:03Z", ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, ZoneOffset.UTC).withZoneSameInstant(OFFSET_0230)},
132+
{INSTANT_WITH_NEW_YORK, "2020-11-01T05:00:00Z", ZonedDateTime.of(2020, 11, 1, 5, 0, 0, 0, ZoneOffset.UTC).withZoneSameInstant(NEW_YORK)},
133+
{INSTANT_WITH_NEW_YORK, "2020-11-01T06:00:00Z", ZonedDateTime.of(2020, 11, 1, 6, 0, 0, 0, ZoneOffset.UTC).withZoneSameInstant(NEW_YORK)},
128134
{INSTANT_OFFSETID, "2014-06-30T01:02:03Z +02:30", ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, ZoneOffset.UTC).withZoneSameInstant(OFFSET_0230)},
129135
{INSTANT_OFFSETSECONDS, "2014-06-30T01:02:03Z 9000", ZonedDateTime.of(2014, 6, 30, 1, 2, 3, 0, ZoneOffset.UTC).withZoneSameInstant(OFFSET_0230)},
130136
{INSTANTSECONDS_WITH_PARIS, "86402", Instant.ofEpochSecond(86402).atZone(PARIS)},
131137
{INSTANTSECONDS_NOS_WITH_PARIS, "86402.123456789", Instant.ofEpochSecond(86402, 123456789).atZone(PARIS)},
132138
{INSTANTSECONDS_OFFSETSECONDS, "86402 9000", Instant.ofEpochSecond(86402).atZone(OFFSET_0230)},
139+
{INSTANTSECONDS_WITH_NEW_YORK, "1604206800", Instant.ofEpochSecond(1604206800).atZone(NEW_YORK)}, // 2020-11-01T05:00:00 UTC
140+
{INSTANTSECONDS_WITH_NEW_YORK, "1604210400", Instant.ofEpochSecond(1604210400).atZone(NEW_YORK)}, // 2020-11-01T06:00:00 UTC
133141
};
134142
}
135143

0 commit comments

Comments
 (0)