deHaar
deHaar

Reputation: 18568

Does a 'z' in a datetime String have different outputs in different locales?

Not long ago, I provided an answer to a question about how to extract the time zone from a ZonedDateTime parsed from a String.
It worked in general, but there were different outputs of the same code on OP's system an my one, which somehow doesn't let me go anymore.

The related question asked how to get a ZoneId from a ZonedDateTime and I provided a way. Admittedly, it is not the accepted answer, but still seemed worth an upvote from someone.

The special thing about my answer is concerning the 'z' in the pattern used to parse the time String. There is a time zone name in that String representing the zone "Australia/Adelaide" by "... ACST" (Australian Central Standard Time).

When I parse it on my system and print/format the ZonedDateTime using the DateTimeFormatter.ISO_ZONED_DATE_TIME, it prints the time in "America/Manaus" and having extracted the ZoneId, it is still that one from South America. OP stated in a comment below my answer, that on his system, at least one of the output lines shows the desired/correct ZoneId.

How is that possible? Does the system default locale have any influence on parsing the 'z' in datetime Strings?

This is the code from my answer in the question plus an output of my ZoneId.systemDefault():

public static void main(String args[]) throws Exception {
    String time = "2 Jun 2019 03:51:17 PM ACST";
    String pattern = "d MMM yyyy hh:mm:ss a z"; // z detects the time zone (ACST here)
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

    // parse a time object using the formatter and the time String
    ZonedDateTime zdt = ZonedDateTime.parse(time, formatter);
    // print it using a standard formatter
    System.out.println(zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
    // extract the zone id
    ZoneId zoneId = zdt.getZone();
    ZoneId sysDefault = ZoneId.systemDefault();
    // print the zone id
    System.out.println("Time zone of parsed String is " + zoneId);
    System.out.println("System default time zone is " + sysDefault);

    // retrieve the instant seconds
    Instant instant = zdt.toInstant();
    // print the epoch seconds of another time zone
    System.out.println("Epoch seconds in Australia/Adelaide are " 
            + instant.atZone(ZoneId.of("Australia/Adelaide")).toEpochSecond());
}

and its output is this:

2019-06-02T15:51:17-04:00[America/Manaus]
Time zone of parsed String is America/Manaus
System default time zone is Europe/Berlin
Epoch seconds in Australia/Adelaide are 1559505077

Can anyone point me to a mistake I have made or confirm and explain the influence of different system default ZoneIds on parsing a String to a ZonedDateTime?

Upvotes: 4

Views: 177

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338775

The Answer by Ole V.V. is correct.

ISO 8601

Furthermore, strings such as "2 Jun 2019 03:51:17 PM ACST" should not be parsed. Such formats should never be used to exchange date-time values. Such strings should be used only for presentation to the human user, not for data-exchange.

Would you try the exchange the monetary amount of USD 23.67 as twenty three dollars and sixty-seven cents in United States dollars or as vingt-trois dollars et soixante-sept cents en dollars des États-Unis?

To exchange date-time values via text, always use the standard ISO 8601 formats. The sensible formats are designed to be unambiguous, easy to parse by machine, and easy to read by humans across cultures.

If using a time zone, use the ZonedDateTime::toString method to generate text. This method wisely extends the ISO 8601 format to append the name of the time zone in square brackets.

ZoneId z = ZoneId.of( "Australia/Adelaide" ) ;
String output = ZonedDateTime.now( z ).toString() ;

2020-02-10T06:30:57.756491+10:30[Australia/Adelaide]

Run that code live at IdeOne.com.

Parsing.

ZonedDateTime zdt = ZonedDateTime.parse( output ) ;

Even better, use UTC values when the time zone is not directly relevant. To capture and communicate the current moment in UTC, use Instant.

Again, call toString & parse to generate and parse text in standard ISO 8601 format. The Z on the end means UTC, an offset of zero hours-minutes-seconds, and is pronounced “Zulu”.

String output = Instant.now().toString() ;

2020-02-09T20:07:42.718473Z

Upvotes: 1

Anonymous
Anonymous

Reputation: 86296

I too have experienced ambiguous time zone abbreviations being parsed differently on different JVMs (also when providing a locale, so that is not the only issue). I don't think the exact behaviour is documented. In some cases the JVMs default time zone was chosen, I don't know if it will always be when it matches, though.

You can control the choice of time zone in ambiguous cases through the overloaded DateTimeFormatterBuilder.appendZoneText(TextStyle, Set<ZoneId>) method.

An example where locale makes a difference: Europe/Berlin and many other European time zones will be formatted into Central European Time or CET in many locales. In a German locale they instead become Mitteleuropäische Zeit or MET.

Upvotes: 6

Related Questions