@@ -135,8 +135,8 @@ public class TimestampUtils {
135135 private final StringBuilder sbuf = new StringBuilder ();
136136
137137 // This calendar is used when user provides calendar in setX(, Calendar) method.
138- // It ensures calendar is proleptic Gregorian.
139- private final Calendar calendarWithUserTz = createProlepticGregorianCalendar ( TimeZone . getDefault () );
138+ // It ensures calendar is Gregorian.
139+ private final Calendar calendarWithUserTz = new GregorianCalendar ( );
140140
141141 private @ Nullable Calendar calCache ;
142142 private @ Nullable ZoneOffset calCacheZone ;
@@ -159,11 +159,11 @@ private Calendar getCalendar(ZoneOffset offset) {
159159 }
160160
161161 // normally we would use:
162- // calCache = createProlepticGregorianCalendar (TimeZone.getTimeZone(offset));
162+ // calCache = new GregorianCalendar (TimeZone.getTimeZone(offset));
163163 // But this seems to cause issues for some crazy offsets as returned by server for BC dates!
164164 final String tzid = offset .getTotalSeconds () == 0 ? "UTC" : "GMT" .concat (offset .getId ());
165165 final TimeZone syntheticTZ = new SimpleTimeZone (offset .getTotalSeconds () * 1000 , tzid );
166- calCache = createProlepticGregorianCalendar (syntheticTZ );
166+ calCache = new GregorianCalendar (syntheticTZ );
167167 calCacheZone = offset ;
168168 return calCache ;
169169 }
@@ -766,7 +766,7 @@ public OffsetDateTime toOffsetDateTimeBin(byte[] bytes) throws PSQLException {
766766 return new Date (PGStatement .DATE_NEGATIVE_INFINITY );
767767 }
768768 if ( cal == null ) {
769- cal = createProlepticGregorianCalendar ( TimeZone . getDefault () );
769+ cal = Calendar . getInstance ( );
770770 }
771771
772772 ParsedTimestamp pt ;
@@ -1782,17 +1782,28 @@ public String timeToString(java.util.Date time, boolean withTimeZone) {
17821782 }
17831783
17841784 /**
1785- * Converts the given postgresql seconds to java seconds. See {@link #toPgSecs}
1785+ * Converts the given postgresql seconds to java seconds. Reverse engineered by inserting varying
1786+ * dates to postgresql and tuning the formula until the java dates matched. See {@link #toPgSecs}
17861787 * for the reverse operation.
17871788 *
17881789 * @param secs Postgresql seconds.
17891790 * @return Java seconds.
17901791 */
17911792 @ SuppressWarnings ("JavaDurationGetSecondsToToSeconds" )
17921793 private static long toJavaSecs (long secs ) {
1793- // postgres epoch to java epoch
1794+ // postgres epoc to java epoc
17941795 secs += PG_EPOCH_DIFF .getSeconds ();
17951796
1797+ // Julian/Gregorian calendar cutoff point
1798+ if (secs < -12219292800L ) { // October 4, 1582 -> October 15, 1582
1799+ secs += 86400 * 10 ;
1800+ if (secs < -14825808000L ) { // 1500-02-28 -> 1500-03-01
1801+ int extraLeaps = (int ) ((secs + 14825808000L ) / 3155760000L );
1802+ extraLeaps --;
1803+ extraLeaps -= extraLeaps / 4 ;
1804+ secs += extraLeaps * 86400L ;
1805+ }
1806+ }
17961807 return secs ;
17971808 }
17981809
@@ -1805,9 +1816,20 @@ private static long toJavaSecs(long secs) {
18051816 */
18061817 @ SuppressWarnings ("JavaDurationGetSecondsToToSeconds" )
18071818 private static long toPgSecs (long secs ) {
1808- // java epoch to postgres epoch
1819+ // java epoc to postgres epoc
18091820 secs -= PG_EPOCH_DIFF .getSeconds ();
18101821
1822+ // Julian/Gregorian calendar cutoff point
1823+ if (secs < -13165977600L ) { // October 15, 1582 -> October 4, 1582
1824+ secs -= 86400 * 10 ;
1825+ if (secs < -15773356800L ) { // 1500-03-01 -> 1500-02-28
1826+ int years = (int ) ((secs + 15773356800L ) / -3155823050L );
1827+ years ++;
1828+ years -= years / 4 ;
1829+ secs += years * 86400L ;
1830+ }
1831+ }
1832+
18111833 return secs ;
18121834 }
18131835
@@ -1854,23 +1876,6 @@ public static TimeZone parseBackendTimeZone(String timeZone) {
18541876 return TimeZone .getTimeZone (timeZone );
18551877 }
18561878
1857- /**
1858- * Create a proleptic Gregorian calendar with the given time zone. This differs from a newly
1859- * created (Gregorian)Calendar instance that is typically a hybrid of the Julian and Gregorian
1860- * calendar
1861- *
1862- * @param tz the time zone to use
1863- * @return The proleptic Gregorian Calendar instance
1864- */
1865- @ SuppressWarnings ("JavaUtilDate" ) // Using new Date(long) is not problematic on its own
1866- public static Calendar createProlepticGregorianCalendar (TimeZone tz ) {
1867- GregorianCalendar prolepticGregorianCalendar = new GregorianCalendar (tz );
1868- // Make the calendar pure (proleptic) Gregorian
1869- prolepticGregorianCalendar .setGregorianChange (new java .util .Date (Long .MIN_VALUE ));
1870-
1871- return prolepticGregorianCalendar ;
1872- }
1873-
18741879 private static long floorDiv (long x , long y ) {
18751880 long r = x / y ;
18761881 // if the signs are different and modulo not zero, round down
0 commit comments