mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8166138: DateTimeFormatter.ISO_INSTANT should handle offsets
Reviewed-by: rriggs, scolebourne, naoto
This commit is contained in:
parent
ea3fb3979c
commit
f5d19b9109
3 changed files with 79 additions and 24 deletions
|
@ -1138,9 +1138,12 @@ public final class DateTimeFormatter {
|
||||||
* <p>
|
* <p>
|
||||||
* This returns an immutable formatter capable of formatting and parsing
|
* This returns an immutable formatter capable of formatting and parsing
|
||||||
* the ISO-8601 instant format.
|
* the ISO-8601 instant format.
|
||||||
* When formatting, the second-of-minute is always output.
|
* When formatting, the instant will always be suffixed by 'Z' to indicate UTC.
|
||||||
|
* The second-of-minute is always output.
|
||||||
* The nano-of-second outputs zero, three, six or nine digits as necessary.
|
* The nano-of-second outputs zero, three, six or nine digits as necessary.
|
||||||
* When parsing, time to at least the seconds field is required.
|
* When parsing, the behaviour of {@link DateTimeFormatterBuilder#appendOffsetId()}
|
||||||
|
* will be used to parse the offset, converting the instant to UTC as necessary.
|
||||||
|
* The time to at least the seconds field is required.
|
||||||
* Fractional seconds from zero to nine are parsed.
|
* Fractional seconds from zero to nine are parsed.
|
||||||
* The localized decimal style is not used.
|
* The localized decimal style is not used.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -837,6 +837,10 @@ public final class DateTimeFormatterBuilder {
|
||||||
* The leap-second time of '23:59:59' is handled to some degree, see
|
* The leap-second time of '23:59:59' is handled to some degree, see
|
||||||
* {@link DateTimeFormatter#parsedLeapSecond()} for full details.
|
* {@link DateTimeFormatter#parsedLeapSecond()} for full details.
|
||||||
* <p>
|
* <p>
|
||||||
|
* When formatting, the instant will always be suffixed by 'Z' to indicate UTC.
|
||||||
|
* When parsing, the behaviour of {@link DateTimeFormatterBuilder#appendOffsetId()}
|
||||||
|
* will be used to parse the offset, converting the instant to UTC as necessary.
|
||||||
|
* <p>
|
||||||
* An alternative to this method is to format/parse the instant as a single
|
* An alternative to this method is to format/parse the instant as a single
|
||||||
* epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
|
* epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
|
||||||
*
|
*
|
||||||
|
@ -3468,7 +3472,7 @@ public final class DateTimeFormatterBuilder {
|
||||||
.appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':')
|
.appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':')
|
||||||
.appendValue(SECOND_OF_MINUTE, 2)
|
.appendValue(SECOND_OF_MINUTE, 2)
|
||||||
.appendFraction(NANO_OF_SECOND, minDigits, maxDigits, true)
|
.appendFraction(NANO_OF_SECOND, minDigits, maxDigits, true)
|
||||||
.appendLiteral('Z')
|
.appendOffsetId()
|
||||||
.toFormatter().toPrinterParser(false);
|
.toFormatter().toPrinterParser(false);
|
||||||
DateTimeParseContext newContext = context.copy();
|
DateTimeParseContext newContext = context.copy();
|
||||||
int pos = parser.parse(newContext, text, position);
|
int pos = parser.parse(newContext, text, position);
|
||||||
|
@ -3486,6 +3490,7 @@ public final class DateTimeFormatterBuilder {
|
||||||
Long nanoVal = newContext.getParsed(NANO_OF_SECOND);
|
Long nanoVal = newContext.getParsed(NANO_OF_SECOND);
|
||||||
int sec = (secVal != null ? secVal.intValue() : 0);
|
int sec = (secVal != null ? secVal.intValue() : 0);
|
||||||
int nano = (nanoVal != null ? nanoVal.intValue() : 0);
|
int nano = (nanoVal != null ? nanoVal.intValue() : 0);
|
||||||
|
int offset = newContext.getParsed(OFFSET_SECONDS).intValue();
|
||||||
int days = 0;
|
int days = 0;
|
||||||
if (hour == 24 && min == 0 && sec == 0 && nano == 0) {
|
if (hour == 24 && min == 0 && sec == 0 && nano == 0) {
|
||||||
hour = 0;
|
hour = 0;
|
||||||
|
@ -3498,7 +3503,7 @@ public final class DateTimeFormatterBuilder {
|
||||||
long instantSecs;
|
long instantSecs;
|
||||||
try {
|
try {
|
||||||
LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days);
|
LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days);
|
||||||
instantSecs = ldt.toEpochSecond(ZoneOffset.UTC);
|
instantSecs = ldt.toEpochSecond(ZoneOffset.ofTotalSeconds(offset));
|
||||||
instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
|
instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
return ~position;
|
return ~position;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -69,12 +69,18 @@ import java.time.Period;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.format.ResolverStyle;
|
import java.time.format.ResolverStyle;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
|
|
||||||
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8166138
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test DateTimeFormatterBuilder.appendInstant().
|
* Test DateTimeFormatterBuilder.appendInstant().
|
||||||
*/
|
*/
|
||||||
|
@ -200,7 +206,10 @@ public class TCKInstantPrinterParser {
|
||||||
{0, 0, "1970-01-01T00:00:00Z"},
|
{0, 0, "1970-01-01T00:00:00Z"},
|
||||||
{0, 0, "1970-01-01T00:00:00.0Z"},
|
{0, 0, "1970-01-01T00:00:00.0Z"},
|
||||||
{0, 0, "1970-01-01T00:00:00.000Z"},
|
{0, 0, "1970-01-01T00:00:00.000Z"},
|
||||||
{0, 0, "1970-01-01T00:00:00.000000000Z"},
|
|
||||||
|
{0, 0, "1970-01-01T00:00:00+00:00"},
|
||||||
|
{0, 0, "1970-01-01T05:30:00+05:30"},
|
||||||
|
{0, 0, "1970-01-01T01:00:00.0+01:00"},
|
||||||
|
|
||||||
{-1, 0, "1969-12-31T23:59:59Z"},
|
{-1, 0, "1969-12-31T23:59:59Z"},
|
||||||
{1, 0, "1970-01-01T00:00:01Z"},
|
{1, 0, "1970-01-01T00:00:01Z"},
|
||||||
|
@ -208,16 +217,22 @@ public class TCKInstantPrinterParser {
|
||||||
{3600, 0, "1970-01-01T01:00:00Z"},
|
{3600, 0, "1970-01-01T01:00:00Z"},
|
||||||
{86400, 0, "1970-01-02T00:00:00Z"},
|
{86400, 0, "1970-01-02T00:00:00Z"},
|
||||||
|
|
||||||
|
{-1, 0, "1969-12-31T23:59:59+00:00"},
|
||||||
|
{1, 0, "1970-01-01T05:30:01+05:30"},
|
||||||
|
{60, 0, "1969-12-31T19:01:00-05:00"},
|
||||||
|
{3600, 0, "1970-01-01T06:30:00+05:30"},
|
||||||
|
{86400, 0, "1970-01-01T19:00:00-05:00"},
|
||||||
|
|
||||||
{182, 234000000, "1970-01-01T00:03:02.234Z"},
|
{182, 234000000, "1970-01-01T00:03:02.234Z"},
|
||||||
{182, 234000000, "1970-01-01T00:03:02.2340Z"},
|
{182, 234000000, "1970-01-01T00:03:02.2340Z"},
|
||||||
{182, 234000000, "1970-01-01T00:03:02.23400Z"},
|
{182, 234000000, "1970-01-01T00:03:02.23400Z"},
|
||||||
{182, 234000000, "1970-01-01T00:03:02.234000Z"},
|
{182, 234000000, "1970-01-01T00:03:02.234000Z"},
|
||||||
{182, 234000000, "1970-01-01T00:03:02.234000000Z"},
|
|
||||||
|
|
||||||
{((23 * 60) + 59) * 60 + 59, 123456789, "1970-01-01T23:59:59.123456789Z"},
|
{182, 234000000, "1970-01-01T00:03:02.234+00:00"},
|
||||||
|
{182, 234000000, "1970-01-01T05:33:02.2340+05:30"},
|
||||||
|
{182, 234000000, "1969-12-31T19:03:02.23400-05:00"},
|
||||||
|
{182, 234000000, "1970-01-01T00:03:02.234000+00:00"},
|
||||||
|
|
||||||
{Instant.MAX.getEpochSecond(), 999999999, "+1000000000-12-31T23:59:59.999999999Z"},
|
|
||||||
{Instant.MIN.getEpochSecond(), 0, "-1000000000-01-01T00:00:00.000000000Z"},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,22 +245,46 @@ public class TCKInstantPrinterParser {
|
||||||
assertEquals(f.parse(input).query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE);
|
assertEquals(f.parse(input).query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(dataProvider="parseDigits")
|
@DataProvider(name="parseNineDigits")
|
||||||
|
Object[][] data_parse_ninedigits() {
|
||||||
|
return new Object[][] {
|
||||||
|
{0, 0, "1970-01-01T00:00:00.000000000Z"},
|
||||||
|
{0, 0, "1970-01-01T05:30:00.000000000+05:30"},
|
||||||
|
|
||||||
|
{182, 234000000, "1970-01-01T00:03:02.234000000Z"},
|
||||||
|
{182, 234000000, "1970-01-01T01:03:02.234000000+01:00"},
|
||||||
|
|
||||||
|
{((23 * 60) + 59) * 60 + 59, 123456789, "1970-01-01T23:59:59.123456789Z"},
|
||||||
|
{((23 * 60) + 59) * 60 + 59, 123456789, "1970-01-02T05:29:59.123456789+05:30"},
|
||||||
|
|
||||||
|
{Instant.MAX.getEpochSecond(), 999999999, "+1000000000-12-31T23:59:59.999999999Z"},
|
||||||
|
{Instant.MIN.getEpochSecond(), 0, "-1000000000-01-01T00:00:00.000000000Z"},
|
||||||
|
{Instant.MAX.getEpochSecond(), 999999999, "+1000000000-12-31T23:59:59.999999999+00:00"},
|
||||||
|
{Instant.MIN.getEpochSecond(), 0, "-1000000000-01-01T00:00:00.000000000+00:00"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider="parseNineDigits")
|
||||||
public void test_parse_digitsNine(long instantSecs, int nano, String input) {
|
public void test_parse_digitsNine(long instantSecs, int nano, String input) {
|
||||||
DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(9).toFormatter();
|
DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(9).toFormatter();
|
||||||
if (input.charAt(input.length() - 11) == '.') {
|
Instant expected = Instant.ofEpochSecond(instantSecs, nano);
|
||||||
Instant expected = Instant.ofEpochSecond(instantSecs, nano);
|
assertEquals(f.parse(input, Instant::from), expected);
|
||||||
assertEquals(f.parse(input, Instant::from), expected);
|
assertEquals(f.parse(input).query(DateTimeFormatter.parsedExcessDays()), Period.ZERO);
|
||||||
assertEquals(f.parse(input).query(DateTimeFormatter.parsedExcessDays()), Period.ZERO);
|
assertEquals(f.parse(input).query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE);
|
||||||
assertEquals(f.parse(input).query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE);
|
}
|
||||||
} else {
|
|
||||||
try {
|
@DataProvider(name="parseMaxMinInstant")
|
||||||
f.parse(input, Instant::from);
|
Object[][] data_parse_MaxMinInstant() {
|
||||||
fail();
|
return new Object[][] {
|
||||||
} catch (DateTimeException ex) {
|
{"+1000000000-12-31T23:59:59.999999999-01:00"},
|
||||||
// expected
|
{"-1000000000-01-01T00:00:00.000000000+01:00"}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(dataProvider="parseMaxMinInstant", expectedExceptions=DateTimeParseException.class)
|
||||||
|
public void test_invalid_Instant(String input) {
|
||||||
|
DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(-1).toFormatter();
|
||||||
|
f.parse(input, Instant::from);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -283,4 +322,12 @@ public class TCKInstantPrinterParser {
|
||||||
new DateTimeFormatterBuilder().appendInstant(10);
|
new DateTimeFormatterBuilder().appendInstant(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
@Test
|
||||||
|
public void test_equality() {
|
||||||
|
Instant instant1 = Instant.parse("2018-09-12T22:15:51+05:30");
|
||||||
|
Instant instant2 = Instant.parse("2018-09-12T16:45:51Z");
|
||||||
|
assertEquals(instant2, instant1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue