ZonedDateTime format and parsing exception with “z” in format pattern
Asked Answered
E

2

6

I have a problem with parsing ZonedDateTime:

DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-ddzhh:mm");
ZonedDateTime.parse(s, formatter);

This results in an error:

java.time.format.DateTimeParseException:
    Text '2022-05-24UTC12:15' could not be parsed at index 10

whats wrong with this code?

Eydie answered 24/5, 2022 at 12:20 Comment(12)
The pattern is wrong…Lineman
Give this a try! zoneddatetime-parse-method-in-java-with-examplesInterplead
2022-05-24UTC12:15 — What a weird format. Is there a particular reason why this format is used?Confucius
@MCEmperor well 3rd party has such weird formatting, and are using our API, I was suprised with this format alsoEydie
I cannot reproduce. On my Java 17 I get a completely different exception: java.time.format.DateTimeParseException: Text '2022-05-24UTC12:15' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {MinuteOfHour=15, HourOfAmPm=0},ISO,Etc/UTC resolved to 2022-05-24 of type java.time.format.Parsed.Remissible
In your format pattern string you need upper case HH for hour of day. See for example DateTimeParseException: Text '2019-06-07 12:18:16' could not be parsed.Remissible
What is your locale? Could it be that UTC is called something else in your default locale??Remissible
Surprise: I can reproduce your problem in German locale even though your formatter still prints UTC for Etc/UTC time zone. Might look like a bug in java.time (I am still using Java 17).Remissible
I should say it’s a bug. I did a fast search and didn’t find it documented, though. Using German locale DateTimeFormatter can parse 2022-05-24UTC using pattern yyyy-MM-ddz and UTC12:15 using pattern zHH:mm but not 2022-05-24UTC12:15 using pattern yyyy-MM-ddzHH:mm. With non-German locales there isn’t any problem. Consider reporting it.Remissible
Yeah, @OleV.V. it's the German locale that was implicitly considered when I tried it without explicitly providing a locale. Works with Locale.ENGLISH, for example…Lineman
For a workaround just specify a non-German locale on your formatter, for example DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-ddzHH:mm", Locale.ROOT);.Remissible
Secondary conclusions still worth noting: (1) It’s always a good idea to specify locale explicitly on your formatter. (2) That 3rd party — and everyone exchanging date and time data — ought to learn about the ISO 8601 standard for doing so.Remissible
L
6

The character z should be able to parse "UTC" (in most Locales) because UTC is considered a time-zone ID and a time-zone name in java.time. A VV can parse time-zone ids while zcan parse time-zone-names according to the JavaDocs of java.time.DateTimeFormatter, here's the relevant part of the docs:

Symbol  Meaning                     Presentation      Examples
------  -------                     ------------      -------
(…)

V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
z       time-zone name              zone-name         Pacific Standard Time; PST

(…)

That means you can parse it using the character V without providing a specific Locale to your DateTimeFormatter. You will have to put two of them (VV) or you will get a nice IllegalArgumentException with the following message:

java.lang.IllegalArgumentException: Pattern letter count must be 2: V

If you still want to use z, provide a Locale that considers UTC an abbreviation of Universal Time Coordinated, the Central European Summer Time is an abbreviation that definitely changes among different Locales, e.g.

  • English: CEST
  • German: MESZ

Other Locales might have different abbreviations, which makes me wonder if your Locale actually even has a different one for UTC.
Provide Locale.ENGLISH, for example and it should parse successfully.

You should provide one anyway because if you don't, the DateTimeFormatter will implicitly use the default Locale of your (Java Virtual) machine.

So you can try this:

DateTimeFormatter format = DateTimeFormatter.ofPattern("uuuu-MM-ddVVHH:mm");

or this:

DateTimeFormatter format = DateTimeFormatter.ofPattern("uuuu-MM-ddzHH:mm", Locale.ENGLISH);

both should be able to parse an input like "2022-05-24UTC12:15" if you use HH instead of hh for hours of day (hh = 12h format, HH = 24h format).

Lineman answered 24/5, 2022 at 12:33 Comment(10)
thanks, hmm this works right with UTC, but what if I would like to parse " Text '2022-05-24CEST12:15' could not be parsed at index 10" ?Eydie
I want also CEST instead of UTC for exampleEydie
I think thats why I used z :) but its not good for UTC thenEydie
yyyy-MM-ddzHH:mm is perfect then for meEydie
I don't think "2022-05-24CEST12:15" can be parsed at all with a DateTimeFormatter built with a single z, at least it doesn't work on my machine. However, 2022-05-24CET12:15 (no S, only CET) can be parsed with your formatter.Lineman
Yes, @OleV.V. if I apply a Locale it will even parse CEST ;-), but not without and the locales GERMAN and GERMANY don't work.Lineman
That‘s why it does not work without a locale on my machine…Lineman
@Eydie Beware of 2-4 letter pseudo time zones like CET, CEST, CST, IST. These are not real time zones, are not standardized, and are not even unique! They should be used only for presentation to the user, never for data exchange.Whoa
@BasilBourque well I dont like this format but its connected to some 3rd party, thats why.Eydie
@Eydie When you receive such a string with IST, how will you know if it means India Standard Time or Ireland Standard time? If you receive a string with CST, how will you know if it means Central Standard Time or China Standard Time?Whoa
H
-3

Yes it's because of formatter pattern, i suggest to use the code below :

    final DateTimeFormatter format
            = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");

    ZonedDateTime zonedDateTime = ZonedDateTime
            .parse("2022-05-24 14:30:30 -01:00", format);
    
    System.out.println(zonedDateTime);

    ZonedDateTime localTimeline = zonedDateTime
            .withZoneSameInstant(ZoneId.systemDefault());

    // for the local timezone
    System.out.println(localTimeline);
Hedgerow answered 24/5, 2022 at 12:34 Comment(2)
Well, this code won't help the question asker, because their input text is different.Confucius
If we want to suggest a different format - which makes sense - I think we should suggest ISO 8601, the international standard.Remissible

© 2022 - 2024 — McMap. All rights reserved.