Skip to content

Commit d4cf420

Browse files
authored
Revert "fix: make all Calendar instances proleptic Gregorian (#3837) (#3887)" (#3932)
This reverts commit 3609468.
1 parent 6ea732c commit d4cf420

18 files changed

Lines changed: 124 additions & 379 deletions

File tree

benchmarks/src/jmh/java/org/postgresql/benchmark/statement/BindTimestamp.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
package org.postgresql.benchmark.statement;
77

8-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
9-
108
import org.postgresql.benchmark.profilers.FlightRecorderProfiler;
119
import org.postgresql.test.TestUtil;
1210

@@ -47,7 +45,7 @@ public class BindTimestamp {
4745
private Connection connection;
4846
private PreparedStatement ps;
4947
private Timestamp ts = new Timestamp(System.currentTimeMillis());
50-
private Calendar cal = createProlepticGregorianCalendar(TimeZone.getTimeZone("UTC"));
48+
private Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
5149

5250
@Setup(Level.Trial)
5351
public void setUp() throws SQLException {
@@ -83,5 +81,4 @@ public static void main(String[] args) throws RunnerException {
8381

8482
new Runner(opt).run();
8583
}
86-
8784
}

benchmarks/src/jmh/java/org/postgresql/benchmark/time/TimestampToDate.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
package org.postgresql.benchmark.time;
77

8-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
9-
108
import org.openjdk.jmh.annotations.Benchmark;
119
import org.openjdk.jmh.annotations.BenchmarkMode;
1210
import org.openjdk.jmh.annotations.Fork;
@@ -25,6 +23,7 @@
2523

2624
import java.sql.Timestamp;
2725
import java.util.Calendar;
26+
import java.util.GregorianCalendar;
2827
import java.util.TimeZone;
2928
import java.util.concurrent.TimeUnit;
3029

@@ -42,7 +41,7 @@ public class TimestampToDate {
4241
TimeZone timeZone;
4342

4443
Timestamp ts = new Timestamp(System.currentTimeMillis());
45-
Calendar cachedCalendar = createProlepticGregorianCalendar(TimeZone.getDefault());
44+
Calendar cachedCalendar = new GregorianCalendar();
4645

4746
@Setup
4847
public void init() {

benchmarks/src/jmh/java/org/postgresql/benchmark/time/TimestampToTime.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
package org.postgresql.benchmark.time;
77

8-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
9-
108
import org.openjdk.jmh.annotations.Benchmark;
119
import org.openjdk.jmh.annotations.BenchmarkMode;
1210
import org.openjdk.jmh.annotations.Fork;
@@ -43,7 +41,7 @@ public class TimestampToTime {
4341
TimeZone timeZone;
4442

4543
Timestamp ts = new Timestamp(System.currentTimeMillis());
46-
Calendar cachedCalendar = createProlepticGregorianCalendar(TimeZone.getDefault());
44+
Calendar cachedCalendar = new GregorianCalendar();
4745

4846
@Setup
4947
public void init() {

pgjdbc/src/main/java/org/postgresql/PGStatement.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ public interface PGStatement {
2020
// -infinity / infinity representation in Java
2121
long DATE_POSITIVE_INFINITY = 9223372036825200000L;
2222
long DATE_NEGATIVE_INFINITY = -9223372036832400000L;
23-
// Days (2^31) in ms that can be stored minus the difference between the postgres and java epoch
24-
long DATE_POSITIVE_SMALLER_INFINITY = 185541640502400000L;
25-
long DATE_NEGATIVE_SMALLER_INFINITY = -185541640502400000L;
23+
long DATE_POSITIVE_SMALLER_INFINITY = 185543533774800000L;
24+
long DATE_NEGATIVE_SMALLER_INFINITY = -185543533774800000L;
2625

2726
/**
2827
* Returns the Last inserted/updated oid.

pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
package org.postgresql.jdbc;
77

8-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
98
import static org.postgresql.util.internal.Nullness.castNonNull;
109

1110
import org.postgresql.Driver;
@@ -3977,7 +3976,7 @@ public void updateArray(String columnName, @Nullable Array x) throws SQLExceptio
39773976
if (timestampValue == null) {
39783977
return null;
39793978
}
3980-
Calendar calendar = createProlepticGregorianCalendar(getDefaultCalendar().getTimeZone());
3979+
Calendar calendar = Calendar.getInstance(getDefaultCalendar().getTimeZone());
39813980
calendar.setTimeInMillis(timestampValue.getTime());
39823981
return type.cast(calendar);
39833982
} else {

pgjdbc/src/main/java/org/postgresql/jdbc/TimestampUtils.java

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

pgjdbc/src/main/java/org/postgresql/util/PGInterval.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
package org.postgresql.util;
77

8-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
9-
108
import org.checkerframework.checker.nullness.qual.Nullable;
119

1210
import java.io.Serializable;
@@ -15,7 +13,6 @@
1513
import java.util.Date;
1614
import java.util.Locale;
1715
import java.util.StringTokenizer;
18-
import java.util.TimeZone;
1916

2017
/**
2118
* This implements a class that handles the PostgreSQL interval type.
@@ -471,7 +468,7 @@ public void add(Date date) {
471468
if (isNull) {
472469
return;
473470
}
474-
final Calendar cal = createProlepticGregorianCalendar(TimeZone.getDefault());
471+
final Calendar cal = Calendar.getInstance();
475472
cal.setTime(date);
476473
add(cal);
477474
date.setTime(cal.getTime().getTime());

pgjdbc/src/test/java/org/postgresql/test/jdbc2/DateTest.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import static org.junit.jupiter.api.Assertions.assertNotNull;
1111
import static org.junit.jupiter.api.Assertions.assertTrue;
1212
import static org.junit.jupiter.api.Assumptions.assumeTrue;
13-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
1413

1514
import org.postgresql.test.TestUtil;
1615

@@ -24,7 +23,6 @@
2423
import java.sql.Statement;
2524
import java.util.ArrayList;
2625
import java.util.Arrays;
27-
import java.util.Calendar;
2826
import java.util.List;
2927
import java.util.Locale;
3028
import java.util.Objects;
@@ -320,13 +318,7 @@ private void dateTest() throws SQLException {
320318
st.close();
321319
}
322320

323-
private static java.sql.Date makeDate(int year, int month, int day) {
324-
Calendar cal = createProlepticGregorianCalendar(TimeZone.getDefault());
325-
cal.clear();
326-
// Note that Calendar.MONTH is zero based
327-
cal.set(year, month - 1, day);
328-
329-
return new java.sql.Date(cal.getTimeInMillis());
321+
private static java.sql.Date makeDate(int y, int m, int d) {
322+
return new java.sql.Date(y - 1900, m - 1, d);
330323
}
331-
332324
}

pgjdbc/src/test/java/org/postgresql/test/jdbc2/GetXXXTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import static org.junit.jupiter.api.Assertions.assertEquals;
99
import static org.junit.jupiter.api.Assertions.assertNotNull;
1010
import static org.junit.jupiter.api.Assertions.assertTrue;
11-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
1211

1312
import org.postgresql.test.TestUtil;
1413
import org.postgresql.util.PGInterval;
@@ -25,7 +24,6 @@
2524
import java.sql.Timestamp;
2625
import java.util.Calendar;
2726
import java.util.HashMap;
28-
import java.util.TimeZone;
2927

3028
/*
3129
* Test for getObject
@@ -39,7 +37,7 @@ void setUp() throws Exception {
3937
TestUtil.createTempTable(con, "test_interval",
4038
"initial timestamp with time zone, final timestamp with time zone");
4139
PreparedStatement pstmt = con.prepareStatement("insert into test_interval values (?,?)");
42-
Calendar cal = createProlepticGregorianCalendar(TimeZone.getDefault());
40+
Calendar cal = Calendar.getInstance();
4341
cal.add(Calendar.DAY_OF_YEAR, -1);
4442

4543
pstmt.setTimestamp(1, new Timestamp(cal.getTime().getTime()));

pgjdbc/src/test/java/org/postgresql/test/jdbc2/IntervalTest.java

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import static org.junit.jupiter.api.Assertions.assertFalse;
1010
import static org.junit.jupiter.api.Assertions.assertNotNull;
1111
import static org.junit.jupiter.api.Assertions.assertTrue;
12-
import static org.postgresql.jdbc.TimestampUtils.createProlepticGregorianCalendar;
1312

1413
import org.postgresql.test.TestUtil;
1514
import org.postgresql.util.PGInterval;
@@ -28,8 +27,8 @@
2827
import java.sql.Statement;
2928
import java.util.Calendar;
3029
import java.util.Date;
30+
import java.util.GregorianCalendar;
3131
import java.util.Locale;
32-
import java.util.TimeZone;
3332
import java.util.concurrent.ThreadLocalRandom;
3433

3534
@Isolated("Uses Locale.setDefault")
@@ -149,7 +148,7 @@ void daysHours() throws SQLException {
149148
@Test
150149
void addRounding() {
151150
PGInterval pgi = new PGInterval(0, 0, 0, 0, 0, 0.6006);
152-
Calendar cal = createProlepticGregorianCalendar(TimeZone.getDefault());
151+
Calendar cal = Calendar.getInstance();
153152
long origTime = cal.getTime().getTime();
154153
pgi.add(cal);
155154
long newTime = cal.getTime().getTime();
@@ -209,7 +208,7 @@ void offlineTests() throws Exception {
209208
}
210209

211210
private static Calendar getStartCalendar() {
212-
Calendar cal = createProlepticGregorianCalendar(TimeZone.getDefault());
211+
Calendar cal = new GregorianCalendar();
213212
cal.set(Calendar.YEAR, 2005);
214213
cal.set(Calendar.MONTH, 4);
215214
cal.set(Calendar.DAY_OF_MONTH, 29);
@@ -287,25 +286,6 @@ void date() throws Exception {
287286
assertEquals(date2, date);
288287
}
289288

290-
@Test
291-
void dateYear1000() throws Exception {
292-
final Calendar calYear1000 = createProlepticGregorianCalendar(TimeZone.getDefault());
293-
calYear1000.clear();
294-
calYear1000.set(1000, Calendar.JANUARY, 1);
295-
296-
final Calendar calYear2000 = createProlepticGregorianCalendar(TimeZone.getDefault());
297-
calYear2000.clear();
298-
calYear2000.set(2000, Calendar.JANUARY, 1);
299-
300-
final Date date = calYear1000.getTime();
301-
final Date dateYear2000 = calYear2000.getTime();
302-
303-
PGInterval pgi = new PGInterval("@ +1000 years");
304-
pgi.add(date);
305-
306-
assertEquals(dateYear2000, date);
307-
}
308-
309289
@Test
310290
void postgresDate() throws Exception {
311291
Date date = getStartCalendar().getTime();
@@ -492,13 +472,7 @@ void microSecondsAreRoundedToNearest() throws SQLException {
492472
assertEquals(1, pgi.getMicroSeconds());
493473
}
494474

495-
private static java.sql.Date makeDate(int year, int month, int day) {
496-
Calendar cal = createProlepticGregorianCalendar(TimeZone.getDefault());
497-
cal.clear();
498-
// Note that Calendar.MONTH is zero based
499-
cal.set(year, month - 1, day);
500-
501-
return new java.sql.Date(cal.getTimeInMillis());
475+
private static java.sql.Date makeDate(int y, int m, int d) {
476+
return new java.sql.Date(y - 1900, m - 1, d);
502477
}
503-
504478
}

0 commit comments

Comments
 (0)