Reputation: 20780
At 6:00am
Sydney time, Rory Allan clicks the foofoo
button in their browser.
This inserts into the foofoo
table with an 8:00pm UTC
timestamp in the database of my server.
A lot of people are excited about this foofoo
button, and they wanted to see what time of the day Sydney time Rory clicked it.
As I stare at my screen in California and view the status of the click, I want to see that he clicked it at 6:00am
, not 1:00pm
, which is what I will see if I pull the UTC time from the server and let my browser convert it.
You see, a lot of different people click foofoo
, such as Phillip Herman in Germany, or Ivan Efimiov in Russia. And all we care about is the relative time of the day they clicked it, in the location they clicked it, regardless of the viewing location.
I don't know the best way to do this. Do I take the local timestamp and convert it to a string, storing it in addition to the real UTC timestamp? Or is this a common problem with a common resolution that I haven't found? I'm guessing / hoping the latter.
This isn't Language specific. These dates have a long journey:
Unix timestamp > Python date > JSON > Node > Mongo > Node > Browser
Upvotes: 0
Views: 55
Reputation: 339372
Exchange date-time values in UTC as strings in standard ISO 8601 format.
General rule in date-time handling is to think in UTC, work in UTC, log in UTC, share in UTC, and store in UTC. But present in zoned time for the user.
By think in UTC, I mean every programmer needs to learn to stop their parochial thinking about their own particular home time zone. Translating back-and-forth to your own zone to UTC to other zones will drive a person nuts. Think of UTC as The One True Time®. All other zones and offsets are mere variations.
This strategy is much like internationalization. The programmer uses key strings in her own human language to look up string values from the localization tool to present a value (piece of text) in the human language preferred by her user. In date-time handling, the programmer works in UTC but applies a zone preferred by her user for presenting text in the user-interface.
The basic Java class for this Instant
. The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
So when Rory in Sydney clicks his button, we record an Instant
object.
Instant instant = Instant.now() ;
instant.toString(): 2017-06-01T09:24:54.435Z
To present that moment to Rory in his own time zone, we apply a ZoneId
to get a ZonedDateTime
object.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = instant.atZone( z );
zdt.toString(): 2017-06-01T19:24:54.435+10:00[Australia/Sydney]
Now we have two objects, instant
& zdt
, that both refer to the same simultaneous moment on the timeline. The only difference is wall-clock time.
If the user in California wants to see the moment of Rory's button click according to Sydney time, then we already have that solution seen above. If not, if the California user wants to see the moment of Rory's button click in her own California clock, then read on.
We can adjust into yet another region’s wall-clock time by applying another time zone.
ZoneId zAmericaLosAngeles = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdtAmericaLosAngeles = instant.atZone( zAmericaLosAngeles );
zdtAmericaLosAngeles.toString(): 2017-06-01T02:24:54.435-07:00[America/Los_Angeles]
Now we have three objects that all represent the same simultaneous moment: instant
, zdt
, and zdtAmericaLosAngeles
. One moment, three wall-clock times.
See this above code run live at IdeOne.com.
If you literally meant you want the time-of-day only, without the date, you can extract a LocalTime
object from those objects above.
But think twice about doing this, as presenting a time-only without date and zone can lead to ambiguity and misunderstanding.
LocalTime lt = zdt.toLocalTime();
all we care about is the relative time of the day they clicked it, in the location they clicked it, regardless of the viewing location
If you are really really really sure that is what you want, then combine my advice above. (But I doubt this is a wise way to go.)
LocalTime lt = LocalTime.now( ZoneId.of( "Australia/Sydney" ) ) ; // Current time-of-day in Sydney.
We have been using the modern java.time classes in examples above. They are exceptional – I mean than literally. They are virtually unique. Virtually all other platforms have terrible support for date-time work. The predecessor to java.time was the Joda-Time project which was ported to .Net platform as Noda Time. Other than java.time & Noda Time, I know of no other decent library on other platforms.
The ISO 8601 standard defines many sensible practical formats for textual representation of date-time values.
The java.time classes use the standard formats by default when generating & parsing strings. You have been viewing those ISO 8601 formats in examples above. Except for ZonedDateTime
which wisely extends the standard by appending the name of the time zone in square brackets.
The T
in the middle separates the date portion from the time-of-day portion.
For UTC, the Z
on the end is short for Zulu
and means UTC.
For offset-from-UTC, you see a plus/minus number of hours and minutes ahead of or behind UTC. A time zone is history of past, present, and future offsets in use by a particular region.
How databases handle date-times varies widely, though poor support is most common. A database driver, such as JDBC drivers, add another layer of behavior. So no way to succinctly address that here. And this topic is already asked and answered in many other pages in Stack Overflow.
If your database lacks serious date-time support, you may be better off storing the ISO 8601 strings with the Z
on the end. These values when sorted alphabetical are also in chronological order.
Upvotes: 1
Reputation: 241693
Ok, clearly 8:00pm UTC
is not enough information. But what do you really want to know?
Is 06:00
enough information?
LocalTime
, TimeOfDay
, etc.HH:MM
format (on a 24-hour clock)(60 * HH) + MM
.Is 06:00 Australia/Sydney
enough information?
Is 06:00+10:00
enough information?
time with time zone
data type, such as the one that exists in PostgreSQL, though even the PostgreSQL docs strongly discourage using this type.Assuming you have the date, such as today, then:
Is 2017-05-31T06:00
enough information?
DateTime
or LocalDateTime
in various languages - but be careful that it is not bound to any specific time zone. Use DateTimeKind.Unspecified
in .NET, or "naive" DateTime's in Python, etc.Is 2017-05-31T06:00+10:00
enough information?
DateTimeOffset
or OffsetDateTime
type for this purpose.Is 2017-05-31T06:00 Australia/Sydney
enough information?
Is 2017-05-31T06:00+10:00 Australia/Sydney
enough information?
ZonedDateTime
in Java/Joda-Time/Noda-Time, or an "aware" DateTime in Python (pytz, dateutils, etc.), or similar types when they exist in your platform.timestamp with time zone
, as you might expect it to store the time zone and it typically does not (despite the name).As you can tell - there are a LOT of options, and it really depends on exact use case and features available in each language/platform. You'll find more details if you search/ask for each one separately.
Upvotes: 1