--- old/src/java.base/share/classes/java/time/format/DateTimeFormatter.java 2015-12-10 18:47:13.544910800 +0530 +++ new/src/java.base/share/classes/java/time/format/DateTimeFormatter.java 2015-12-10 18:47:12.504910800 +0530 @@ -80,6 +80,7 @@ import java.time.Period; import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatterBuilder.CompositePrinterParser; @@ -473,6 +474,17 @@ * day-of-week was valid for the date. *
  • If an {@linkplain #parsedExcessDays() excess number of days} * was parsed then it is added to the date if a date is available. + *
  • If a second-based field is present, but {@code LocalTime} was not parsed, + * then the resolver ensures that milli, micro and nano second values are + * available to meet the contract of {@link ChronoField}. + * These will be set to zero if missing. + *
  • If both date and time were parsed and either an offset or zone is present, + * the field {@link ChronoField#INSTANT_SECONDS} is created. + * If an offset was parsed then the offset will be combined with the + * {@code LocalDateTime} to form the instant, with any zone ignored. + * If a {@code ZoneId} was parsed without an offset then the zone will be + * combined with the {@code LocalDateTime} to form the instant using the rules + * of {@link ChronoLocalDateTime#atZone(ZoneId)}. * * * @implSpec --- old/src/java.base/share/classes/java/time/format/Parsed.java 2015-12-10 18:47:18.691910800 +0530 +++ new/src/java.base/share/classes/java/time/format/Parsed.java 2015-12-10 18:47:17.657410800 +0530 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -594,15 +594,16 @@ private void resolveInstant() { // add instant seconds if we have date, time and zone + // Offset (if present) will be given priority over the zone. if (date != null && time != null) { - if (zone != null) { - long instant = date.atTime(time).atZone(zone).getLong(ChronoField.INSTANT_SECONDS); + Long offsetSecs = fieldValues.get(OFFSET_SECONDS); + if (offsetSecs != null) { + ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue()); + long instant = date.atTime(time).atZone(offset).toEpochSecond(); fieldValues.put(INSTANT_SECONDS, instant); } else { - Long offsetSecs = fieldValues.get(OFFSET_SECONDS); - if (offsetSecs != null) { - ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue()); - long instant = date.atTime(time).atZone(offset).getLong(ChronoField.INSTANT_SECONDS); + if (zone != null) { + long instant = date.atTime(time).atZone(zone).toEpochSecond(); fieldValues.put(INSTANT_SECONDS, instant); } } --- old/test/java/time/tck/java/time/TCKZonedDateTime.java 2015-12-10 18:47:23.445910800 +0530 +++ new/test/java/time/tck/java/time/TCKZonedDateTime.java 2015-12-10 18:47:22.432410800 +0530 @@ -751,7 +751,7 @@ {"2012-06-30T12:30:40Z[GMT]", 2012, 6, 30, 12, 30, 40, 0, "GMT"}, {"2012-06-30T12:30:40Z[UT]", 2012, 6, 30, 12, 30, 40, 0, "UT"}, {"2012-06-30T12:30:40Z[UTC]", 2012, 6, 30, 12, 30, 40, 0, "UTC"}, - {"2012-06-30T12:30:40+01:00[Z]", 2012, 6, 30, 12, 30, 40, 0, "Z"}, + {"2012-06-30T12:30:40+01:00[Z]", 2012, 6, 30, 11, 30, 40, 0, "Z"}, {"2012-06-30T12:30:40+01:00[+01:00]", 2012, 6, 30, 12, 30, 40, 0, "+01:00"}, {"2012-06-30T12:30:40+01:00[GMT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "GMT+01:00"}, {"2012-06-30T12:30:40+01:00[UT+01:00]", 2012, 6, 30, 12, 30, 40, 0, "UT+01:00"}, --- /dev/null 2015-12-10 18:47:28.000000000 +0530 +++ new/test/java/time/tck/java/time/format/TCKDTFParsedInstant.java 2015-12-10 18:47:27.060910800 +0530 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package tck.java.time.format; + +import static org.testng.AssertJUnit.assertEquals; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Testing DateTimeFormatter Parsing with 4 different test conditions: + * 1. When Zone and Offset not provided + * 2. When Zone and Offset provided + * 3. When Offset is not provided and Zone is provided + * 4. When Zone is not provided and Offset is provided + */ + +@Test +public class TCKDTFParsedInstant { + private static final ZoneId EUROPE_BERLIN = ZoneId.of("Europe/Berlin"); + private static final ZoneId ASIA_ISTANBUL = ZoneId.of("Asia/Istanbul"); + private static DateTimeFormatter dtFormatter; + private static ZonedDateTime zdt1, zdt2; + private static LocalDateTime ldt1, ldt2; + private static OffsetDateTime odt1; + private static String s; + + @BeforeMethod + public void setUp() throws Exception { + dtFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME; + } + + @Test + private static void testWithoutZoneWithoutOffset() { + dtFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + ldt1 = LocalDateTime.of(2012, 10, 28, 1, 45, 0); + for (int i = 0; i < 6; i++) { + s = ldt1.format(dtFormatter); + ldt2 = LocalDateTime.parse(s, dtFormatter); + assertEquals(ldt1, ldt2); + ldt1 = ldt1.plusMinutes(15); + } + } + + @DataProvider(name="parseWithZoneWithOffset") + Object[][] data_parse_WithZone_WithOffset() { + return new Object[][] { + {"2012-10-28T01:45:00-02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("-02:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00-01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("-01:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00-00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("-00:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+00:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+01:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+02:00"), EUROPE_BERLIN}, + {"2012-10-28T01:45:00+03:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 1, 45, 0, 0), ZoneOffset.of("+03:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-02:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-01:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-00:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+00:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+01:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+02:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00+03:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+03:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00-02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-02:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00-01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-01:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00-00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-00:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+00:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+00:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+01:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+01:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+02:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+02:00"), EUROPE_BERLIN}, + {"2012-10-28T03:45:00+03:00[Europe/Berlin]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+03:00"), EUROPE_BERLIN}, + {"2012-10-28T02:45:00-02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-02:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00-01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-01:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00-00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("-00:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+00:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+01:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+02:00"), ASIA_ISTANBUL}, + {"2012-10-28T02:45:00+03:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 2, 45, 0, 0), ZoneOffset.of("+03:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00-02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-02:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00-01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-01:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00-00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("-00:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+00:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+01:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+02:00"), ASIA_ISTANBUL}, + {"2012-10-28T03:45:00+03:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 3, 45, 0, 0), ZoneOffset.of("+03:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00-02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("-02:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00-01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("-01:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00-00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("-00:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+00:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+00:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+01:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+01:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+02:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+02:00"), ASIA_ISTANBUL}, + {"2012-10-28T04:45:00+03:00[Asia/Istanbul]", + LocalDateTime.of(2012, 10, 28, 4, 45, 0, 0), ZoneOffset.of("+03:00"), ASIA_ISTANBUL} + }; + } + + @Test(dataProvider="parseWithZoneWithOffset") + private static void testWithZoneWithOffset(String zdtString, LocalDateTime ldt, ZoneOffset offset, ZoneId zone) { + dtFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME; + zdt1 = ZonedDateTime.ofInstant(ldt, offset, zone); + zdt2 = ZonedDateTime.parse(zdtString, dtFormatter); + assertEquals(zdt1, zdt2); + } + + @Test + private static void testWithZoneWithoutOffset() { + dtFormatter = DateTimeFormatter.ofPattern("d MMM HH:mm:ss uuuu VV"); + zdt1 = ZonedDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneId.of("Europe/Berlin")); + String s1; + for (int i = 0; i < 6; i++) { + s = zdt1.format(dtFormatter); + zdt2 = ZonedDateTime.parse(s, dtFormatter); + s1 = zdt2.format(dtFormatter); + assertEquals(s, s1); + zdt1 = zdt1.plusMinutes(15); + } + } + + @DataProvider(name="parseWithOffsetWithoutZone") + Object[][] data_parse_WithOffset_WithoutZone() { + return new Object[][] { + {"2012-10-28T01:45:00-08:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("-08:00"))}, + {"2012-10-28T01:45:00-05:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("-05:00"))}, + {"2012-10-28T01:45:00-01:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("-01:00"))}, + {"2012-10-28T01:45:00+00:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("+00:00"))}, + {"2012-10-28T01:45:00+01:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("+01:00"))}, + {"2012-10-28T01:45:00+04:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("+04:00"))}, + {"2012-10-28T01:45:00+10:00", OffsetDateTime.of(2012, 10, 28, 1, 45, 0, 0, ZoneOffset.of("+10:00"))}, + {"2012-10-28T02:45:00-08:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("-08:00"))}, + {"2012-10-28T02:45:00-05:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("-05:00"))}, + {"2012-10-28T02:45:00-01:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("-01:00"))}, + {"2012-10-28T02:45:00+00:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("+00:00"))}, + {"2012-10-28T02:45:00+01:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("+01:00"))}, + {"2012-10-28T02:45:00+04:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("+04:00"))}, + {"2012-10-28T02:45:00+10:00", OffsetDateTime.of(2012, 10, 28, 2, 45, 0, 0, ZoneOffset.of("+10:00"))}, + {"2012-10-28T03:45:00-08:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("-08:00"))}, + {"2012-10-28T03:45:00-05:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("-05:00"))}, + {"2012-10-28T03:45:00-01:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("-01:00"))}, + {"2012-10-28T03:45:00+00:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("+00:00"))}, + {"2012-10-28T03:45:00+01:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("+01:00"))}, + {"2012-10-28T03:45:00+04:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("+04:00"))}, + {"2012-10-28T03:45:00+10:00", OffsetDateTime.of(2012, 10, 28, 3, 45, 0, 0, ZoneOffset.of("+10:00"))} + }; + } + + @Test(dataProvider="parseWithOffsetWithoutZone") + private static void testWithOffsetWithoutZone(String odtString, OffsetDateTime expectedOTD) { + dtFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + odt1 = OffsetDateTime.parse(odtString, dtFormatter); + assertEquals(expectedOTD, odt1); + } +}