leponceau.org

Programming And Stuff, You Know The Thing…

Why is subtracting these two times (in 1927) giving a strange result? (Java timezone handling)

Posted at — Mar 7, 2013
package my.evaluation.tests;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;

public class TimeZoneTest {
    
    private final static TimeZone defaultTimeZoneBackup = TimeZone.getDefault();
    
    @After
    public void afterTest() {
        // restore default timezone to avoid introducing interdependencies between the test units...
        // (avoid using compile-on-save in Netbeans... it will save the class using lmod time from
        // the wrong timezone and therefore refuse to update it upon further changes!)
        TimeZone.setDefault(defaultTimeZoneBackup);
    }
    
    /**
     * SimpleDateFormat seems to use the default timezone that was active during its constructor
     * execution...
     * 
     * @url http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result
     * @throws ParseException 
     */
    @Test
    public void testSimpleDateFormatDefaultTimeZone() throws ParseException {
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long millis = sf.parse("1927-12-31 23:54:08").getTime()
            - sf.parse("1927-12-31 23:54:07").getTime();
        assertEquals(millis, 353000L);
        
         
        sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        millis = sf.parse("1927-12-31 23:54:08").getTime()
            - sf.parse("1927-12-31 23:54:07").getTime();
        assertEquals(millis, 353000L);
        
        
        sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        millis = sf.parse("1927-12-31 23:54:08").getTime()
            - sf.parse("1927-12-31 23:54:07").getTime();
        assertEquals(millis, 1000L);
    }
}

Unfortunately, the timezone handling in Java is, well, NOT SO OBVIOUS. And don’t even think of JDBC timestamps… the first thing I always do is to create a static initializer that checks if the default timezone and default locale are set to UTC/English.US in every server application. Some database apps don’t store timezones… and assume that client+server run in the same timezone. So you better handle timezone conversion explicitly. Many devs do not use timestamp types at all and store System.currentTimeMillis() as long. More at stackoverflow.com.