A java.util.Date
object has no timezone information. It has only a long
value, which is the number of milliseconds from 1970-01-01T00:00:00Z
(also known as "unix epoch" or just "epoch"). This value is absolutely independent of timezone (you can say "it's in UTC" as well).
When you call Logger.info("finalDate:: " + finalDate);
, it calls the toString()
method of java.util.Date
, and this method uses the system's default timezone behind the scenes, giving the impression that the date object itself has a timezone - but it doesn't.
Check the values of finalDate.getTime()
and currentdate.getTimeInMillis()
, you'll see they are almost the same - "almost" because the SimpleDateFormat
doesn't have the fraction of seconds, so you're losing the milliseconds precision (format
method creates a String
without the milliseconds, and the parse
method sets it to zero when the field is not present). If I change the formatter to this, though:
// using ".SSS" to don't lose milliseconds when formatting
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
The output is:
Local:: Mon Jul 03 17:34:34 UTC 2017
America/Denver:: 2017-07-03 11:34:34.508
finalDate:: Mon Jul 03 17:34:34 UTC 2017
And both finalDate.getTime()
and currentdate.getTimeInMillis()
will have exactly the same values (Note that Date.toString()
doesn't print the milliseconds, so you can't know what's their value - only by comparing getTime()
values you know if they are the same).
Conclusion: just change your formatter to use the milliseconds (.SSS
) and parsing/formatting will work. The fact that it shows the dates in another timezone is an implementation detail (toString()
method uses system's default timezone), but the milliseconds value is correct.
If you want to get 11h at UTC, you must create another formatter and set its timezone to UTC:
DateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
finalDate = parser.parse(formattedDate);
Then, finalDate
's time will have the value of 11h at UTC:
finalDate:: Mon Jul 03 11:34:34 UTC 2017
New Java 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.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time
and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp
), but the classes and methods names are the same.
To do what you need, you can use a ZonedDateTime
(a date and time + a timezone) and convert to another timezone keeping the same date/time values:
// current date/time in Denver
ZonedDateTime denverNow = ZonedDateTime.now(ZoneId.of("America/Denver"));
// convert to UTC, but keeping the same date/time values (like 11:34)
ZonedDateTime converted = denverNow.withZoneSameLocal(ZoneOffset.UTC);
System.out.println(converted); // 2017-07-03T11:34:34.508Z
The output will be:
2017-07-03T11:34:34.508Z
If you want a different format, use a DateTimeFormatter
:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// pattern for day/hour
.appendPattern("EEE MMM dd HH:mm:ss ")
// UTC offset
.appendOffset("+HHMM", "UTC")
// year
.appendPattern(" yyyy")
// create formatter
.toFormatter(Locale.ENGLISH);
System.out.println(fmt.format(converted));
The output will be:
Mon Jul 03 11:34:34 UTC 2017
If you still need to use java.util.Date
, you can easily convert from/to the new API.
In Java >= 8:
// convert your Calendar object to ZonedDateTime
converted = currentdate.toInstant()
.atZone(ZoneId.of("America/Denver"))
.withZoneSameLocal(ZoneOffset.UTC);
// converted is equals to 2017-07-03T11:34:34.508Z
// from ZonedDateTime to Date and Calendar (date will be 11:34 UTC)
Date d = Date.from(converted.toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(d);
// to get a Date that corresponds to 11:34 in Denver
Date d = Date.from(converted.withZoneSameLocal(ZoneId.of("America/Denver")).toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(d);
In Java <= 7 (ThreeTen Backport), you can use the org.threeten.bp.DateTimeUtils
class:
// convert Calendar to ZonedDateTime
converted = DateTimeUtils.toInstant(currentdate)
.atZone(ZoneId.of("America/Denver"))
.withZoneSameLocal(ZoneOffset.UTC);
// converted is equals to 2017-07-03T11:34:34.508Z
// convert ZonedDateTime to Date (date will be 11:34 UTC)
Date d = DateTimeUtils.toDate(converted.toInstant());
Calendar c = DateTimeUtils.toGregorianCalendar(converted);
// to get a Date that corresponds to 11:34 in Denver
Date d = DateTimeUtils.toDate(converted.withZoneSameLocal(ZoneId.of("America/Denver")).toInstant());
Calendar c = DateTimeUtils.toGregorianCalendar(converted.withZoneSameLocal(ZoneId.of("America/Denver")));