tl;dr
No shift
java.util.Date date = myGregorianCalendar.getTime() ; // Same moment, same point on the timeline. `Date` is in UTC, `GregorianCalendar` may be in some other time zone.
String output = date.toString() ; // This new string is a lie, dynamically applying the JVM’s current time zone while the `Date` is actually in UTC, always, by definition.
There is no shift. Calling GregorianCalendar.getTime
produced a java.util.Date
. The Date
object is always in UTC, by definition. Unfortunately the Date::toString
method lies, injecting the JVM’s current default time zone while producing a String.
Be clear that the Date
and String
are two separate distinct objects. One holds a moment in UTC, the other is a textual representation of that moment after being adjusted into some time zone.
The GregorianCalendar
, the Date
, and the String
all represent the same moment, same point on the timeline, but different wall-clock time.
Use java.time for clarity
Date-time handling is much easier and clear if you use modern java.time classes rather than awful mess that is the legacy classes Date
, Calendar
, and GregorianCalendar
.
java.time
The GregorianCalendar
class is one of the troublesome old date-time classes supplanted by the java.time classes built into Java 8 and later. Much of the java.time functionality is back-ported to Java 6 and Java 7 in the ThreeTen-Backport project.
Convert from legacy class to modern java.time using new methods added to the old classes, specifically GregorianCalendar::toZonedDateTime
. If using the back-port, use the DateTimeUtils
class.
ZonedDateTime zdt = DateTimeUtils.toZonedDateTime( myCalendar ) ;
A ZonedDateTime
object is the replacement for GregorianCalendar
. This class is conceptually the combination of a Instant
(a moment in UTC) with an assigned time zone, a ZoneId
object.
If you want the same moment as seen in UTC, extract the Instant
.
Instant instant = zdt.toInstant() ;
You can convert back to a java.util.Date
from an Instant
, for compatibility with old code not yet updated to java.time.
java.util.Date date = DateTimeUtils.toDate( instant ) ; // Convert from modern `Instant` class to legacy `Date` class.
If you want just the date portion, without the time-of-day and without the time zone, create a LocalDate
object.
LocalDate ld = zdt.toLocalDate() ;
The problem is calling Calendar.getTime() gives a different date, offset by (I think) our timezone. So the next day by 8 hours.
How can we do this without this shift?
…
And the getTime() is returning a Date of "Thu Apr 30 18:00:00 MDT 1992" - I'm in the Mountain Time Zone.
What you are seeing is an illusion. The GregorianCalendar::getTime
method returns to you a java.util.Date
object. Then you implicitly called toString
on that Date
object. That java.util.Date::toString
method has an unfortunate behavior of applying your JVM’s current default time zone while generating a string to represent its value. The value of the Date
is actually UTC, always UTC, by definition. That toString
method creates the illusion that the Date
harbors a time zone when in fact it does not†.
†Actually, the java.util.Date
class does harbor a time zone, but deep within its source code. Used for stuff like the equals
method implementation. But the class has no getter or setter, so it seems invisible to us. And in the context of your Question, is irrelevant.
Confusing? Yes. This is one of many reasons to avoid these terrible old date-time classes. Use only java.time classes instead.
About java.time
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.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
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.