Search code examples
javaparsingexceptiontime

Find out a work around to bypass Java.time bug with spelling of name of month


I found a java.time bug recently. it is very easy to reproduce it;

I tested it on JDK 17.0.10 and JDK 21.0.4 on MacOS X and Centos Stream 9

Code as:

public class JakartaDateTime {

public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy");
    LocalDateTime now = LocalDateTime.now();
    System.out.println(formatter.format(now));
}

This code will print a string

Fri Sept 06 23:10:23 2024

Sure,it is not I expected, the expected result should be:

Fri Sep 06 23:10:23 2024

I had report it as a bug to java bug report and Oracle bug database ID: 9077542. Im sure it will not be fixed soon. So I have to find out a work around to avoid it.

I'm trying to use gson to map a income json to our java class, json object key/value pair as:

{"date":"Fri Sep 06 23:10:23 2024"}

so, I have to code a deserialiser to pars that string to java.time.LocalDateTime. I did some thing like this:

public class EuDateTimeDeserializer implements JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
    String dateTimeStr =jsonElement.getAsString();
    if(!ThssUtils.isValidString(dateTimeStr)){
        return null;
    }else {
        return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy"));
    }
  }
}

But this deserialiser only works on other months than September.

throws error as:

java.time.format.DateTimeParseException: Text 'Fri Sep  6 15:42:12 2024' could not be parsed at index 4

That means, the deserialiser not recognise above string.

Does any one have this experience to find out a work around to avoid previous java.time bug?

Please advise!


Solution

  • tl;dr

    Not a bug. Specify Locale.

    DateTimeFormatter.ofPattern( … ).withLocale( locale )
    

    Even better: Use only standard ISO 8601 formats when exchanging, storing, or logging date-time values textually. Use localized text only for presentation to the user, never for data.

    Feature, not a bug

    While you neglected to mention exactly what you consider to be a "bug", I presume you mean the missing t in Sept vs Sep.

    Localization

    The spelling, abbreviation, and punctuation in date-time formatting depends on a Locale. The human language and cultural norms contained in a Locale object control the process of localization.

    Specify a Locale

    Here we specify English in United States 🇺🇸. We get Sep without the t.

    LocalDateTime ldt = LocalDateTime.of( 2024 , Month.SEPTEMBER , 6 , 23 , 10 , 23 , 0 );
    System.out.println( "ldt = " + ldt );
    
    Locale locale = Locale.of( "en" , "US" );
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "EEE MMM dd HH:mm:ss yyyy" ).withLocale( locale );
    String output = formatter.format( ldt );
    
    System.out.println( "output = " + output );
    

    ldt = 2024-09-06T23:10:23

    output = Fri Sep 06 23:10:23 2024

    Change that locale to English in Australia 🇦🇺. We get Sept with the t.

    Locale locale = Locale.of( "en" , "AU" );
    

    ldt = 2024-09-06T23:10:23

    output = Fri Sept 06 23:10:23 2024

    Change that locale to French in Canada 🇨🇦. We get sept. in lowercase with punctuation.

    Locale locale = Locale.of( "fr" , "CA" );
    

    output = ven. sept. 06 23:10:23 2024

    Please retract your faulty bug report.

    Tip: Always suspect your own code, or your own misunderstanding, before doubting a library bundled with Java. Yes, a bug could creep into the the codebase of an implementation of Java. But bugs like your allegation here is rare. The OpenJDK code base used for most implementations of Java is some of the most thoroughly examined and tested code ever produced (outside of specialties such as military & aerospace).

    Avoid LocalDateTime.now

    By the way, your code calls LocalDateTime.now. I cannot imagine a scenario where that is the smart thing to do. That class cannot represent a moment.

    A LocalDateTime object represens a date with a time-of-day but lacks the context of an offset-from-UTC or a time zone. Without that context, we have no way to know if you meant 11 PM in Toowoomba Australia, 11 PM in Toulouse France, or 11 PM in Toledo Ohio US — three very different moments, several hours apart.

    To capture the current moment, use one of the three classes that track a specific point on the timeline:

    • Instant
    • OffsetDateTime
    • ZonedDateTime

    The first and third are most common for business purposes, while the second is used mainly for exchange with a SQL database.

    Instant now = Instant.now() ;  // Capture current moment as seen "in UTC", meaning an offset from UTC of zero hours-minutes-seconds. 
    

    See that same moment through the wall-clock/calendar of a particular time zone.

    ZoneId z = ZoneId.of( "Australia/Brisbane" ) ;
    ZonedDateTime zdt = instant.atZone( z ) ;