Search code examples
javaandroidtimetimezoneazure-eventhub

java timezone mixup. Almost 2 hour shift for two timestamps supposedly holding UTC time zone


I use a code of:

import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
LocalDateTime.now(ZoneOffset.UTC).format(formatter)
LocalDateTime.now().format(formatter)

to generate an event time timestamp in UTC in an Android app.

Example output could be:

2019-09-30T19:19:49.133 # the app (UTC)
2019-09-30T21:19:49.398 # my current time zone

Now when pushing it to a cloud message store (azure event hub), one field with submission time in UTC is added (enqueuedtimeutc, https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.eventhubs.eventdata.systempropertiescollection.enqueuedtimeutc?view=azure-dotnet)

However when calculating the distance there is an almost 2 hours shift between both timestamps supposedly being delivered in UTC:

distance(s)  frequency
7194.0    0.350518
7196.0    0.133110
7195.0    0.084421
7199.0    0.077668
7193.0    0.075642

how can this be explained? I thought that both timestamps are in UTC. And in fact, the example output seems to work fine.

Is the clock on my Android device 2 hours off (also unlikely as smartphones usually sync itself up with a clock). Why is the time difference not exactly 2hours * 60minuts * 60seconds = 7200 seconds but lower? As latency and processing time needs to be added, I would expect this value to be greater.

edit

Maybe it is better to use:

DateTimeFormatter.ISO_INSTANT.format(Instant.now())

But this should not change anything i.e. is exactly (besides the format string) matching my time output.


Solution

  • I thought that both timestamps are in UTC.

    Not exactly.

    Never call LocalDateTime.now. I cannot imagine a case where that makes sense. Certainly not in this case.

    That class lacks any concept of time zone or offset-from-UTC. So it cannot be used to track a moment. A LocalDateTime holds only a date and a time-of-day. So if you you capture the current moment of noon on January 23, 2020 in America/Montreal by using LocalDateTime, you just threw away the Montreal info. So we are left with only the date and the time-of-day. We no longer know if you meant noon in Tokyo Japan, noon in Tunis, Tunisia, or noon in Los Angeles, CA US — all very different moments several ours apart, and none of them your original intent. We lost your original intent.

    You did capture the date and time-of-day as seen in UTC by passing ZoneOffset.UTC, but using LocalDateTime adds no value, only distraction or possibly unintended values.

    Instant

    To capture the current moment in UTC, use Instant.

    Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.
    

    ISO 8601

    The standard way to represent such a value is using the ISO 8601 formats, ending in either a +00:00 for an offset of zero hours-minutes-seconds from UTC, or its shortcut Z (pronounced “Zulu”).

    String output = Instant.toString() ;   // Generate string in standard ISO 8601 format.
    

    2019-09-30T20:04:56.827546Z

    You may want to truncate to milliseconds if your data sink cannot handle microseconds.

    Instant instant = Instant.now().truncatedTo( ChronoUnit.MILLIS ) ;  // Lop off the microseconds, keeping milliseconds. 
    

    Parse.

    Instant instant = Instant.parse( "2019-09-30T20:04:56.827546Z" ) ;
    

    I cannot help further as I do not use either Android or Azure, and you did not describe enough detail.

    Now when pushing it to a cloud message store (azure event hub),

    Push what? By what API call? Your link leads to a page in C#, not Java. And that page appears to be a getter not a setter (not sure, I don't read C#).