Reputation: 1653
I'm using the java Calendar to create some date at midnight (GMT), then change the timezone to my local timezone to make sure the requested time would be 1:00 (GMT +1). The code below works, incl. 2 successful asserts.
TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT");
TimeZone TIMEZONE_LOCAL = TimeZone.getDefault(); // GMT + 1
private Calendar getMidnightGmtCalendarWithLocalTimezone() {
Calendar calendar = Calendar.getInstance(Locale.GERMAN);
calendar.set(2017, Calendar.JANUARY, 1, 0, 0, 0);
calendar.setTimeZone(TIMEZONE_GMT); // Probably not required
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY)); // Assert 1
// Log.i("TAG", "" + calendar.get(Calendar.HOUR_OF_DAY));
calendar.setTimeZone(TIMEZONE_LOCAL);
assertEquals(1, calendar.get(Calendar.HOUR_OF_DAY)); // Assert 2
return calendar;
}
Now I delete the 1st assert, and I'd expect nothing changes. Reality: the 2nd assert fails now! I've tried to understand the implementation of .get(), and apparently it does also compute some time, so it's not just collecting the value. Still I don't get why my 2nd assert is failing.
The 2nd assert succeeds again when I uncomment the Log line (just to be sure the problem arises from the calendar.get(), and not the assert!)
So first question: why does this happen? Second qeustion: how can I make sure I've got a Calendar instance with the Local timezone when I've set the time on midnight for Timezone GMT? (in other words, how can I correctly convert timezones?)
EDIT: I'm using this on Android, so cannot use Java8. Joda time (as suggested below) is a great alternative, and I'll definately look into that. However, as answer for my question I'd like to know HOW this works with Java7 Calendar, because I'm bound to that code for the moment.
Upvotes: 1
Views: 5944
Reputation: 338181
Use java.time, not java.util.Calendar
.
Instant.parse( "2017-01-01T00:00:00Z" ) // UTC.
.atZone( ZoneId.of( "Africa/Algiers" ) ) // Time zone with offset +01:00.
Calendar
The old date-time classes including Calendar
are troublesome, poorly designed, confusing, and flawed. Avoid them. No point in trying to understand their overwrought API.
The legacy date-time classes are supplanted by the java.time package of classes built into Java 8 and later.
Instant
The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.parse( "2017-01-01T00:00:00Z" );
instant.toString(): 2017-01-01T00:00:00Z
ZonedDateTime
You asked to see that in a time zone that is one hour ahead of UTC. So let's try applying time zone Africa/Algiers
to get a ZonedDateTime
.
ZoneId z = ZoneId.of( "Africa/Algiers" );
ZonedDateTime zdt = instant.atZone( z );
zdt.toString(): 2017-01-01T01:00+01:00[Africa/Algiers]
OffsetDateTime
If you want to construct your UTC date-time via arguments rather than parsing a string (as seen above), pass arguments to a factory method of OffsetDateTime
. Specify the convenient constant, ZoneOffset.UTC
. Months are numbered sanely, 1-12 for January-December, in contrast to Calendar
.
OffsetDateTime odt = OffsetDateTime.of( 2017 , 1 , 1 , 0 , 0 , 0 , 0 , ZoneOffset.UTC ) ;
odt.toString(): 2017-01-01T00:00Z
Similar to above, we can adjust into a time zone. Adjustments are made for anomalies such as Daylight Saving Time (DST).
ZoneId z = ZoneId.of( "Africa/Algiers" );
ZonedDateTime zdt = odt.atZoneSameInstant( z );
zdt.toString(): 2017-01-01T01:00+01:00[Africa/Algiers]
See this code run line in IdeOne.com.
Locale
Regarding the use of Locale
in the Question’s code… Locale
is completely orthogonal to time zone, separate and distinct. A Locale
object affects only the formatting of a string being generated to represent the date-time value.
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
Upvotes: 6
Reputation: 30809
So, I have run the following code snippet:
public static void main(String[] args) throws Exception {
TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT");
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin"));
calendar.set(2017, Calendar.JANUARY, 1, 0, 0, 0);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
calendar.setTimeZone(TIMEZONE_GMT); // Probably not required
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
}
On executing it, I got 0
and 23
in the console which is correct.
I then commented out the first Sysout
statement and it printed 0
(I expected it to print 23
).
On exploring a bit more, I found this SO answer which correctly explains that calendar object only stores the milliseconds and TimeZone
is not taken into account.
So, if I were you, I would not worry too much about asserts
, I would make sure I get a Calendar
instance with required timezone (or default timezone if default timezone is set to the timezone you need), e.g:
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin"));
//OR
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin"));
//OR
Calendar calendar = Calendar.getInstance(); //If default timezone is Europe/Berlin
Upvotes: 0