2

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?

SJ1
  • 344
  • 3
  • 12
  • 1
    Is there a specific reason you are using the old legacy date API of Java instead of the much better `java.time`? – Zabuzard Jun 24 '20 at 11:37
  • @Zabuzard No specific reason, It's an old codebase that tries to serialize the date fields by setting dateFormat in Jackson's object Mapper. Ran into this while writing a test case. – SJ1 Jun 24 '20 at 11:43
  • @ArvindKumarAvinash - disagree. Root cause of this issue is that OP used `Calendar`'s `set` methods without clearing/explicit setting of its `MILLISECOND` field. – Nowhere Man Jun 24 '20 at 11:51
  • @AlexRudenko One (maybe not the only) root cause of your root cause is that `Calendar` has a confusing API. I recommend that (at least after java.time, March 2014) we don’t use that class anymore. I understand that this minimal example is derived from old code, possibly from before the advent of java.time, though. PS The original you link to is spot-on. – Ole V.V. Jun 24 '20 at 14:47
  • 2
    @OleV.V. I'm well aware and totally agree that `Date/Calendar`are obsolete and have not to be used at all. But there are different cases when people encounter issues with legacy code (OP's explicitly stated this) or when some schools teach outdated technology stack. And I believe it's not a sin to mention/refer to _obviously_ obsolete solution to obsolete problem in such cases, it's better than just recommend to switch to something new and shiny :) – Nowhere Man Jun 24 '20 at 15:21

3 Answers3

3

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);
    }
}
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

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);
    }

}
Norwort
  • 13
  • 6
0

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.

deHaar
  • 17,687
  • 10
  • 38
  • 51