SimpleDateFormat adds random milliseconds while formatting to string

Viewed 260

I am trying to format the date to ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSSZ") using SimpleDateFormat, but formatted string seems to have random values at milliseconds place.

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;


public class DemoApplication {

    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
        calendar.set(2020, 5 , 22, 17, 30, 00);
        Date date = calendar.getTime();

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        String s = df.format(date);
        System.out.println(s);
    }

}

Output on multiple runs:

2020-06-22T17:30:00.886+0530

2020-06-22T17:30:00.049+0530

2020-06-22T17:30:00.799+0530

In the above output, everything is consistent except milliseconds after dot(.), Can someone explain this?

3 Answers

In the above output, everything is consistent except milliseconds after dot(.), Can someone explain this?

The reason is that your format has milliseconds but you haven't set milliseconds in the Calendar instance; therefore, it gives you the milliseconds of the moment when you run your code.

You can verify the same using the following code:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
        calendar.set(2020, 5, 22, 17, 30, 00);
        Date date = calendar.getTime();

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        String s = df.format(date);
        System.out.println(s);
        System.out.println(date.toInstant().getNano()); // Added this line
    }
}

Output:

2020-06-22T17:30:00.425+0100
425000000

On a side note, I recommend you stop using outdated date-time API and start using the modern date-time API.

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        ZonedDateTime zdt = LocalDateTime.of(2020, 5, 22, 17, 30, 00).atZone(ZoneId.systemDefault());
        System.out.println(zdt);
    }
}

Milliseconds aren't covered by

calendar.set(2020, 5 , 22, 17, 30, 00);

you can an extra Line to set them to 0:

calendar.set(Calendar.MILLISECOND, 0);

so that your program looks like this:

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;


public class DemoApplication {

    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
        calendar.set(2020, 5 , 22, 17, 30, 00);
        calendar.set(Calendar.MILLISECOND, 0);
        Date date = calendar.getTime();

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        String s = df.format(date);
        System.out.println(s);
    }

}

The JavaDocs of java.util.Calendar state the following for the method getInstance():

Gets a calendar using the specified time zone and default locale. The Calendar returned is based on the current time in the given time zone with the default locale.

That means you are getting the actual moment and then by calendar.set(2020, 5 , 22, 17, 30, 00); you set year, month, day, hours, minutes and seconds but you are not changing the smaller units of time which will stay as they were when you called Calendar calendar = Calendar.getInstance(TimeZone.getDefault());. That's not random, it's the current millis at that very moment of calling getInstance().

For a possibility of setting the milliseconds of a Calendar to any specific value, have a look at the answer given by @Norwort.

I think you shouldn't be using the outdated datetime classes from java.util. Instead, use java.time, maybe like this:

public static void main(String[] args) throws ParseException {
    ZonedDateTime zonedDateTime = ZonedDateTime.of(2020, 5, 22, 17, 30, 0, 0,
                                                    ZoneId.systemDefault());
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    String s = zonedDateTime.format(dtf);
    System.out.println(s);
}

Which prints

2020-05-22T17:30:00.000+0200

when executed on my system, which has an offset of +02:00 at the moment.
The output on your system might differ, but will have set milliseconds and smaller units of time to 0.

Related