String to ZonedDateTime is changing format

2019-03-01 10:38发布

问题:

String ip="2011-05-01T06:47:35.422-05:00";
ZonedDateTime mzt = ZonedDateTime.parse(ip).toInstant().atZone(ZoneOffset.UTC);
System.out.println(mzt);

System.out.println("-----");

String ip2="2011-05-01T00:00:00.000-05:00";
ZonedDateTime mzt2 = ZonedDateTime.parse(ip2).toInstant().atZone(ZoneOffset.UTC);
System.out.println(mzt2);

Output:

2011-05-01T11:47:35.422Z
-----
2011-05-01T05:00Z

Why is the date format getting changed in case 2? I am getting SQLServer Database error due to this.

回答1:

This is what toString from documentation said

The format used will be the shortest that outputs the full value of the time where the omitted parts are implied to be zero.

To solve this problem, you need another formatter :

String result = mzt2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss:SSS'Z'"));

Output

2011-05-01T11:47:35.422Z
2011-05-01T05:00:00:000Z


回答2:

Just to complement YCF_L's answer:

Using a quoted letter in the pattern, like it was done with the Z (it's inside quotes: 'Z') is wrong. This will treat Z as a literal and ignore the object's offset.

For this particular case, it works, but the "Z" in the end means that the date is in UTC, and you can't hardcode it as a literal. Putting it inside quotes will always print "Z" no matter what's the offset, giving wrong results.

Example:

// wrong: it uses Z inside quotes
DateTimeFormatter wrong = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss:SSS'Z'");

// correct: it uses the offset pattern (X)
DateTimeFormatter correct = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss:SSSXXX");

// print a date/time not in UTC
String ip2 = "2011-05-01T00:00:00.000-05:00";
ZonedDateTime mzt = ZonedDateTime.parse(ip2);

System.out.println(mzt.format(wrong)); // 2011-05-01T00:00:00:000Z
System.out.println(mzt.format(correct)); // 2011-05-01T00:00:00:000-05:00

Note that the wrong formatter prints Z, which is wrong, because the offset of mzt is -05:00.

The wrong formatter works for your case because you're using UTC:

// convert to UTC
ZonedDateTime mzt2 = mzt.withZoneSameInstant(ZoneOffset.UTC);
System.out.println(mzt2.format(wrong)); // 2011-05-01T05:00:00:000Z
System.out.println(mzt2.format(correct)); // 2011-05-01T05:00:00:000Z

In this case, both formatters prints the correct result, but that's a coincidence, because mzt2 is in UTC (so the offset is "Z"). But for any offset different than UTC, only the correct formatter will work.

Also note that I used withZoneSameInstant, that has the same result as using .toInstant().atZone(ZoneOffset.UTC).