Reputation: 4044
I am trying to parse date string with timezone using this code for tests:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZZZZZ", Locale.US);
Calendar calendar = Calendar.getInstance();
calendar.setTime(sdf.parse("2017-07-26T06:00-06:00"));
int offset = calendar.getTimeZone().getRawOffset();
I am trying to change timezone from -06
to +09
, but offset
always contains 10800000
.
How to parse date with timezone correctly (I need time and timezone both)?
Upvotes: 5
Views: 2903
Reputation:
Note: -06:00
is an offset, not a timezone - those 2 concepts are related, but they are different things (more on that below).
The problem with SimpleDateFormat
and Calendar
is that they use the system's default timezone, so even though you parse a date with a different offset (like -06:00
), the resulting Calendar
will have the default timezone (you can check what zone is by calling TimeZone.getDefault()
).
That's just one of the many problems and design issues of this old API.
Fortunately, there's a better alternative, if you don't mind adding a dependency to your project (in this case, I think it's totally worth it). In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP to make it work (more on how to use it here).
To work with offsets, you can use the org.threeten.bp.OffsetDateTime
class:
// parse the String
OffsetDateTime odt = OffsetDateTime.parse("2017-07-26T06:00-06:00");
This will parse all the fields correctly (date/time and offset). To get the offset value, similar to calendar.getTimeZone().getRawOffset()
, you can do:
// get offset in milliseconds
int totalSeconds = odt.getOffset().getTotalSeconds() * 1000;
I had to multiply by 1000 because calendar
returns the value in milliseconds, but ZoneOffset
returns in seconds.
To convert this to another offset (+09:00
), it's straightforward:
// convert to +09:00 offset
OffsetDateTime other = odt.withOffsetSameInstant(ZoneOffset.ofHours(9));
As I said, timezone and offset are different things:
-06:00
means "6 hours behind UTC" and +09:00
means "9 hours ahead UTC"So, the code above works fine if you're working with offsets and wants to convert to a different one. But if you want to work with a timezone, you must convert the OffsetDateTime
to a ZonedDateTime
:
// convert to a timezone
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// get the offset
totalSeconds = zdt.getOffset().getTotalSeconds() * 1000;
The getOffset()
method above will check the history of the specified timezone and get the offset that was active in that corresponding instant (so, if you take a date during DST, for example, the offset (and also date and time) will be adjusted accordingly).
The API uses IANA timezones names (always in the format Region/City
, like America/Sao_Paulo
or Europe/Berlin
).
Avoid using the 3-letter abbreviations (like CST
or PST
) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds()
.
You can also use the system's default timezone with ZoneId.systemDefault()
, but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
Upvotes: 2