Reputation: 7
I am trying to calculate the duration between two instances of LocalDateTime
. The special thing here is that each instance of LocalDateTime
could be from ANYWHERE in the world:
LocalDateTime start
could be from Nevada and LocalDateTime end
could be from Tokyo. Each "time" associated with the LocalDateTime
is, obviously enough, local to that location.
So if I said...
LocalDateTime start = LocalDateTime.parse("2015-07-14T10:00:00");
and said that start
represented Chicago, that would mean 10:00 AM in Chicago time.
Then I could say...
LocalDateTime end = LocalDateTime.parse("2015-07-14T03:00:00");
and end
represents Moscow, so it is 3:00AM in Moscow time.
Can I create a robust enough solution that will allow start
and end
to represent any cities in the world and still correctly calculate the duration between the two?
Upvotes: 1
Views: 1041
Reputation: 339362
I think you misunderstand the purpose of LocalDateTime
. The "local" means any locality, not a specific locality. As in "Christmas starts at midnight on December 25, 2015" where we mean any locality’s midnight. Christmas starts in Paris several hours earlier than Montréal, for example.
If you know the date-time is meant to represent a date-time in Nevada, the use a Joda-Time DateTime
assigned the proper time zone name of America/Boise
. In the new java.time package (Tutorial) built into Java 8 and later, use a ZonedDateTime
object with assigned time zone.
Similarly, if you know the date-time is local to Tokyo, do not use LocalDateTime
. Use a Joda-Time DateTime
with an assigned time zone of Asia/Tokyo
.
Elapsed time between a pair of LocalDateTime
instances makes no sense. For example, the times may be 14:00 and 18:00 on the same date, but that does not mean four hours difference. If you really meant 14:00 in Paris and 18:00 in Chicago, that would be several hours difference, not two.
I am not discussing calculating elapsed time as that has been handled many many times on StackOverflow. I'm trying to clarify some concepts here. Then you can move on to the existing Questions & Answers for calculating elapsed time.
Generally in SQL databases you should be using the data type TIMESTAMP WITH TIME ZONE
(a.k.a. TIMESTAMPZ
with a Z
for "zulu"). This misnomer actually means "with respect for time zone". Incoming data with an offset from UTC or other time zone information is adjusted to UTC. The data's offset or time zone is not preserved.
The SQL data type TIMESTAMP WITHOUT TIME ZONE
(a.k.a. TIMESTAMP
) means the same as a LocalDateTime
in Java: no time zone at all. Not tied to the timeline. Any offset or time zone information with input data is ignored, no adjustment made.
Postgres doc may help explain.
When retrieving such a value from the database, the UTC value may be adjusted to a particular time zone by your admin tool (such as pgAdmin in Postgres) or your database driver or by your app.
In your app it is generally best to keep your date-time values in UTC as much as possible. Do nearly all of your storage, business logic, and data exchange in UTC. Only adjust to a particular time zone when expected by the user.
If you do have a LocalDateTime
object, and you want to assign it a time zone, here is some example code. We also adjust to get the very same moment as seen in Montréal and in UTC. First the example is shown in Joda-Time, then in java.time.
Example in Joda-Time 2.8.
LocalDateTime ldt = new LocalDateTime( "2015-07-14T10:00:00" ); // Nowhere in particular.
DateTimeZone zoneChicago = DateTimeZone.forID( "America/Chicago" );
DateTime dateTimeChicago = ldt.toDateTime( zoneChicago );
DateTime dateTimeMontreal = dateTimeChicago.withZone( DateTimeZone.forID( "America/Montreal" ) );
DateTime dateTimeUtc = dateTimeChicago.withZone( DateTimeZone.UTC );
Dump to console.
System.out.println( "LocalDateTime (nowhere): " + ldt );
System.out.println( "Chicago: " + dateTimeChicago );
System.out.println( "Montréal: " + dateTimeMontreal );
System.out.println( "UTC: " + dateTimeUtc);
When run.
LocalDateTime (nowhere): 2015-07-14T10:00:00.000
Chicago: 2015-07-14T10:00:00.000-05:00
Montréal: 2015-07-14T11:00:00.000-04:00
UTC: 2015-07-14T15:00:00.000Z
Example in java.time of Java 8 Update 51.
LocalDateTime ldt = LocalDateTime.parse( "2015-07-14T10:00:00" ); // Nowhere in particular.
ZoneId zoneChicago = ZoneId.of( "America/Chicago" );
ZonedDateTime zdtChicago = ZonedDateTime.of( ldt, zoneChicago );
ZonedDateTime zdtMontreal = zdtChicago.withZoneSameInstant( ZoneId.of( "America/Montreal" ) );
ZonedDateTime zdtUtc = zdtChicago.withZoneSameInstant( ZoneOffset.UTC ); // ZoneOffset is a subclass of ZoneId.
Dump to console.
System.out.println( "LocalDateTime (nowhere): " + ldt );
System.out.println( "Chicago: " + zdtChicago );
System.out.println( "Montréal: " + zdtMontreal );
System.out.println( "UTC: " + zdtUtc);
When run.
LocalDateTime (nowhere): 2015-07-14T10:00
Chicago: 2015-07-14T10:00-05:00[America/Chicago]
Montréal: 2015-07-14T11:00-04:00[America/Montreal]
UTC: 2015-07-14T15:00Z
Upvotes: 5