Joda Time bug or my mistake? (Java Joda Time dates

2020-02-07 05:52发布

问题:

so I was having a problem parsing a date, using the JodaTime chronology IslamicChronology so wrote a small example to demonstrate my problem.

Here's the code:

import org.joda.time.Chronology;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.chrono.IslamicChronology;

import java.util.Date;

/**
 * Test
 */
public class Test
{
    public static void main(String[] args)
    {
        Date now = new Date();

        String format = "dd MMM yyyy";
        Chronology calendarSystem = IslamicChronology.getInstance();
        DateTimeFormatter formatter = DateTimeFormat.forPattern(format).withChronology(calendarSystem);

        String nowAsString = formatter.print(now.getTime());

        System.out.println("nowAsString = " + nowAsString);

        long parsedNowTs = formatter.parseMillis(nowAsString);

        String parsedNowTsAsString = formatter.print(parsedNowTs);
    }
}

And the output:

nowAsString = 16 10 1430
Exception in thread "main" java.lang.IllegalArgumentException: Invalid format: "16 10 1430" is malformed at "10 1430"
    at org.joda.time.format.DateTimeFormatter.parseMillis(DateTimeFormatter.java:634)
    at test.Test.main(Test.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    ...

I'm thinking the problem is that the month name is numeric but am I right? Anyone got any suggestions? This cannot be recreated if the chronology is gregorian.

Thanks in advance.

回答1:

If anyone's interested...I've found a work around:

  • Create a new class e.g. IslamicChronologyWithNames which delegates to an instance of IslamicChronology in package org.joda.time.DateTimeZone
  • Modify the one method; assemble(Fields fields): call the delegate's method and then set fields.monthOfYear (and possibly dayOfWeek) to you own subclass of BasicMonthOfYearDateTimeField
  • The subclass of BasicMonthOfYearDateTimeField can then lookup in property files names for the month's (or day's if DayOfWeek...) name. Subclass needs to be in package org.joda.time.chrono to be able to extend BasicMonthOfYearDateTimeField.

There is still an issue that Joda time seems to validate the date you are parsing before calling methods of subclass like getAsText(int fieldValue, Locale locale) and because it has no knowledge of the month names that your class returns, fails validation and so never calls the methods. My work-around was to have a static method in this class which converts a date as a string with islamic month names into a date as a string with gregorian english month names. So before calling parseDateTime(), call the static method and then the date string passes validation. Then, instead of processing Islamic month names in the convertText() method, use the default gregorian implementation inside your subclass:

protected int convertText(String text, Locale locale)
{
    return GJLocaleSymbols.forLocale(locale).monthOfYearTextToValue(text);
}

That should work! Hope it makes sense for anyone who has the same problem.



回答2:

MMM in the format template indicates the abbreviated month name. A numeric month index should be indicated with MM.

After reading your comment on dtsazza's answer, I first realized your actual problem. :)

I've taken a look into the JODA source code and it seems as if the support for the Islamic chronlogy is rather broken. First of all, the formatter does not support month names, so that the patterns MMM and MMMM are formatted as the month number, although according to the documentation of DateTimeFormat, the abbreviated month name should be used for MMM and the full month name for MMMM.

One problem with your code example is that parseMillis is always using the ISO chronology, although the formatter has been configured with another chronology. This is also mentioned in the API documentation, although not particulary intuitive.

If I replace parseMillis with parseDateTime, I would have expected the chronology to be used for parsing (at least the documentation says so), but it looks as if the implementation ignores the configured chronology and goes on with the ISO chronology here as well.



回答3:

You've specified the format as dd MMM yyyy; as per the API that means you need to supply the month in abbreviated-string form (in this case, "Oct"). The input you passed in would parse correctly if the format were dd MM yyyy.

Edit: it seems I missed your point somewhat. What you've found is a case where for a given formatter,

long input = ...; // whatever
formatter.parseMillis(formatter.print(input));

is throwing an exception. While I can't see any explicit guarantees that this should always work, I'd certainly expect that to be the case - so yes, I'd support your claim that this is a probable bug in Joda itself.

At the very least, if this is expected behaviour there should be some much clearer indication that it can happen.