Aaron
Aaron

Reputation: 419

DateTimeFormatter and SimpleDateFormat produce different results using same input string

We currently in the process of replacing SimpleDateFormat with DateTimeFormatter. During this I came across a weird behavior. There is a difference in milliseconds which I can't explain to myself. Here is the code:

val timeString = "2021-09-17T13:37:00.09Z"
val newFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneId.of("UTC"))
val oldFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US)

val zonedDateTime = newFormatter.parse(timeString)
val newFormatterDate = Date.from(zonedDateTime.toInstant())
val oldFormatterDate = oldFormatter.parse(timeString)

Assert.assertEquals(newFormatterDate, oldFormatterDate) // false
// newFormatterDate.time is 1631885820090 and 
// oldFormatterDate.time is 1631885820009

I found a lot of posts here stating that we shouldn't use SimpleDateFormat anymore.

But can someone explain to me how this could happen? Do we have a bug in our code or misunderstood something?

Edit: The solution provided by @Ole V.V.'s link (How to parse date-time with two or three milliseconds digits in java?) may solve the bug I encounter but it does not answer the question/explain why these two formatters produce different results.

Upvotes: 2

Views: 961

Answers (2)

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79550

The modern, DateTimeFormatter considers the digits after seconds as the fraction of a second whereas the legacy, SimpleDateFormat considers the digits after the seconds as the number of milliseconds.

Let's see how DateTimeFormatter processes it:

0.09 seconds = 0.09 * 1000 ms = 90 ms

On the other hand, SimpleDateFormat processes it as 09 milliseconds = 9 milliseconds.

By the way, while using the modern Date-Time API, you do not need to use a DateTimeFormatter explicitly to parse your Date-Time string because it is already in ISO 8601 format. The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards.

Demo:

import java.time.Instant;

public class Main {
    public static void main(String[] args) {
        System.out.println(Instant.parse("2021-09-17T13:37:00.09Z").toEpochMilli());
    }
}

Output:

1631885820090

Learn more about the modern Date-Time API* from Trail: Date Time.


* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.

Upvotes: 3

laalto
laalto

Reputation: 152917

Your input data has two digits for fractional seconds. Your input patterns have three. The implementations interpret differently what 09 fractional seconds mean when three digits are expected.

If possible, fix either your input data or your input pattern to agree on the number of fractional digits. If you need to accept both two or three digits of fractional seconds, have a look at How to parse date-time with two or three milliseconds digits in java?

Upvotes: 1

Related Questions