Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Java LocalTime.parse working in spring boot on local PC but not working in Docker container

Title describes my problem. I’m feeling really dumb now because it’s probably a minor, tiny issue.

I’ve been stuck on this for almost 2 days. The app I’m building is much bigger, with docker compose to start the postgres container, but the problem can be recreated by creating a new (java 21, maven) spring boot project with only spring web as its dependency. In the class with @SpringBootApplication, add the following:

@Bean
ApplicationRunner applicationRunner() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
        return args -> {
            System.out.println("Parsed: 12:30 pm -> " + LocalTime.parse("12:30 p.m.", formatter));
        };
}

When I run it with intellij or ./mvnw spring-boot:run, or in just a regular java class (without the annotation) it runs.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

But, when I use this docker file:

FROM eclipse-temurin:21-jdk-alpine
COPY . /app/
WORKDIR /app
CMD ["./mvnw", "spring-boot:run", "-Dskiptests"]

And build with docker build -t localtime:1.0 ., then run with: docker run -p 1234:8080 --name localtime-container localtime:1.0. This is what I get after the regular spring startup log messages:

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-02-20T06:08:00.097Z ERROR 71 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.time.format.DateTimeParseException: Text '12:30 p.m.' could not be parsed at index 6
        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2108) ~[na:na]
        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:2010) ~[na:na]
        at java.base/java.time.LocalTime.parse(LocalTime.java:473) ~[na:na]
        at org.example.localtimedemo.LocaltimeDemoApplication.lambda$applicationRunner$0(LocaltimeDemoApplication.java:22) ~[classes/:na]
        at org.springframework.boot.SpringApplication.lambda$callRunner$4(SpringApplication.java:786) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.3.jar:6.1.3]
        at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.3.jar:6.1.3]
        at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.3.jar:6.1.3]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:786) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.2.2.jar:3.2.2]
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
        at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na]
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:na]
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:341) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.2.jar:3.2.2]
        at org.example.localtimedemo.LocaltimeDemoApplication.main(LocaltimeDemoApplication.java:15) ~[classes/:na]

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  26.863 s
[INFO] Finished at: 2024-02-20T06:08:00Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.2.2:run (default-cli) on project localtime-demo: Process terminated with exit code: 1 -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

BUTTTT, When I uncomment the sout statement, it runs fine.

I’ve tried using different Dockerfiles, same problem.

Does anyone know what’s going on and how to fix it?

Thank you

>Solution :

tl;dr

DateTimeFormatter
    .ofPattern( … ) 
    .withLocale( Locale.CANADA_FRENCH ) ;

Specify locale

The indicator for day/night is a matter for localization. Some locales use AM/PM. Some use am/pm. Some use a.m./p.m. And still others use I-don’t-know-what.

Your code neglects to specify a Locale object. So your DateTimeFormatter implicitly relies upon the JVM’s current default locale.

I would bet that the current default locale of the JVM in your own machine differs from that of your Docker environment.

The best solution is to avoid needlessly relying on the JVM’s current default locale. Specify explicitly.

To specify locale, call DateTimeFormatter#withLocale. Keep in mind that java.time uses immutable objects. So the withLocale method generates and returns a new fresh DateTimeFormatter object rather than altering (“mutating”) the original.

Locale locale = Locale.US ;
DateTimeFormatter f = 
    DateTimeFormatter
        .ofPattern( … ) 
        .withLocale( locale ) ;
String output = myLocalTime.format( f ) ;

That is just one example. You will need to determine for yourself the appropriate locale that meets your expectations.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading