--- old/src/java.base/share/classes/java/time/chrono/Chronology.java 2016-03-01 12:54:44.164575994 +0300 +++ new/src/java.base/share/classes/java/time/chrono/Chronology.java 2016-03-01 12:54:43.888575994 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -67,6 +67,7 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatterBuilder; import java.time.format.ResolverStyle; import java.time.format.TextStyle; @@ -712,6 +713,56 @@ return new ChronoPeriodImpl(this, years, months, days); } + //--------------------------------------------------------------------- + + /** + * Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z. + *

+ * Returns the number of seconds from the epoch of 1970-01-01T00:00:00Z + * formed using given prolepticYear, month, dayOfMonth , hour, minute, second and zoneOffset. + * Subclass can override the default implementation for a more efficient implementation. + * + * @param prolepticYear the chronology proleptic-year + * @param month the chronology month-of-year + * @param dayOfMonth the chronology day-of-month + * @param hour the hour-of-day to represent, from 0 to 23 + * @param minute the minute-of-hour to represent, from 0 to 59 + * @param second the second-of-minute to represent, from 0 to 59 + * @param zoneOffset the zone offset, not null + * @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative + */ + public default long epochSecond(int prolepticYear, int month, int dayOfMonth, + int hour, int minute, int second, ZoneOffset zoneOffset) { + Objects.requireNonNull(zoneOffset, "zoneOffset"); + long daysInSec = Math.multiplyExact(date(prolepticYear, month, dayOfMonth).toEpochDay(), 86400); + long timeinSec = (hour * 60 + minute) * 60 + second; + return Math.addExact(daysInSec, timeinSec - zoneOffset.getTotalSeconds()); + } + + /** + * Gets the number of seconds from the epoch of 1970-01-01T00:00:00Z. + *

+ * The number of seconds is caluculated using given era, prolepticYear, + * month, dayOfMonth , hour, minute, second and zoneOffset. + * + * @param era the era of the correct type for the chronology, not null + * @param yearofEra the chronology year-of-era + * @param month the chronology month-of-year + * @param dayOfMonth the chronology day-of-month + * @param hour the hour-of-day to represent, from 0 to 23 + * @param minute the minute-of-hour to represent, from 0 to 59 + * @param second the second-of-minute to represent, from 0 to 59 + * @param zoneOffset the zone offset, not null + * @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative + */ + public default long epochSecond(Era era, int yearofEra, int month, int dayOfMonth, + int hour, int minute, int second, ZoneOffset zoneOffset) { + Objects.requireNonNull(era, "era"); + Objects.requireNonNull(zoneOffset, "zoneOffset"); + long daysInSec = Math.multiplyExact(date(era, yearofEra, month, dayOfMonth).toEpochDay(), 86400); + long timeinSec = (hour * 60 + minute) * 60 + second; + return Math.addExact(daysInSec, timeinSec - zoneOffset.getTotalSeconds()); + } //----------------------------------------------------------------------- /** * Compares this chronology to another chronology. --- old/src/java.base/share/classes/java/time/chrono/IsoChronology.java 2016-03-01 12:54:44.889575994 +0300 +++ new/src/java.base/share/classes/java/time/chrono/IsoChronology.java 2016-03-01 12:54:44.624575994 +0300 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, 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 @@ -61,14 +61,17 @@ */ package java.time.chrono; -import java.io.InvalidObjectException; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.PROLEPTIC_MONTH; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.time.Clock; @@ -79,8 +82,9 @@ import java.time.Month; import java.time.Period; import java.time.Year; -import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; @@ -263,6 +267,119 @@ return LocalDate.from(temporal); } + //----------------------------------------------------------------------- + /** + * Gets the number of seconds from the epoch of 1970-01-01T00:00:00Z + * formed using the specified year, month, day, hour, minute, second and zoneOffset. + * + * @param prolepticYear the year to represent, from MIN_YEAR to MAX_YEAR + * @param month the month-of-year to represent, from 1 to 12 + * @param dayOfMonth the day-of-month to represent, from 1 to 31 + * @param hour the hour-of-day to represent, from 0 to 23 + * @param minute the minute-of-hour to represent, from 0 to 59 + * @param second the second-of-minute to represent, from 0 to 59 + * @param zoneOffset the zone offset, not null + * @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative + * @throws DateTimeException if the value of any field is out of range, + * or if the day-of-month is invalid for the month-year + */ + @Override + public long epochSecond(int prolepticYear, int month, int dayOfMonth, + int hour, int minute, int second, ZoneOffset zoneOffset) { + YEAR.checkValidValue(prolepticYear); + MONTH_OF_YEAR.checkValidValue(month); + DAY_OF_MONTH.checkValidValue(dayOfMonth); + HOUR_OF_DAY.checkValidValue(hour); + MINUTE_OF_HOUR.checkValidValue(minute); + SECOND_OF_MINUTE.checkValidValue(second); + Objects.requireNonNull(zoneOffset, "zoneOffset"); + if (dayOfMonth > 28) { + int dom = numberOfDaysOfMonth(prolepticYear, month); + if (dayOfMonth > dom) { + if (dayOfMonth == 29) { + throw new DateTimeException("Invalid date 'February 29' as '" + prolepticYear + "' is not a leap year"); + } else { + throw new DateTimeException("Invalid date '" + Month.of(month).name() + " " + dayOfMonth + "'"); + } + } + } + + long totalDays = 0; + int timeinSec = 0; + totalDays += 365L * prolepticYear; + long DAYS_0000_TO_1970 = (146097 * 5L) - (30L * 365L + 7L); // taken from LocalDate + if (prolepticYear >= 0) { + totalDays += (prolepticYear + 3L) / 4 - (prolepticYear + 99L) / 100 + (prolepticYear + 399L) / 400; + } else { + totalDays -= prolepticYear / -4 - prolepticYear / -100 + prolepticYear / -400; + } + totalDays += (367 * month - 362) / 12; + totalDays += dayOfMonth - 1; + if (month > 2) { + totalDays--; + if (IsoChronology.INSTANCE.isLeapYear(prolepticYear) == false) { + totalDays--; + } + } + totalDays -= DAYS_0000_TO_1970; + timeinSec = (hour * 60 + minute ) * 60 + second; + return Math.addExact(Math.multiplyExact(totalDays, 86400L), timeinSec - zoneOffset.getTotalSeconds()); + } + + /** + * Gets the number of days for the given month in the given year. + * + * @param year the year to represent, from MIN_YEAR to MAX_YEAR + * @param month the month-of-year to represent, from 1 to 12 + * @return the number of days for the given month in the given year + */ + private int numberOfDaysOfMonth(int year, int month) { + int dom; + switch (month) { + case 2: + dom = (IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28); + break; + case 4: + case 6: + case 9: + case 11: + dom = 30; + break; + default: + dom = 31; + break; + } + return dom; + } + + //----------------------------------------------------------------------- + /** + * Gets the number of seconds from the epoch of 1970-01-01T00:00:00Z + * formed using the specified era, yearOfEra, month, day, hour, minute, second and zoneOffset. + * + * @param era the ISO era, not null + * @param yearOfEra the ISO year-of-era + * @param month the ISO month-of-year + * @param dayOfMonth the ISO day-of-month + * @param hour the hour-of-day to represent, from 0 to 23 + * @param minute the minute-of-hour to represent, from 0 to 59 + * @param second the second-of-minute to represent, from 0 to 59 + * @param zoneOffset the zone offset, not null + * @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative + * @throws DateTimeException if the value of any field is out of range, + * or if the day-of-month is invalid for the month-year + */ + @Override + public long epochSecond(Era era, + int yearOfEra, int month, int dayOfMonth, + int hour, int minute, int second, ZoneOffset zoneOffset) { + Objects.requireNonNull(era, "era"); + if (era.getValue() == 0) { + yearOfEra = 1 - yearOfEra; + } + return epochSecond(yearOfEra, month, dayOfMonth, hour, minute, second, zoneOffset); + } + /** * Obtains an ISO local date-time from another date-time object. *

--- old/test/java/time/tck/java/time/chrono/TCKChronology.java 2016-03-01 12:54:45.653575995 +0300 +++ new/test/java/time/tck/java/time/chrono/TCKChronology.java 2016-03-01 12:54:45.353575995 +0300 @@ -68,11 +68,16 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.time.ZoneId; import java.time.Clock; import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; +import java.time.chrono.Era; import java.time.chrono.HijrahChronology; import java.time.chrono.HijrahEra; import java.time.chrono.IsoChronology; @@ -97,6 +102,14 @@ @Test public class TCKChronology { + private static final ZoneOffset OFFSET_P0100 = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_M0100 = ZoneOffset.ofHours(-1); + + private static final int YDIFF_MEIJI = 1867; + private static final int YDIFF_SHOWA = 1925; + private static final int YDIFF_HEISEI = 1988; + private static final int YDIFF_MINGUO = 1911; + private static final int YDIFF_THAIBUDDHIST = 543; //----------------------------------------------------------------------- // regular data factory for ID and calendarType of available calendars //----------------------------------------------------------------------- @@ -337,4 +350,60 @@ Chronology chrono = Chronology.of("FooFoo"); } + @DataProvider(name = "epochSecond_dataProvider") + Object[][] data_epochSecond() { + return new Object[][] { + {JapaneseChronology.INSTANCE, 1873, 9, 7, 1, 2, 2, OFFSET_P0100}, + {JapaneseChronology.INSTANCE, 1928, 2, 28, 1, 2, 2, OFFSET_M0100}, + {JapaneseChronology.INSTANCE, 1989, 1, 8, 1, 2, 2, OFFSET_P0100}, + {HijrahChronology.INSTANCE, 1434, 9, 7, 1, 2, 2, OFFSET_P0100}, + {MinguoChronology.INSTANCE, 1873, 9, 7, 1, 2, 2, OFFSET_P0100}, + {MinguoChronology.INSTANCE, 1928, 2, 28, 1, 2, 2, OFFSET_M0100}, + {MinguoChronology.INSTANCE, 1989, 1, 8, 1, 2, 2, OFFSET_P0100}, + {ThaiBuddhistChronology.INSTANCE, 1873, 9, 7, 1, 2, 2, OFFSET_P0100}, + {ThaiBuddhistChronology.INSTANCE, 1928, 2, 28, 1, 2, 2, OFFSET_M0100}, + {ThaiBuddhistChronology.INSTANCE, 1989, 1, 8, 1, 2, 2, OFFSET_P0100}, + {IsoChronology.INSTANCE, 1873, 9, 7, 1, 2, 2, OFFSET_P0100}, + {IsoChronology.INSTANCE, 1928, 2, 28, 1, 2, 2, OFFSET_M0100}, + {IsoChronology.INSTANCE, 1989, 1, 8, 1, 2, 2, OFFSET_P0100}, + + }; + } + + @Test(dataProvider = "epochSecond_dataProvider") + public void test_epochSecond(Chronology chrono, int y, int m, int d, int h, int min, int s, ZoneOffset offset) { + ChronoLocalDate chronoLd = chrono.date(y, m, d); + assertEquals(chrono.epochSecond(y, m, d, h, min, s, offset), + OffsetDateTime.of(LocalDate.MIN.with(chronoLd), LocalTime.of(h, min, s), offset) + .toEpochSecond()); + } + + @DataProvider(name = "era_epochSecond_dataProvider") + Object[][] data_era_epochSecond() { + return new Object[][] { + {JapaneseChronology.INSTANCE, JapaneseEra.MEIJI, 1873 - YDIFF_MEIJI, 9, 7, 1, 2, 2, OFFSET_P0100}, + {JapaneseChronology.INSTANCE, JapaneseEra.SHOWA, 1928 - YDIFF_SHOWA, 2, 28, 1, 2, 2, OFFSET_M0100}, + {JapaneseChronology.INSTANCE, JapaneseEra.HEISEI, 1989 - YDIFF_HEISEI, 1, 8, 1, 2, 2, OFFSET_P0100}, + {HijrahChronology.INSTANCE, HijrahEra.AH, 1434, 9, 7, 1, 2, 2, OFFSET_P0100}, + {MinguoChronology.INSTANCE, MinguoEra.BEFORE_ROC, 1873 - YDIFF_MINGUO, 9, 7, 1, 2, 2, OFFSET_P0100}, + {MinguoChronology.INSTANCE, MinguoEra.ROC, 1928 - YDIFF_MINGUO, 2, 28, 1, 2, 2, OFFSET_M0100}, + {MinguoChronology.INSTANCE, MinguoEra.ROC, 1989 - YDIFF_MINGUO, 1, 8, 1, 2, 2, OFFSET_P0100}, + {ThaiBuddhistChronology.INSTANCE, ThaiBuddhistEra.BE, 1873 + YDIFF_THAIBUDDHIST, 9, 7, 1, 2, 2, OFFSET_P0100}, + {ThaiBuddhistChronology.INSTANCE, ThaiBuddhistEra.BE, 1928 + YDIFF_THAIBUDDHIST, 2, 28, 1, 2, 2, OFFSET_M0100}, + {ThaiBuddhistChronology.INSTANCE, ThaiBuddhistEra.BE, 1989 + YDIFF_THAIBUDDHIST, 1, 8, 1, 2, 2, OFFSET_P0100}, + {IsoChronology.INSTANCE, IsoEra.CE, 1873, 9, 7, 1, 2, 2, OFFSET_P0100}, + {IsoChronology.INSTANCE, IsoEra.CE, 1928, 2, 28, 1, 2, 2, OFFSET_M0100}, + {IsoChronology.INSTANCE, IsoEra.CE, 1989, 1, 8, 1, 2, 2, OFFSET_P0100}, + + }; + } + + @Test(dataProvider = "era_epochSecond_dataProvider") + public void test_epochSecond(Chronology chrono, Era era, int y, int m, int d, int h, int min, int s, ZoneOffset offset) { + ChronoLocalDate chronoLd = chrono.date(era, y, m, d); + assertEquals(chrono.epochSecond(era, y, m, d, h, min, s, offset), + OffsetDateTime.of(LocalDate.MIN.with(chronoLd), LocalTime.of(h, min, s), offset) + .toEpochSecond()); + } + } --- old/test/java/time/tck/java/time/chrono/TCKIsoChronology.java 2016-03-01 12:54:46.377575994 +0300 +++ new/test/java/time/tck/java/time/chrono/TCKIsoChronology.java 2016-03-01 12:54:46.083575994 +0300 @@ -63,10 +63,16 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.ZoneId; +import java.time.OffsetDateTime; +import java.time.Year; import java.time.ZonedDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.chrono.Chronology; +import java.time.chrono.Era; +import java.time.chrono.HijrahEra; import java.time.chrono.IsoChronology; +import java.time.chrono.IsoEra; import java.time.format.ResolverStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; @@ -87,6 +93,8 @@ public class TCKIsoChronology { // Can only work with IsoChronology here // others may be in separate module + private static final ZoneOffset OFFSET_P0100 = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_M0100 = ZoneOffset.ofHours(-1); @Test public void factory_from_TemporalAccessor_dateWithChronlogy() { @@ -675,9 +683,104 @@ } } + @DataProvider(name = "epochSecond_dataProvider") + Object[][] data_epochSecond() { + return new Object[][] { + {2008, 3, 3, 1, 2, 2, OFFSET_P0100}, + {2008, 3, 3, 1, 2, 2, OFFSET_M0100}, + {2008, 2, 28, 1, 2, 2, OFFSET_P0100}, + {2009, 3, 3, 1, 2, 2, OFFSET_P0100}, + {2009, 3, 3, 1, 2, 2, OFFSET_M0100}, + {2009, 2, 28, 1, 2, 2, OFFSET_P0100}, + {1968, 3, 3, 1, 2, 2, OFFSET_P0100}, + {1968, 3, 3, 1, 2, 2, OFFSET_M0100}, + {1968, 2, 28, 1, 2, 2, OFFSET_P0100}, + {1969, 3, 3 , 1, 2, 2, OFFSET_P0100}, + {1969, 3, 3, 1, 2, 2, OFFSET_M0100}, + {1969, 2, 28, 1, 2, 2, OFFSET_P0100}, + {1970, 1, 1, 1, 2, 2, OFFSET_P0100}, + {1970, 1, 1, 1, 2, 2, OFFSET_M0100}, + {-4, 3, 3 , 1, 2, 2, OFFSET_P0100}, + {-1, 3, 3 , 1, 2, 2, OFFSET_P0100}, + }; + } + + @Test(dataProvider = "epochSecond_dataProvider") + public void test_epochSecond_1(int y, int m, int d, int h , int min, int s, ZoneOffset offset) { + assertEquals(IsoChronology.INSTANCE.epochSecond(y, m, d, h, min, s, offset), + OffsetDateTime.of(y, m, d, h, min, s, 0, offset).toEpochSecond()); + } + + @Test + public void test_epochSecond_2() { + assertEquals(IsoChronology.INSTANCE.epochSecond(2008, 3, 3, 1, 2, 2, OFFSET_P0100), + ZonedDateTime.of(2008, 3, 3, 1, 2, 2, 0, ZoneId.of("+01:00")).toEpochSecond()); + assertEquals(IsoChronology.INSTANCE.epochSecond(1969, 3, 3, 1, 2, 2, OFFSET_P0100), + ZonedDateTime.of(1969, 3, 3, 1, 2, 2, 0, ZoneId.of("+01:00")).toEpochSecond()); + } + + @Test + public void test_epochSecond_max() { + assertEquals(IsoChronology.INSTANCE.epochSecond(Year.MAX_VALUE, 12, 31, 23, 59, 59, ZoneOffset.MIN), + OffsetDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 0, ZoneOffset.MIN).toEpochSecond()); + } + + @Test + public void test_epochSecond_min() { + assertEquals(IsoChronology.INSTANCE.epochSecond(Year.MIN_VALUE, 1, 1, 0, 0, 0, ZoneOffset.MAX), + OffsetDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0, 0, 0, ZoneOffset.MAX).toEpochSecond()); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_epochSecond_bad() { + IsoChronology.INSTANCE.epochSecond(2009, 2, 29, 1, 2, 2, OFFSET_P0100); + } + + @DataProvider(name = "era_epochSecond_dataProvider") + Object[][] data_era_epochSecond() { + return new Object[][] { + {IsoEra.CE, 2008, 3, 3, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 2008, 3, 3, 1, 2, 2, OFFSET_M0100}, + {IsoEra.CE, 2008, 2, 28, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 2009, 3, 3, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 2009, 3, 3, 1, 2, 2, OFFSET_M0100}, + {IsoEra.CE, 2009, 2, 28, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 1968, 3, 3, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 1968, 3, 3, 1, 2, 2, OFFSET_M0100}, + {IsoEra.CE, 1968, 2, 28, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 1969, 3, 3 , 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 1969, 3, 3, 1, 2, 2, OFFSET_M0100}, + {IsoEra.CE, 1969, 2, 28, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 1970, 1, 1, 1, 2, 2, OFFSET_P0100}, + {IsoEra.CE, 1970, 1, 1, 1, 2, 2, OFFSET_M0100}, + {IsoEra.BCE, 5, 3, 3 , 1, 2, 2, OFFSET_P0100}, + {IsoEra.BCE, 2, 3, 3 , 1, 2, 2, OFFSET_P0100}, + }; + } + + @Test(dataProvider = "era_epochSecond_dataProvider") + public void test_era_epochSecond_1(Era era, int y, int m, int d, int h , int min, int s, ZoneOffset offset) { + assertEquals(IsoChronology.INSTANCE.epochSecond(era, y, m, d, h, min, s, offset), + OffsetDateTime.of(IsoChronology.INSTANCE.date(era, y, m, d), LocalTime.of(h, min, s), offset) + .toEpochSecond()); + } + + @Test + public void test_era_epochSecond_2() { + assertEquals(IsoChronology.INSTANCE.epochSecond(IsoEra.CE, 2008, 3, 3, 1, 2, 2, OFFSET_P0100), + ZonedDateTime.of(2008, 3, 3, 1, 2, 2, 0, ZoneId.of("+01:00")).toEpochSecond()); + assertEquals(IsoChronology.INSTANCE.epochSecond(IsoEra.CE, 1969, 3, 3, 1, 2, 2, OFFSET_P0100), + ZonedDateTime.of(1969, 3, 3, 1, 2, 2, 0, ZoneId.of("+01:00")).toEpochSecond()); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_era_epochSecond_bad() { + IsoChronology.INSTANCE.epochSecond(HijrahEra.AH, 2009, 2, 29, 1, 2, 2, OFFSET_P0100); + } + + //----------------------------------------------------------------------- private static LocalDate date(int y, int m, int d) { return LocalDate.of(y, m, d); } - }