Reputation: 3898
In Order to show a reproducible scenario, I am doing the following
Get the current system time (local time)
Convert Local time to UTC // Works Fine Till here
Reverse the UTC time, back to local time. Followed 3 different approaches (listed below) but all the 3 approaches retains the time in UTC only.
{
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
String format = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat (format);
// Convert Local Time to UTC (Works Fine)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date gmtTime = new Date(sdf.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1
sdf.setTimeZone(TimeZone.getDefault());
localTime = new Date(sdf.format(gmtTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat
DateFormat df = new SimpleDateFormat (format);
df.setTimeZone(TimeZone.getDefault());
localTime = df.parse((df.format(gmtTime)));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
// Approach 3
Calendar c = new GregorianCalendar(TimeZone.getDefault());
c.setTimeInMillis(gmtTime.getTime());
System.out.println("Local Time " + c.toString());
}
Upvotes: 37
Views: 124669
Reputation: 338775
UPDATE: The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. See Tutorial by Oracle.
See my other Answer using the industry-leading java.time classes.
Normally we consider it bad form on StackOverflow.com to answer a specific question by suggesting an alternate technology. But in the case of the date, time, and calendar classes bundled with Java 7 and earlier, those classes are so notoriously bad in both design and execution that I am compelled to suggest using a 3rd-party library instead: Joda-Time.
Joda-Time works by creating immutable objects. So rather than alter the time zone of a DateTime object, we simply instantiate a new DateTime with a different time zone assigned.
Your central concern of using both local and UTC time is so very simple in Joda-Time, taking just 3 lines of code.
org.joda.time.DateTime now = new org.joda.time.DateTime();
System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
Output when run on the west coast of North America might be:
Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00
UTC (Zulu) time zone: 2013-10-15T09:45:30.801Z
Here is a class with several examples and further comments. Using Joda-Time 2.5.
/**
* Created by Basil Bourque on 2013-10-15.
* © Basil Bourque 2013
* This source code may be used freely forever by anyone taking full responsibility for doing so.
*/
public class TimeExample {
public static void main(String[] args) {
// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 and earlier.
// http://www.joda.org/joda-time/
// Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
// JSR 310 was inspired by Joda-Time but is not directly based on it.
// http://jcp.org/en/jsr/detail?id=310
// By default, Joda-Time produces strings in the standard ISO 8601 format.
// https://en.wikipedia.org/wiki/ISO_8601
// You may output to strings in other formats.
// Capture one moment in time, to be used in all the examples to follow.
org.joda.time.DateTime now = new org.joda.time.DateTime();
System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
// You may specify a time zone in either of two ways:
// • Using identifiers bundled with Joda-Time
// • Using identifiers bundled with Java via its TimeZone class
// ----| Joda-Time Zones |---------------------------------
// Time zone identifiers defined by Joda-Time…
System.out.println( "Time zones defined in Joda-Time : " + java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );
// Specify a time zone using DateTimeZone objects from Joda-Time.
// http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );
// ----| Java Zones |---------------------------------
// Time zone identifiers defined by Java…
System.out.println( "Time zones defined within Java : " + java.util.Arrays.toString( java.util.TimeZone.getAvailableIDs() ) );
// Specify a time zone using TimeZone objects built into Java.
// http://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html
java.util.TimeZone parisTimeZone = java.util.TimeZone.getTimeZone( "Europe/Paris" );
System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );
}
}
Upvotes: 4
Reputation: 338775
Instant.now() // Capture the current moment in UTC.
.atZone( ZoneId.systemDefault() ) // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
.toInstant() // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
.atZone( ZoneId.of( "Europe/Paris" ) ) // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
.toInstant() // And back to UTC again.
The modern approach uses the java.time classes that supplanted the troublesome old legacy date-time classes (Date
, Calendar
, etc.).
Your use of the word "local" contradicts the usage in the java.time class. In java.time, "local" means any locality or all localities, but not any one particular locality. The java.time classes with names starting with "Local…" all lack any concept of time zone or offset-from-UTC. So they do not represent a specific moment, they are not a point on the timeline, whereas your Question is all about moments, points on the timeline viewed through various wall-clock times.
Get the current system time (local time)
If you want to capture the current moment in UTC, use Instant
. The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Adjust into a time zone by applying a ZoneId
to get a ZonedDateTime
. Same moment, same point on the timeline, different wall-clock time.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, different wall-clock time.
As a shortcut, you can skip the usage of Instant
to get a ZonedDateTime
.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Convert Local time to UTC // Works Fine Till here
You can adjust from the zoned date-time to UTC by extracting an Instant
from a ZonedDateTime
.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Instant instant = zdt.toInstant() ;
Reverse the UTC time, back to local time.
As shown above, apply a ZoneId
to adjust the same moment into another wall-clock time used by the people of a certain region (a time zone).
Instant instant = Instant.now() ; // Capture current moment in UTC.
ZoneId zDefault = ZoneId.systemDefault() ; // The JVM's current default time zone.
ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;
ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ; // The JVM's current default time zone.
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ; // The JVM's current default time zone.
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;
Going back to UTC from a zoned date-time, call ZonedDateTime::toInstant
. Think of it conceptually as: ZonedDateTime = Instant + ZoneId.
Instant instant = zdtAuckland.toInstant() ;
All of these objects, the Instant
and the three ZonedDateTime
objects all represent the very same simultaneous moment, the same point in history.
Followed 3 different approaches (listed below) but all the 3 approaches retains the time in UTC only.
Forget about trying to fix code using those awful Date
, Calendar
, and GregorianCalendar
classes. They are a wretched mess of bad design and flaws. You need never touch them again. If you must interface with old code not yet updated to java.time, you can convert back-and-forth via new conversion methods added to the old classes.
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.
Upvotes: 3
Reputation: 2575
I also recommend using Joda as mentioned before.
Solving your problem using standard Java Date
objects only can be done as follows:
// **** YOUR CODE **** BEGIN ****
long ts = System.currentTimeMillis();
Date localTime = new Date(ts);
String format = "yyyy/MM/dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(format);
// Convert Local Time to UTC (Works Fine)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date gmtTime = new Date(sdf.format(localTime));
System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
+ gmtTime.toString() + "," + gmtTime.getTime());
// **** YOUR CODE **** END ****
// Convert UTC to Local Time
Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
+ fromGmt.toString() + "-" + fromGmt.getTime());
Output:
Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
Upvotes: 60
Reputation: 86286
I am joining the choir recommending that you skip the now long outdated classes Date
, Calendar
, SimpleDateFormat
and friends. In particular I would warn against using the deprecated methods and constructors of the Date
class, like the Date(String)
constructor you used. They were deprecated because they don’t work reliably across time zones, so don’t use them. And yes, most of the constructors and methods of that class are deprecated.
While at the time you asked the question, Joda-Time was (from all I know) a clearly better alternative, time has moved on again. Today Joda-Time is a largely finished project, and its developers recommend you use java.time
, the modern Java date and time API, instead. I will show you how.
ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());
// Convert Local Time to UTC
OffsetDateTime gmtTime
= localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
System.out.println("Local:" + localTime.toString()
+ " --> UTC time:" + gmtTime.toString());
// Reverse Convert UTC Time to Local time
localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
System.out.println("Local Time " + localTime.toString());
For starters, note that not only is the code only half as long as yours, it is also clearer to read.
On my computer the code prints:
Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]
I left out the milliseconds from the epoch. You can always get them from System.currentTimeMillis();
as in your question, and they are independent of time zone, so I didn’t find them intersting here.
I hesitatingly kept your variable name localTime
. I think it’s a good name. The modern API has a class called LocalTime
, so using that name, only not capitalized, for an object that hasn’t got type LocalTime
might confuse some (a LocalTime
doesn’t hold time zone information, which we need to keep here to be able to make the right conversion; it also only holds the time-of-day, not the date).
Your conversion from local time to UTC was incorrect and impossible
The outdated Date
class doesn’t hold any time zone information (you may say that internally it always uses UTC), so there is no such thing as converting a Date
from one time zone to another. When I just ran your code on my computer, the first line it printed, was:
Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000
07:25:45 CEST
is correct, of course. The correct UTC time would have been 05:25:45 UTC
, but it says CEST
again, which is incorrect.
Now you will never need the Date
class again, :-) but if you were ever going to, the must-read would be All about java.util.Date on Jon Skeet’s coding blog.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
Upvotes: 2
Reputation: 12940
You have a date with a known timezone (Here Europe/Madrid
), and a target timezone (UTC
)
You just need two SimpleDateFormats:
long ts = System.currentTimeMillis(); Date localTime = new Date(ts); SimpleDateFormat sdfLocal = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss"); sdfLocal.setTimeZone(TimeZone.getTimeZone("Europe/Madrid")); SimpleDateFormat sdfUTC = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss"); sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC")); // Convert Local Time to UTC Date utcTime = sdfLocal.parse(sdfUTC.format(localTime)); System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + utcTime.toString() + "-" + utcTime.getTime()); // Reverse Convert UTC Time to Locale time localTime = sdfUTC.parse(sdfLocal.format(utcTime)); System.out.println("UTC:" + utcTime.toString() + "," + utcTime.getTime() + " --> Local time:" + localTime.toString() + "-" + localTime.getTime());
So after see it working you can add this method to your utils:
public Date convertDate(Date dateFrom, String fromTimeZone, String toTimeZone) throws ParseException { String pattern = "yyyy/MM/dd HH:mm:ss"; SimpleDateFormat sdfFrom = new SimpleDateFormat (pattern); sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone)); SimpleDateFormat sdfTo = new SimpleDateFormat (pattern); sdfTo.setTimeZone(TimeZone.getTimeZone(toTimeZone)); Date dateTo = sdfFrom.parse(sdfTo.format(dateFrom)); return dateTo; }
Upvotes: 4
Reputation: 3149
I strongly recommend using Joda Time http://joda-time.sourceforge.net/faq.html
Upvotes: 1