Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DateTimeParseException: fails on one host, works on another, same JDK

Tags:

java

date

I'm absolutely baffled. I'm on OpenJdk 11.0.3 both locally and up on my production host. One parses a date and one doesn't. Any ideas on what could be causing the difference?

Edit: hacky work around noted at the end

Same JDK:

kesselc:~/openjdk-11.0.3+7/bin$ ./java -version
openjdk version "11.0.3" 2019-04-16
OpenJDK Runtime Environment 18.9 (build 11.0.3+7)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.3+7, mixed mode)

prodhost: # java -version
openjdk version "11.0.3" 2019-04-16
OpenJDK Runtime Environment (build 11.0.3+7-Ubuntu-1ubuntu218.04.1)
OpenJDK 64-Bit Server VM (build 11.0.3+7-Ubuntu-1ubuntu218.04.1, mixed mode, sharing)

Different results:

kesselc:$ ~/openjdk-11.0.3+7/bin/java DateTest
2019-07-10T09:48-06:00[America/Denver]

prodhost: # java DateTest
Exception in thread "main" java.time.format.DateTimeParseException: Text '948 AM MDT Wed Jul 10 2019' could not be parsed: null
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.ZonedDateTime.parse(ZonedDateTime.java:598)
    at DateTest.main(DateTest.java:13)
Caused by: java.lang.NullPointerException
    at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.prefixLength(DateTimeFormatterBuilder.java:4527)
    at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.add0(DateTimeFormatterBuilder.java:4396)
    at java.base/java.time.format.DateTimeFormatterBuilder$PrefixTree.add(DateTimeFormatterBuilder.java:4391)
    at java.base/java.time.format.DateTimeFormatterBuilder$ZoneTextPrinterParser.getTree(DateTimeFormatterBuilder.java:4138)
    at java.base/java.time.format.DateTimeFormatterBuilder$ZoneIdPrinterParser.parse(DateTimeFormatterBuilder.java:4249)
    at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(DateTimeFormatterBuilder.java:2370)
    at java.base/java.time.format.DateTimeFormatter.parseUnresolved0(DateTimeFormatter.java:2107)
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2036)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    ... 2 more

Here's the simple test class I'm running on both:

public class DateTest {
    private static final DateTimeFormatter hhmm_a_zzz_EEE_MMM_dd_yyyy = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("hmm a zzz EEE MMM d yyyy")
            .toFormatter();

    public static void main(String[] args) {
        System.out.println(ZonedDateTime.parse("948 AM MDT Wed Jul 10 2019", hhmm_a_zzz_EEE_MMM_dd_yyyy));
    }
}

Edit: My solution, of a sort. In this case, I'm parsing NOAA forecasts that are specifically US-centric. So, I hacked my own equivalent of ZoneId.of("MDT") to map to an offset for the 17 time zones noted here: https://www.timetemperature.com/abbreviations/united_states_time_zone_abbreviations.shtml

I feel vaguely dirty and ashamed, but this particular date parser is specific to this particular source, so I'll call it good enough.

I still don't know why the 2 systems behaved differently, but now the problem is irrelevant.

Here's the System.getProperties dump on the two systems.

Good (kesselc):

{sun.desktop=gnome, awt.toolkit=sun.awt.X11.XToolkit, java.specification.version=11, sun.cpu.isalist=, sun.jnu.encoding=UTF-8, java.class.path=., java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, java.vendor.url=http://java.oracle.com/, user.timezone=, os.name=Linux, java.vm.specification.version=11, sun.java.launcher=SUN_STANDARD, user.country=US, sun.boot.library.path=/home/kesselc/.sdkman/candidates/java/11.0.2-open/lib, sun.java.command=DateTest, jdk.debug=release, sun.cpu.endian=little, user.home=/home/kesselc, user.language=en, java.specification.vendor=Oracle Corporation, java.version.date=2019-01-15, java.home=/home/kesselc/.sdkman/candidates/java/11.0.2-open, file.separator=/, java.vm.compressedOopsMode=Zero based, line.separator= , java.specification.name=Java Platform API Specification, java.vm.specification.vendor=Oracle Corporation, java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=11.0.2+9, user.name=kesselc, path.separator=:, os.version=4.4.0-154-generic, java.runtime.name=OpenJDK Runtime Environment, file.encoding=UTF-8, java.vm.name=OpenJDK 64-Bit Server VM, java.vendor.version=18.9, java.vendor.url.bug=http://bugreport.java.com/bugreport/, java.io.tmpdir=/tmp, java.version=11.0.2, user.dir=/home/kesselc/Projects/flex/weather/out/production/classes, os.arch=amd64, java.vm.specification.name=Java Virtual Machine Specification, java.awt.printerjob=sun.print.PSPrinterJob, sun.os.patch.level=unknown, java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib, java.vendor=Oracle Corporation, java.vm.info=mixed mode, java.vm.version=11.0.2+9, sun.io.unicode.encoding=UnicodeLittle, java.class.version=55.0}

Failing (prodhost):

{awt.toolkit=sun.awt.X11.XToolkit, java.specification.version=11, sun.cpu.isalist=, sun.jnu.encoding=ANSI_X3.4-1968, java.class.path=., java.vm.vendor=Oracle Corporation, sun.arch.data.model=64, java.vendor.url=http://java.oracle.com/, user.timezone=, os.name=Linux, java.vm.specification.version=11, sun.java.launcher=SUN_STANDARD, user.country=US, sun.boot.library.path=/usr/lib/jvm/java-11-openjdk-amd64/lib, sun.java.command=DateTest, jdk.debug=release, sun.cpu.endian=little, user.home=/root, user.language=en, java.specification.vendor=Oracle Corporation, java.version.date=2019-04-16, java.home=/usr/lib/jvm/java-11-openjdk-amd64, file.separator=/, java.vm.compressedOopsMode=32-bit, line.separator= , java.specification.name=Java Platform API Specification, java.vm.specification.vendor=Oracle Corporation, java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=11.0.3+7-Ubuntu-1ubuntu218.04.1, user.name=root, path.separator=:, os.version=4.4.0-1079-aws, java.runtime.name=OpenJDK Runtime Environment, file.encoding=ANSI_X3.4-1968, java.vm.name=OpenJDK 64-Bit Server VM, java.vendor.url.bug=http://bugreport.java.com/bugreport/, java.io.tmpdir=/tmp, java.version=11.0.3, user.dir=/opt/ct/deploy, os.arch=amd64, java.vm.specification.name=Java Virtual Machine Specification, java.awt.printerjob=sun.print.PSPrinterJob, sun.os.patch.level=unknown, java.library.path=/usr/java/packages/lib:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib, java.vendor=Oracle Corporation, java.vm.info=mixed mode, sharing, java.vm.version=11.0.3+7-Ubuntu-1ubuntu218.04.1, sun.io.unicode.encoding=UnicodeLittle, java.class.version=55.0}

like image 294
Chris Kessel Avatar asked Dec 13 '22 11:12

Chris Kessel


1 Answers

I have been looking around in the source code of DateTimeFormatterBuilder, and I’m far from sure, but it seems to me that a possible reason for your NullPointerException is a null time zone abbreviation in some locale data. This could cause DateTimeFormatterBuilder.ZoneTextPrinterParser.getTree() to pass a null to DateTimeFormatterBuilder.PrefixTree.add(), which in turn doesn’t expect a null. If so, the different behaviour could be caused by a combination of different time zone and different locale. Note that time zone and locale are independent.

Edit: The questioner has reported that my suggestion doesn’t fix this particular issue. I am letting it stand because I think that the possibility of specifying preferred time zones for parsing a time zone abbreviation may be helpful for several others.

I have not reproduced your exception, so cannot give a sure way to fix, but I would like to suggest that you try:

    Set<ZoneId> preferredZones = Set.of(ZoneId.of("America/Goose_Bay"),
            ZoneId.of("America/Moncton"), ZoneId.of("America/New_York"),
            ZoneId.of("America/Chicago"), ZoneId.of("America/Denver"),
            ZoneId.of("America/Los_Angeles"), ZoneId.of("America/Anchorage"),
            ZoneId.of("Pacific/Honolulu"), ZoneId.of("America/Adak"),
            ZoneId.of("Pacific/Pago_Pago"), ZoneId.of("Pacific/Guam"));
    DateTimeFormatter hhmm_a_zzz_EEE_MMM_dd_yyyy = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("hmm a ")
            .appendZoneText(TextStyle.SHORT, preferredZones)
            .appendPattern(" EEE MMM d yyyy")
            .toFormatter(Locale.US);

The preferred zones have been chosen to match the 17 abbreviations you link to:

AST        America/Goose_Bay, America/Moncton
EST EDT    America/New_York
CST CDT    America/Chicago
MST MDT    America/Denver
PST PDT    America/Los_Angeles
AKST AKDT  America/Anchorage
HST        Pacific/Honolulu
HAST HADT  America/Adak
SST SDT    Pacific/Pago_Pago
CHST       Pacific/Guam

You may want to check whether I have got my mappings right.

In addition, as others have already suggested, I have specified Locale.US for the formatter.

like image 127
Ole V.V. Avatar answered May 23 '23 07:05

Ole V.V.