When you do:
dateFormat.setTimeZone(TimeZone.getDefault());
And then parse the String
, you're saying to the formatter: "I'm gonna give you a String with a date and time in this timezone". As you used TimeZone.getDefault()
, it'll use the JVM's default timezone. So the first thing is to tell the formatter to use UTC when parsing:
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// use UTC to parse
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
....
date = dateFormat.parse(dateString);
I'm testing with dateString
equals to 2017-08-01 10:00:00
. So, the date
will be equivalent to 2017-08-01 10:00:00 UTC
. When you print the date with System.out.println
, it'll call the toString()
and convert it to the JVM's default timezone.
In the JVM I'm using, the default timezone is America/Sao_Paulo
, so when I call this:
System.out.println(date);
It prints:
Tue Aug 01 07:00:00 BRT 2017
Because in August 1st, 10 AM in UTC is equivalent to 7 AM in São Paulo. This will result will change according to your JVM's default timezone.
If you want to print this date but with the date and time equivalent to another timezone (other than UTC), you'll need another formatter with a different timezone set:
DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// use my default timezone to format (America/Sao_Paulo)
dateFormat2.setTimeZone(TimeZone.getDefault());
formattedDate = dateFormat2.format(date);
This will print:
2017-08-01 07:00:00
Instead of the default timezone, you can use whatever timezone you want (I'm using America/New_York
as an example):
DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// use New York timezone
dateFormat2.setTimeZone(TimeZone.getTimeZone("America/New_York"));
formattedDate = dateFormat2.format(date);
This will convert the date/time to New York's timezone:
2017-08-01 06:00:00
If you want to print the date/time in UTC, then you don't need to create another formatter, just use the first one (as it's already set to UTC).
The JVM's default timezone can be changed without notice, even at runtime, so it's better to use a specific one instead of just using getDefault()
.
Prefer to use 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 TimeZone.getAvailableIDs()
.
New Date/Time API
The old classes (Date
, Calendar
and SimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.
In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
This API is much easier to work with. First you create a org.threeten.bp.format.DateTimeFormatter
and parse the input String
to a org.threeten.bp.LocalDateTime
.
Then you convert it to UTC (creating a org.threeten.bp.ZonedDateTime
), and then to the timezone you want, and use another formatter to output it:
// date/time with optional milliseconds
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]");
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(dateString, parser);
// convert to UTC
ZonedDateTime z = dt.atZone(ZoneOffset.UTC);
// convert to another timezone
z = z.withZoneSameInstant(ZoneId.of("America/New_York"));
// format it
// date/time without milliseconds
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(fmt.format(z));
You can also use the default timezone using ZoneId.systemDefault()
, and get all the available timezones with ZoneId.getAvailableZoneIds()
.
If you want the date/time in UTC, you can skip the call to withZoneSameInstant()
.
To convert it to a java.util.Date
, you can use the org.threeten.bp.DateTimeUtils
class:
// convert ZonedDateTime to java.util.Date
Date d = DateTimeUtils.toDate(z.toInstant());