Search code examples
javamongodbtimetimezoneutc

How to correctly deal with time saving on server and client side


I have read a lot about time ( incremental time , observed time, offset, timezones...) Now I'm developing a Backend Architecture with Spring and it comes to the point where i have to fit it all together. Clients send and receive JSONs with Timestamps and values and I can decide how the Syntax should be. I'm not shure if the constellation is right the way I planed it so I would be happy if you correct me in necessary points.

First: How I understand the basic concept.

Mantra: Always work with utc time.

Format : iso8601 YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) or 1997-07-16T20:20:30.45Z

TimeZone: eg "Europe/Berlin" This is the id for timezone database where the specific rules for the zones are defined ( DST offset .. ).

Offset: Tells you the positive or negative offset to the utc time.

What I planned: In my case it is possible that the data was sampled within diefferent timezones. It's also porrible that a timezone is changed during the sampling process.

So I want to store the data as follows in my mongodb:

 "TimeStamp": {
                            "StartTime":  "1997-07-16T20:20:30.45Z",
                            "EndTime":  "1997-07-16T20:20:30.45Z",
                            "StartTimeZone":"Europe/Berlin",
                            "EndTimeZone":"Europe/Berlin",
                            "StartTimeOffset": +7,
                            "EndTimeOffset" : +7
                        } 

The client can choose an interval from within the data will be returned. The interval has also to be defined as UTC iso format without offset. As far as I know with the UTC format I can do $lte and $gte operations on the date to filter the interval. So the client receives a JSOnArray with mupltiple JSOnObjects. Each Object has a value and a TimeStamp Object. With using the TimeZone one can have a look how the offset was at the time when the data was sampled so I don't have to add offset information and so one can calculate the local time of the user. But what if the offset changes. I guess it makes sense also to store the offset for that so so one can reconstruct also "old" timezone rules. Do you think this is a good solution or is there something I forgot/misunderstood/can be done more efficient


Solution

  • Your Question is rather confused.

    If you have a moment in UTC, you know the offset-from-UTC is zero, so you do not care about further time zone information.

    Instant instant = Instant.now() ;      // Capture the current moment as seen in UTC (an offset-from-UTC of zero hours-minutes-seconds).
    String output = instant.toString() ;   // Generate text representing this moment in UTC in standard ISO 8601 format. The `Z` on end means UTC (an offset of zero), and is pronounced "Zulu". 
    

    If someone wants to see that moment through the wall-clock time used in Germany, they can apply a ZoneId to produce a ZonedDateTime object.

    ZoneId zBerlin = ZoneId.of( "Europe/Berlin" ) ;
    ZonedDateTime zdtBerlin = instant.atZone( zBerlin ) ;
    

    If someone wants to see that moment through the wall-clock time used in Japan, ditto.

    ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ; 
    ZonedDateTime zdtTokyo = instant.atZone( zTokyo ) ;
    

    If someone wants to see that moment through the wall-clock time used in Québec, ditto.

    ZoneId zMontréal = ZoneId.of( "America/Montreal" ) ; 
    ZonedDateTime zdtMontréal = instant.atZone( zMontréal ) ;
    

    All of these (instant, zdtBerlin, zdtTokyo, zdtMontréal) all represent the very same simultaneous moment, the same point on the timeline. Only the wall-clock time is different. Imagine a conference call with participants in each of these regions. They all experience the same moment, but when they look up at the clock hanging on their respective wall, they each see a different reading.

    TimeZone: eg "Europe/Berlin" This is the id for timezone database where the specific rules for the zones are defined ( DST offset .. ).

    Offset: Tells you the positive or negative offset to the utc time.

    Be clear on the meaning of zone and offset:

    • Offset-from-UTC is merely a number of hours-minutes-seconds. Nothing more. For example, -07:00.
    • A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. For example, in a place with politicians crazy enough to have adopted Daylight Saving Time (DST), their offset-from-UTC changes twice per year, jumping a head an hour, and then falling back an hour. DST does not bend time in an Einstein/Relativity sense, DST merely plays silly games with wall-clock time.

    data was sampled within different timezones

    You do not care. If you have a moment captured in UTC, that is all you need.

    It's also porrible that a timezone is changed during the sampling process.

    Again… You do not care. If you have a moment captured in UTC, that is all you need.

    So I want to store the data as follows in my mongodb:

     "TimeStamp": {
        "StartTime":  "1997-07-16T20:20:30.45Z",
        "EndTime":  "1997-07-16T20:20:30.45Z",
        "StartTimeZone":"Europe/Berlin",
        "EndTimeZone":"Europe/Berlin",
        "StartTimeOffset": +7,
        "EndTimeOffset" : +7
    } 
    

    No, much too much. The last four lines are redundant and possibly confusing. All you need is:

     "TimeStamp": {
        "StartTime":  "1997-07-16T20:20:30.45Z",
        "EndTime":  "1997-07-16T20:20:30.45Z"
    } 
    

    By the way, the term timestamp usually means a specific moment, not a span of time. I suggest a better name such as timespan.

     "TimeSpan": {
        "StartTime":  "1997-07-16T20:20:30.45Z",
        "EndTime":  "1997-07-16T20:20:30.45Z"
    } 
    

    By the way, in your examples the start time equals the end time. Perhaps you made a mistake when posting.

    With using the TimeZone one can have a look how the offset was at the time when the data was sampled so I don't have to add offset information

    You do not need additional offset information. The Z on the end of the first two lines tells you everything you need to know. The Z, pronounced "Zulu", means UTC, an offset of zero.

    String input = "1997-07-16T20:20:30.45Z" ;    // The `Z` = UTC, an offset of zero.
    Instant instant = Instant.parse( input ) ;
    ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
    ZonedDateTime zdt = instant.atZone( z ) ;     // Same moment, different wall-clock time.
    

    so one can calculate the local time of the user

    Apparently the word "user" here is a poor choice of terms. You mean the person collecting the data, not the person receiving the data. So you want to remember the time zone in which the data was collected.

    So bundle the name of the time zone in which the data sample or instrument reading was collected.

     "TimeSpan": {
        "StartTime":  "1997-07-16T20:20:30.45Z",
        "EndTime":  "1997-07-16T20:20:30.45Z"
        "SampleTimeZone": "Europe/Berlin"
    } 
    

    When you want to localize a moment, all you need is a moment (an Instant) and a time zone (a ZoneId). From that you can generate a string using the wall-clock time of that zone.

    String startInput = …  // Pull "StartTime" element from JSON.
    String zoneName = …  // Pull "SampleTimeZone" from JSON.
    Instant instant = Instant.parse( startInput ) ;  // "1997-07-16T20:20:30.45Z"
    ZoneName zone = ZoneId.of( zoneName ) ;  //  "Europe/Berlin" 
    ZonedDateTime zdt = instant.atZone( zone ) ;
    String output = zdt.toString() ;    // Or use a `DateTimeFormatter`. 
    

    Do you think this is a good solution

    No. You were confused about UTC, time zone, and offset-from-UTC somehow being unrelated. But they are all connected. UTC is The One True time. Some places used an offset to adjust for their region’s wall-clock time. A time zone is a history of those changes for that region.