Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is java's SimpleDateFormat substracting 1 second from my UTC date when using SimpleTimeZone

Can someone explain why is SimpleDateFormat substracting 1 second to my parsed date when setting the time zone with SimpleTimeZone?

Is this a jdk bug?

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

public class SimpleDateFormatTest {

    public static void main(String[] args) throws ParseException {

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

        format.setTimeZone(TimeZone.getTimeZone("UTC"));
        System.out.println(format.parse("2016-02-24T17:31:00Z")); 
        // prints Wed Feb 24 17:31:00 UTC 2016

        format.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC"));
        System.out.println(format.parse("2016-02-24T17:31:00Z")); 
        // Wed Feb 24 17:30:59 UTC 2016
    }

}
like image 358
josketres Avatar asked Jan 23 '15 10:01

josketres


2 Answers

You're using the constructor improperly.

The constructor of SimpleTimeZone is defined as:

public SimpleTimeZone(int rawOffset, String ID)

Constructs a SimpleTimeZone with the given base time zone offset from GMT and time zone ID with no daylight saving time schedule.

Parameters:

rawOffset - The base time zone offset in milliseconds to GMT.

ID - The time zone name that is given to this instance.

So the first parameter to the constructor is how much the time zone you are creating is going to differ from GMT, in milliseconds.

There is no reason to use any of the constants STANDARD_TIME, UTC_TIME or WALL_TIME here. This is not the place for them to be used. However, because these constants are of type int, and the rawOffset parameter is of type int, Java is not preventing you from passing these constant erroneously to the constructor.

In the Java source code, this constant is defined as:

public static final int UTC_TIME = 2;

So what you are actually doing when you call

new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC")

Is saying "Please create a time zone which is 2 milliseconds away from GMT, and call it 'UTC'". I'm pretty sure that this is not what you were trying to achieve. It is a custom time zone, that's a little different than UTC, and this difference is rounded to a whole second in your output.

like image 145
RealSkeptic Avatar answered Nov 15 '22 07:11

RealSkeptic


The value of SimpleTimeZone.UTC_TIME is actually 2, so the SimpleTimeZone is constructed with a two millisecond offset:

/**
 * Constant for a mode of start or end time specified as UTC. European
 * Union rules are specified as UTC time, for example.
 * @since 1.4
 */
public static final int UTC_TIME = 2;

/**
 * Constructs a SimpleTimeZone with the given base time zone offset from GMT
 * and time zone ID with no daylight saving time schedule.
 *
 * @param rawOffset  The base time zone offset in milliseconds to GMT.
 * @param ID         The time zone name that is given to this instance.
 */
public SimpleTimeZone(int rawOffset, String ID)
{
    this.rawOffset = rawOffset;
    setID (ID);
    dstSavings = millisPerHour;
}

Ideone test:

public static void main(String[] args) throws ParseException {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

    format.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date1 = format.parse("2016-02-24T17:31:00Z");
    System.out.println(date1.getTime());

    format.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC"));
    Date date2 = format.parse("2016-02-24T17:31:00Z");
    System.out.println(date2.getTime());
}

Results:

1456335060000
1456335059998
like image 32
Tobías Avatar answered Nov 15 '22 07:11

Tobías