Given that my default TimeZone is Europe/Paris:
System.out.println("Current Timezone: " + TimeZone.getDefault());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
String dateStrIn = "2016-08-01T00:00:00Z";
Date date = dateFormat.parse(dateStrIn);
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
The output is:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T00:00:00Z
Date.toString() Mon Aug 01 02:00:00 CEST 2016
Output date String: 2016-08-01T02:00:00+02
Now, I repeat the execution but setting a different default TimeZone (UTC):
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
...
And this is the output:
Current Timezone: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Input date String: 2016-08-01T00:00:00Z
Date.toString() Mon Aug 01 00:00:00 UTC 2016
Output date String: 2016-08-01T02:00:00+02
Date.toString() has correctly taken into account the TimeZone change. However, the String gotten from dateFormat.format(date); is still showing +02 instead of Z or +00, why?
Using standard Java API, is there any way to force the formatting according to a selected TimeZone?
UPDATE:
Jesper's solution works in almost all cases, but I have come across this one (using Canary Island's summer time zone WEST) in which it doesn't:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
String dateStrIn = "2016-08-01T08:00:00 WEST";
Date date = dateFormat.parse(dateStrIn);
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
Output:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T08:00:00 WEST
Date.toString() Mon Aug 01 09:00:00 CEST 2016
Output date String: 2016-08-01T08:00:00 WEST
You can see that Output date String is still expressed in WEST.
If, for instance, I change the initial dateStrIn to: String dateStrIn = "2016-08-01T08:00:00 GMT";, then the Output date String is expressed in CEST as espected:
Output date String: 2016-08-01T10:00:00 CEST
Could it be a bug?
UPDATE 2:
Another example
Default TimeZone for both Date and SimpleDateFormat: "Europe/Paris"
Input String: "2016-08-01T08:00:00 WET"
Output:
Date.toString() Mon Aug 01 10:00:00 CEST 2016
Output date String: 2016-08-01T09:00:00 WEST
Notice that dateFormat.format(date); has produced a WEST date string. From WET -> WEST when it should be WET -> CEST.
Instead of setting the default time zone, set the time zone on your SimpleDateFormat object:
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
This will make the SimpleDateFormat object format your Date object in the UTC time zone.
If you're using Java 8, consider using the new date and time API in the package java.time instead of the old java.util.Date and java.text.SimpleDateFormat.
You torture yourself by using the troublesome old legacy date-time classes now outmoded by the java.time framework.
tl;dr
ZonedDateTime.ofInstant( Instant.parse( "2016-08-01T00:00:00Z" ) , ZoneId.of( "Europe/Paris" ) )
java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat. The Joda-Time team also advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
Instant
The Z on the end of your input string 2016-08-01T00:00:00Z is short for Zulu and means UTC. Represented in java.time by the Instant class with a resolution up to nanoseconds.
Instant instant = Instant.parse ( "2016-08-01T00:00:00Z" );
ZonedDateTime
Specify a time zone via ZoneId to get a ZonedDateTime.
In the summer, Paris time is two hours ahead of UTC. So the same moment as seen on a clock on the wall in Paris is 2 AM rather than midnight, on same date of August 1st.
ZoneId zoneId_Paris = ZoneId.of ( "Europe/Paris" );
ZonedDateTime zdt_Paris = ZonedDateTime.ofInstant ( instant , zoneId_Paris );
You can adjust into another time zone such as Atlantic/Canary. For this date in the summer, the time is one hour ahead of UTC. Result is 1 AM rather than midnight.
ZoneId zoneId_Canary = ZoneId.of ( "Atlantic/Canary" );
ZonedDateTime zdt_Canary = zdt_Paris.withZoneSameInstant ( zoneId_Canary );
Dump to console.
System.out.println ( "instant: " + instant + " | zdt_Paris: " + zdt_Paris + " | zdt_Canary: " + zdt_Canary );
instant: 2016-08-01T00:00:00Z | zdt_Paris: 2016-08-01T02:00+02:00[Europe/Paris] | zdt_Canary: 2016-08-01T01:00+01:00[Atlantic/Canary]
All three of these objects (UTC, Paris, Canary) represent the very same simultaneous moment in history, the same single point on the timeline. Each is viewed through the lens of a different wall-clock time.
Real Time Zones
Avoid 3-4 letter zone abbreviations such as WET and WEST. These are not real time zones, not standardized, and not even unique(!).
Europe/Paris and Atlantic/Canary are proper time zone names, in the format of continent/region.
The answer to this mystery was on the Javadoc:
DateFormat.parse(String source, ParsePosition pos)
This parsing operation uses the calendar to produce a Date. As a
result, the calendar's date-time fields and the TimeZone value may
have been overwritten, depending on subclass implementations. Any
TimeZone value that has previously been set by a call to setTimeZone
may need to be restored for further operations.
Setting the TimeZone after parsing works it out:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
String dateStrIn = "2016-08-01T08:00:00 WET";
Date date = dateFormat.parse(dateStrIn);
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
Right output:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T08:00:00 WET
Date.toString() Mon Aug 01 10:00:00 CEST 2016
Output date String: 2016-08-01T10:00:00 CEST
No, its not a bug.
You have to first understand how Date class works. It is nothing but a wrapper over the number of milliseconds since the epoch, expressed in long. Hence, whichever timezone it may be, the underlying value of the date object remains the same. You can never really change the timezone of a Date class. You can only represent a String format of a date instance using a SimpleDateFormat class. This representation might have different time zones, based on which you are using while creating the SimpleDateFormatobject.
Again, you need to check the toString method of the Date class. It always prints the date with the default time zone.
Edit
You should look at the SimpleDateFormat.parse() definition as well. The JDK says that,
The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
Related
What I mean is this. Suppose I have a date object that is May 1, 2018, 12:00AM. I would like to create a ZonedDateTime object that is May 1, 2018, 12:00AM but of ANOTHER time zone (say EST). Because if I pass in that date object into ZonedDateTime, it treats that date object as UTC (so it treats it as May 1 2018, 12:00AM GMT), and I want it to preserve that date field values but change the timzeone to EST (May 1st 2018, 12:00AM EST). Is there a way to do this in Java?
What you need is a LocalDate:
LocalDate date = LocalDate.of(2018, Month.MAY, 1);
This will be understood in all time zones and will never be anything else than May 1. The “Local” in this and other class names in java.time means “without timezone”.
If you do insist on a ZonedDateTime, the answer is withZoneSameLocal:
ZonedDateTime zdt = date.atStartOfDay(ZoneOffset.UTC);
ZonedDateTime inEst = zdt.withZoneSameLocal(ZoneId.of("Australia/Brisbane"));
System.out.println(inEst);
Output:
2018-05-01T00:00+10:00[Australia/Brisbane]
Don’t rely on EST or other three and four letter time zone abbreviations. EST, for example, is ambiguous (used both in North America and Australia) and is not a time zone (used less than half of the year). Instead give region/city, for example America/Atikokan.
If by “a date object” you meant an object of the outdated java.util.Date class (avoid them if you can, prefer the modern classes in java.time):
Date oldfashionedDate = // …;
OffsetDateTime dateTime = oldfashionedDate.toInstant().atOffset(ZoneOffset.UTC);
if (! dateTime.toLocalTime().equals(LocalTime.MIDNIGHT)) {
throw new IllegalStateException("java.util.Date was supposed to be at midnight in UTC but was " + dateTime);
}
LocalDate date = dateTime.toLocalDate();
System.out.println(date);
Output:
2018-05-01
I have a date string in CST (24 Hours) and I want to convert it to GMT (12 Hours).
I have a java method like below which works fine when my system time is Kolkata time. (System where I run the java method)
But when my system is in Shanghai time, the GMT time comes incorrect.
String inputDate = "01-19-2017 06:01 CST";
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
parsedInput = inputFormatter.parse(inputDate);
// parsedInput -> Tue Jan 19 06:01:00 CST 2017 -> When system time is Shanghai time
// parsedInput -> Thu Jan 19 17:31:00 IST 2017 -> When system time is Kolkata time
SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy hh:mm a Z");
TimeZone gmt = TimeZone.getTimeZone("GMT");
formatter.setTimeZone(gmt);
String formattedDate = formatter.format(parsedInput);
// formattedDate -> 01-18-2017 10:01 PM +0000 -> When system time is Shanghai time (Incorrect)
// formattedDate -> 01-19-2017 12:01 PM +0000 -> When system time is Kolkata time
Avoid pseudo time zones
The 3-4 letter time zone abbreviations such as CST are not actual time zones. They are not standardized. They are not even unique!
Your CST might be “China Standard Time” or might be “Central Standard Time” in the Americas, or might be something else. There is no way to know which one.
Another example: IST might mean “India Standard Time” or “Ireland Standard Time” or others.
Deciphering these pseudo-zones reliably is impossible. The Joda-Time library has a wise policy of refusing to even try. Unfortunately the java.time classes make a guess when parsing, but the result may not be the zone you expect.
So never use these useless pseudo-zones. Use a proper time zone name in the format of continent/region such as America/Chicago, Asia/Kolkata, Pacific/Auckland.
Avoid legacy date-time classes
You are using the troublesome old date-time classes that are now legacy, supplanted by the java.time classes.
Workaround
If you know all your inputs are intended for the same time zone, then lop off the pseudo-zone and process as a LocalDateTime, apply the intended zone as a ZoneId to produce a ZonedDateDate. From that you can extract an Instant for UTC time.
String input = "01-19-2017 06:01".replace( " " , "T" ) ; // Insert a “T” to comply with standard ISO 8601 format used by default in java.time.
LocalDateTime ldt = LocalDateTime.parse( input ); // Lacks any concept of time zone or offset-from-UTC. So *not* an actual moment on the timeline.
ZoneId z = ZoneId.of( "America/Chicago" ); // Assuming “CST” meant this zone in North America.
ZonedDateTime zdt = ldt.atZone( z ); // Assign a time zone to determine an actual moment on the timeline.
Instant instant = zdt.toInstant(); // Extract a value in UTC.
You may choose to see that same moment through the lens of the wall-clock time in India.
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) ) ;
Never rely on default zone
Your JVM’s current default time zone can be changed at any moment by any code in any thread of any app executing within that JVM. Since this can change at any moment you cannot rely on a certain value.
When you omit the optional argument for time zone, the date-time classes implicitly silently apply the current default time zone. So the solution is simple: Always specify the expected/desired time zone.
Notice how the code example above specifies the zone at every opportunity.
(Ditto for Locale, by the way. Specify explicitly rather than rely on current default.)
Your problem is the ambiguity of CST. In most cases SimpleDateFormat understands CST as Central Standard Time as you had expected. But when your computer is running Shanghai Time, this becomes the time zone of the formatter’s calendar, and then it suddenly understands CST as China Standard Time, the same as Shanghai time.
Therefore Darshan Mehta’s solution, setting inputFormatter’s time zone to something other than China Standard Time, works on my computer. I don’t know why it didn’t work on yours (I set it to TimeZone.getTimeZone("America/Chicago") to match the intended interpretation of CST).
The correct and good solution, though, is to avoid the three and four letter time zone abbreviations completely. Yours is just one example out of many where they cause trouble. If you can, give your input string with an explicit zone offset from UTC, such is never ambiguous:
String inputDate = "01-19-2017 06:01 -0600";
Now your code works as expected no matter your computer’s time zone setting. The Z in the pattern string you already have matches -0600 nicely.
All of this said, if you can use the Java 8 date and time classes, you can do yourself a favour of switching over to them. I hesitate to call them “Java 8 date and time classes” because they have also been backported to Java 6 and 7, so if you can live with a library dependency until you get to Java 8, you should have every chance. The newer classes are significantly more programmer-friendly and convenient to work with. A simple example to get you started:
String inputDate = "01-19-2017 06:01 -0600";
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm Z");
ZonedDateTime parsedInput = ZonedDateTime.parse(inputDate, inputFormatter);
OffsetDateTime gmtTime = parsedInput.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy hh:mm a Z");
String formattedDate = gmtTime.format(formatter);
System.out.println(formattedDate); // prints 01-19-2017 12:01 PM +0000
Try setting the TimeZone to inputFormatter as well, e.g.:
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
inputFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
If you don't specify the time zone, it takes the default value into account which is Asia/Shanghai in your case and hence, incorrect result.
You can reproduce it in your current system by replacing GMT with Asia/Shanghai in the above snippett
I am trying to parse date (2015-06-25T00:00:00.000Z) coming from MongoDB.
try {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
Date date = format.parse("2015-06-25T00:00:00.000Z");
return new Date().after(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
Where am I wrong?
Your pattern accepts only second digits but your string has fractional seconds as well
yyyy-MM-dd'T'HH:mm:ss 'Z'
2015-06-25 T 00:00:00.000 Z
You need to use
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
Do not ignore the Z
Your formatting pattern has single quote marks around the Z. That means to interpret the "Z" as mere text, to expect it but then ignore it. Very bad, as that Z has meaning: short for Zulu, and means UTC time zone.
By ignoring the Z, parsing will implicitly apply your JVM’s current default time zone. Your input meant the first moment of the day in UTC (stroke of midnight), but in the following code you can see the value is mis-interpreted as midnight in my default zone of America/Los_Angeles -- an error of 7 hours (my zone’s current offset from UTC).
try {
java.text.DateFormat format = new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss'Z'" , Locale.ENGLISH );
java.util.Date date = format.parse ( "2015-06-25T00:00:00Z" );
System.out.println ( "date: " + date );
} catch ( ParseException e ) {
e.printStackTrace ();
}
In this output, PDT means the Daylight Saving Time (DST) version of Pacific time (more accurately named as America/Los_Angeles). Note the time is 00:00:00 but that is wrong -- should have been seven hours earlier on the previous day as my zone is behind UTC.
date: Thu Jun 25 00:00:00 PDT 2015
java.time
This work would be much easier with the java.time framework built into Java 8 and later. For Java 6 & 7, use the back-port, ThreeTen-Backport. For Android, the adaptation of that back-port, ThreeTenABP.
The java.time classes use ISO 8601 format by default when parsing/generating textual representations of date-time values. Your input happens to comply fully with ISO 8601. So no need to specify a formatting pattern.
An Instant is a moment on the timeline in UTC.
String input = "2015-06-25T00:00:00Z";
Instant instant = Instant.parse ( input );
Apply a proper time zone name if desired.
ZoneId zoneId = ZoneId.of ( "America/Los_Angeles" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Dump to console.
System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt );
input: 2015-06-25T00:00:00Z | instant: 2015-06-25T00:00:00Z | zdt: 2015-06-24T17:00-07:00[America/Los_Angeles]
Note how in this output, the same input is now correctly adjusted to my America/Los_Angeles time zone by moving back seven hours into the previous date.
Convert to/from java.time
Avoid using java.util.Date. The old date-time classes really are that bad. But if required, you can convert between java.time types and the old types. In this case, by using the count-from-epoch in milliseconds. Be aware that you may lose a bit of data, going from the nanoseconds resolution to milliseconds in java.util.Date (not in this case, but perhaps in other cases).
java.util.Date utilDate = new java.util.Date( instant.toEpochMilli() );
My understanding is that PST differs from GMT / UTC by 8 hours. However, when I print this out, I find only a difference of 7 hours. Can you explain what I have done incorrectly here?
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
Date date = sdf1.parse("2014-05-01 13:31:03.7");
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd_HHmmssS");
df.setTimeZone(TimeZone.getTimeZone("PST"));
System.out.println(df.format(date));
df.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(df.format(date));
df.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(df.format(date));
Prints:
20140501_1331037
20140501_2031037
20140501_2031037
I assume you are doing PST in summer/daylights saving time when it is GMT+7. Try the middle of winter.
From http://wwp.greenwichmeantime.co.uk/time-zone/usa/pacific-time/
When does Pacific Time change to Daylight Saving Time?
In most states
in the USA and in most provinces in Canada, Daylight-Saving Time (DST)
is observed. During DST PT or PDT is 7 hours behind Greenwich Mean
Time (GMT-7).
After the Summer months Pacific Time is shifted back by 1 hour to US
Pacific Standard Time (PST) or (GMT-8).
The schedule for the states of the United States that adopt daylight
saving time are:
2 a.m. on the Second Sunday in March to
2 a.m. on the First Sunday of
November.
You have not done anything incorrectly here. If you add the timezone in to the output format:
SimpleDateFormat df = new SimpleDateFormat("HH mm ssS Z z");
you can see the output is actually PDT (daylight savings) and not PST (regular)
10 31 037 -0700 PDT
17 31 037 +0000 GMT
17 31 037 +0000 UTC
12 31 037 -0500 EST
17 31 037 +0000 GMT
May is in Daylight saving time.
Here is the solution using Joda-Time 2.6.
String inputRaw = "2014-05-01 13:31:03.7";
String input = inputRaw.replace( " ", "T" ); // Adjust input to comply with the ISO 8601 standard.
DateTimeZone zone = DateTimeZone.UTC;
DateTime dateTime = new DateTime( input, zone ); // The input lacks an offset. So specify the time zone by which to interpret the parsed input string. The resulting DateTime is then adjusted to the JVM’s current default time zone. So, *two* time zones were used in this line of code, one explicit, the other implicit.
DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC ); // Adjust to UTC.
DateTime dateTimeLosAngeles = dateTime.withZone( DateTimeZone.forID( "America/Los_Angeles" ) ); // Adjust to US west coast time zone. DST is automatically applied as needed.
DateTime dateTimeKolkata = dateTime.withZone( DateTimeZone.forID( "Asia/Kolkata" ) ); // Adjust to India time, five and a half hours ahead of UTC.
All of these DateTime objects represent the same moment in the timeline of the history of the universe. Each has a different time-of-day (and possibly different date) to suit the "wall time" on clocks as likely seen by people in a particular locality.
I've been trying to convert the time since epoch until today and display it in Eastern Standard Time. Here is what outputs on the remote machine (it's remotely hosted):
Date now = new Date(System.currentTimeMillis());
System.out.println(now.toString());
// Thu Apr 24 14:36:11 MST 2014
No idea what MST is, but I want to get the current milliseconds since epoch in EST, and display the result in EST.
No matter what I do, I can't get daylights savings to work (it's currently Daylights Savings Time in the EST Time Zone); I either end up in PST, GMT or UTC, and when I do get "EST" it's either some random value or 1 hour behind or 3 hours behind.
I would like the output to be formatted using this DateFormat:
DateFormat EXPIRE_FORMAT = new SimpleDateFormat("MMM dd, yyyy h:mm a z");
Just set the time zone you want the time to be displayed in using DateFormat#setTimeZone(TimeZone)
Date now = new Date(System.currentTimeMillis());
DateFormat EXPIRE_FORMAT = new SimpleDateFormat("MMM dd, yyyy h:mm a z");
EXPIRE_FORMAT.setTimeZone(TimeZone.getTimeZone("America/Montreal")); // or whatever relevant TimeZone id
System.out.println(EXPIRE_FORMAT.format(now));
AFAIK, there is no EST currently. It's all EDT in Spring.
The above prints
Apr 24, 2014 5:53 PM EDT
The comments and the answer by Sotirios Delimanolis are correct.
Avoid 3 or 4 Letter Time Zone Codes
You should avoid the 3 or 4 letter codes for time zones as they are neither standardized nor unique. Instead use proper time zone names, usually a continent+city.
Avoid j.u.Date
The java.util.Date and .Calendar & SimpleDateFormat classes bundled with Java are notoriously troublesome. Use a decent date-time library with an updated time zone database. For Java, that means either Joda-Time or the new java.time package in Java 8 (inspired by Joda-Time).
Avoid Milliseconds-Since-Epoch
I suggest you avoid working with milliseconds since epoch. Gets confusing fast as the number is meaningless when read by a human. Let the date-time library manage the milliseconds for you.
Specify Time Zone
Generally best to specify the desired/intended time zone. If you omit the time zone, all the major date-time libraries (java.util.Date, Joda-Time, java.time) apply the JVM's default time zone.
Joda-Time Example
Example code in Joda-Time 2.3.
DateTimeZone timeZoneToronto = DateTimeZone.forID( "America/Toronto" );
DateTime dateTimeToronto = new DateTime( timeZoneToronto ); // Current moment.
DateTime dateTimeUTC = dateTimeToronto.withZone( DateTimeZone.UTC );
DateTime dateTimeParis = dateTimeToronto.withZone( DateTimeZone.forID( "Europe/Paris" ) );
If you really want the milliseconds since epoch, call the getMillis method. In example code above, all three DateTime objects have the same number of milliseconds-since-epoch.
long millis = dateTimeToronto.getMillis();
If you need a java.util.Date for use with other classes…
java.util.Date date = dateTimeToronto.toDate();
While Joda-Time uses the ISO 8601 standard formats as its defaults, you may specify other formats for generating strings.
DateTimeFormatter formatter = DateTimeFormat.forPattern( "MMM dd, yyyy h:mm a z" );
String output = formatter.print( dateTimeToronto );