Reputation: 15385
I have the following:
val startWithTZ = new DateTime("2016-10-01T00:00:00", DateTimeZone.UTC)
val endWithTZ = new DateTime("2016-10-01T11:00:00", DateTimeZone.UTC)
val intervalWithTZ = new Interval(startWithTZ, endWithTZ)
val startWithoutTZ = new DateTime("2016-10-01T00:00:00")
val endWithoutTZ = new DateTime("2016-10-01T11:00:00")
val intervalWithoutTZ = new Interval(startWithoutTZ, endWithoutTZ)
With this, I now do the following:
val utcInterval = new Interval(intervalWithoutTZ.getStart.toDateTime(DateTimeZone.UTC),
intervalWithoutTZ.getEnd.toDateTime(DateTimeZone.UTC))
What I see with the utcInterval is not what I was expecting:
2016-09-30T22:00:00.000Z/2016-10-01T09:00:00.000Z
Why is this? I was expecting the utcInterval to be:
2016-10-01T00:00:00.000Z/2016-10-01T11:00:00.000Z
Here is what I get when I use the Scala REPL:
scala> import org.joda.time._
import org.joda.time._
scala> val startWithoutTZ = new DateTime("2016-10-01T00:00:00")
startWithoutTZ: org.joda.time.DateTime = 2016-10-01T00:00:00.000+02:00
scala> val endWithoutTZ = new DateTime("2016-10-01T11:00:00")
endWithoutTZ: org.joda.time.DateTime = 2016-10-01T11:00:00.000+02:00
scala> val intervalWithoutTZ = new Interval(startWithoutTZ, endWithoutTZ)
intervalWithoutTZ: org.joda.time.Interval = 2016-10-01T00:00:00.000+02:00/2016-10-01T11:00:00.000+02:00
scala> val utcInterval = new Interval(intervalWithoutTZ.getStart.toDateTime(DateTimeZone.UTC),
| intervalWithoutTZ.getEnd.toDateTime(DateTimeZone.UTC))
utcInterval: org.joda.time.Interval = 2016-09-30T22:00:00.000Z/2016-10-01T09:00:00.000Z
scala>
Why is it not?
Upvotes: 0
Views: 514
Reputation: 462
I guess your local timezone is UTC-2 at the moment, and since you give no Timezone info to the "withoutTZ" variables, Joda-Time give it the default (local) timezone.
In order to change the timezone à posteriori, you have to convert your DateTime
to a LocalDateTime
object, and then convert it back to DateTime
with the correct timezone, here's an example reusing your code in Java.
EDIT2:
As suggested by @JonSkeet, it's better to directly use DateTime.withZoneRetainFields(DateTimeZone)
on your DateTime
object instead of converting it.
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
public class HelloWorld {
public static void main(String[] args) {
DateTime startWithTZ = new DateTime("2016-10-01T00:00:00", DateTimeZone.UTC);
DateTime endWithTZ = new DateTime("2016-10-01T11:00:00", DateTimeZone.UTC);
Interval intervalWithTZ = new Interval(startWithTZ, endWithTZ);
DateTime startWithoutTZ = new DateTime("2016-10-01T00:00:00");
DateTime endWithoutTZ = new DateTime("2016-10-01T11:00:00");
Interval intervalWithoutTZ = new Interval(startWithoutTZ, endWithoutTZ);
Interval utcInterval =
new Interval(intervalWithoutTZ.getStart().withZoneRetainFields(DateTimeZone.UTC),
intervalWithoutTZ.getEnd().withZoneRetainFields(DateTimeZone.UTC));
System.out.println(utcInterval);
}
}
Output:
2016-10-01T00:00:00.000Z/2016-10-01T11:00:00.000Z
EDIT:
As @JonSkeet mentionned in his comment, you are getting weird results, consult his comment to correct your output.
Upvotes: 0
Reputation: 1500515
Logically, I've always felt that an Interval
should be between two Instant
values rather than two DateTime
values. However, the Joda Time API is what it is.
When you create an Interval
from two DateTime
values, the end time is converted into the time zone of the start time. In your case, this doesn't actually need to do anything because for intervalWithTZ
they're both in UTC, and for intervalWithoutTZ
they're both in your system default time zone. Your naming is inaccurate here, because all the values are in a time zone, even though you didn't specify one. A DateTime
always has a time zone; for a value without a time zone you want LocalDateTime
instead.
So, we can get rid of your intervalWithoutTZ
and simply write your code as:
DateTime startInDefaultZone = new DateTime("2016-10-01T00:00:00")
DateTime endInDefaultZone = new DateTime("2016-10-01T11:00:00")
val utcInterval = new Interval(startInDefaultZone.toDateTime(DateTimeZone.UTC),
endInDefaultZone.toDateTime(DateTimeZone.UTC));
That won't change the result at all... but the basic point is that you're converting a DateTime
of "2016-10-01T00:00:00 in your system default time zone" into UTC... and presumably your default time zone is 2 hours behind UTC, so 11am in UTC is 9am in your local time, etc.
In short, we can actually get rid of intervals entirely here - what you're seeing can be shown more simply as:
DateTime startInDefaultZone = new DateTime("2016-10-01T00:00:00")
DateTime startInUtc = startInDefaultZone.toDateTime(DateTimeZone.UTC);
You appear to expect that to be 2016-10-01T00:00:00Z, but it's actually "the instant in time represented by startInDefaultZone
, but expressed in UTC" which is 2016-10-01T02:00:00Z on your machine.
If you really want to "change time zone without changing local values" you could use withZoneRetainFields
, e.g.
DateTime startInDefaultZone = new DateTime("2016-10-01T00:00:00")
DateTime startInUtc = startInDefaultZone.withZoneRetainFields(DateTimeZone.UTC);
However, that usually means you've used the wrong type to start with, and should have been using LocalDateTime
. It's rarely a useful operation in a well-designed system.
Upvotes: 1