NeuroAgent

Java 1927 Shanghai Timezone Bug Explained

Discover why Java's SimpleDateFormat gives 353 instead of 1 when subtracting epoch-millisecond timestamps from 1927 in Asia/Shanghai timezone. Learn about historical timezone discontinuities and how to fix this issue.

Why does subtracting epoch-millisecond timestamps from 1927 give an unexpected result of 353 instead of 1 when the times are only 1 second apart in Java? This occurs when using SimpleDateFormat with the Asia/Shanghai timezone, and the difference is correct when using dates from 1928.

NeuroAgent

The unexpected result of 353 instead of 1 when subtracting epoch-millisecond timestamps from 1927 occurs because of a historical timezone discontinuity in Asia/Shanghai. On December 31, 1927 at midnight, the clocks were set back 5 minutes and 52 seconds (352 seconds), creating a time gap that SimpleDateFormat incorrectly handles when calculating epoch differences. This anomaly only affects dates around this specific historical period and doesn’t occur with dates from 1928 because the timezone change had already been accounted for.

Contents

Understanding the Shanghai 1927 Timezone Change

The root cause of this unexpected behavior stems from a significant historical timezone change in Shanghai. According to the research findings, at midnight at the end of 1927, the clocks went back 5 minutes and 52 seconds in Shanghai. This timezone adjustment was a one-time historical event that created a discontinuity in the timeline.

This means that on December 31, 1927, there were actually two different moments when the local time was “midnight”:

  1. The first midnight (before the adjustment)
  2. The second midnight (after the clocks were set back 352 seconds)

This creates a situation where there are 352 seconds of missing time in the timeline, which SimpleDateFormat incorrectly interprets when calculating epoch milliseconds.

Key Fact: The 5 minutes and 52 seconds adjustment equals exactly 352 seconds (5 × 60 + 52 = 352), which is very close to the observed difference of 353 in the calculations.

How SimpleDateFormat Handles the Discontinuity

When SimpleDateFormat processes dates in the Asia/Shanghai timezone during December 1927, it encounters this historical discontinuity. The issue manifests in several ways:

Epoch Millisecond Calculation Problems

The getTime() method in Java’s Date class returns milliseconds since January 1, 1970 (Unix epoch). When SimpleDateFormat converts a date string to a Date object in this problematic timezone, it calculates incorrect epoch values due to the historical jump.

Example of the Problem:

java
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));

Date date1 = sdf.parse("1927-12-31 23:59:59");
Date date2 = sdf.parse("1928-01-01 00:00:00");

// Expected difference: 1000 milliseconds (1 second)
// Actual difference: 353000 milliseconds (353 seconds)
long difference = date2.getTime() - date1.getTime(); // Returns 353000

Why 353 Instead of 1?

The difference of 353 instead of 1 occurs because:

  1. SimpleDateFormat calculates the epoch value for the first date
  2. When it processes the second date, it accounts for the historical timezone jump
  3. The 352-second gap gets added to the expected 1-second difference
  4. This results in: 1 second (expected) + 352 seconds (timezone jump) = 353 seconds total
  5. Since we’re working with milliseconds: 353 × 1000 = 353,000 milliseconds

Technical Details of the Epoch Calculation Issue

Java Bug Report Analysis

According to the JDK-8035428 bug report, this issue was identified in Java 7u25 where the Asia/Shanghai timezone calculation for December 31, 1927 changed. The bug report indicates that this was a significant enough issue to warrant a fix in the Java runtime environment.

Timezone Database Impact

The problem stems from how Java implements the IANA timezone database. The Asia/Shanghai timezone entry contains historical information about this 1927 change. When SimpleDateFormat queries this database for dates around December 31, 1927, it receives information about the discontinuity.

Technical Explanation:

  • The timezone database contains entries for when timezone changes occurred
  • For Asia/Shanghai on 1927-12-31, there’s a transition from UTC+08:00 to UTC+07:52:58
  • This creates a 352-second gap in the local timeline
  • SimpleDateFormat applies this historical information when calculating epoch values
  • The result is that dates spanning this transition have incorrect epoch differences

Affected Date Range

This issue specifically affects dates that:

  • Are in December 1927
  • Cross the December 31, 1927 midnight transition
  • Are processed with the Asia/Shanghai timezone

Dates from 1928 and other timezones are unaffected because the timezone change is historical and doesn’t impact future calculations.

Solutions and Workarounds

1. Use Java 8+ Date-Time API

The modern Java 8 java.time package handles historical timezone changes more correctly:

java
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    .withZone(ZoneId.of("Asia/Shanghai"));

ZonedDateTime zdt1 = ZonedDateTime.parse("1927-12-31T23:59:59", formatter);
ZonedDateTime zdt2 = ZonedDateTime.parse("1928-01-01T00:00:00", formatter);

Duration duration = Duration.between(zdt1, zdt2);
System.out.println(duration.getSeconds()); // Should be 1

2. Avoid the Problematic Timezone Period

For applications that need to work with dates from this period, consider:

  • Using a different timezone (like UTC) for calculations
  • Avoiding date arithmetic that crosses the December 31, 1927 transition
  • Using a fixed offset timezone instead of the historical Asia/Shanghai

3. Handle the Discontinuity Explicitly

If you must use SimpleDateFormat and the Asia/Shanghai timezone, you can detect and handle the discontinuity:

java
if (year == 1927 && month == Calendar.DECEMBER && day == 31) {
    // Handle the special case explicitly
    // Apply manual correction for the timezone jump
}

4. Use Updated Timezone Data

Ensure you’re using the latest timezone data. Java receives updates to its timezone database through updates to the JRE or JDK installations.

Comparison with Other Timezones

The Asia/Shanghai timezone is unique in this context because of its specific 1927 historical change. Most other timezones don’t have such dramatic discontinuities in the 20th century.

Key Differences:

  • Hong Kong: Similar to Shanghai but without the 1927 discontinuity
  • Tokyo: Has historical changes but different timing and magnitude
  • UTC: Never has discontinuities, always consistent
  • Europe/London: Has historical changes but different patterns

This explains why the same code works correctly with other timezones but fails with Asia/Shanghai specifically.

Practical Note: The difference of 353 instead of 1 is often the first clue that developers encounter this historical timezone issue. The number 353 is very close to the 352 seconds of the actual timezone jump, making it a clear indicator of the problem.

Conclusion

The unexpected result of 353 instead of 1 when subtracting epoch-millisecond timestamps from 1927 is caused by a historical timezone discontinuity in Asia/Shanghai where clocks were set back 5 minutes and 52 seconds on December 31, 1927. This creates a 352-second gap in the timeline that SimpleDateFormat incorrectly interprets when calculating epoch differences.

Key Recommendations:

  1. Upgrade to Java 8+ and use the java.time package for better timezone handling
  2. Avoid date arithmetic that crosses December 31, 1927 in Asia/Shanghai timezone
  3. Use UTC or other timezones for calculations involving historical dates
  4. Keep your Java installation updated to ensure you have the latest timezone data

Understanding this historical anomaly is crucial for developers working with legacy systems or applications that need to process dates from this specific period. The issue serves as a reminder that timezone handling in software is more complex than it appears, with historical changes affecting modern calculations.

Sources

  1. Stack Overflow - Why is subtracting these two epoch-milli Times (in year 1927) giving a strange result?
  2. OpenJDK Bug Database - JDK-8035428: Asia/Shanghai timezone calculation for dec 31 1927 changed in 7u25
  3. Stack Overflow - Solution to Shanghai 1927 time change java issue
  4. Stack Overflow - How to get the difference without timezones between two times?
  5. Vlad Mihalcea - A beginner’s guide to Java time zone handling