I am facing an issue of Timezone. let me try to describe:
My Web application & DB is running on a server of having different timezone(Europe/Madrid i.e. UTC+1) than my local timezone(India/Kolkata i.e. UTC+5:30).
Lets say my local current time is: Wed Jan 15 14:35:00 IST 2014 and
that server current time is: Wed Jan 15 10:05:00 CET 2014 i.e. lag by 4:30 hrs.
Now when I am trying to save a date time on the DB using calendar selection from my web page (GUI) lets say, the date time I am selecting is: Wed Jan 15 18:30:00 , the final date that is getting saved in DB is becoming Wed Jan 15 14:00:00 (see the difference exactly 4:30 hrs lag time).
let me tell you the coding part of the above flow.
After selection from calendar of my web page -> passing the timestamp(in long) of the selected date time to my server side controller -> at server side, I am constructing java.util.Date object and saving to DB like below:
java.util.Date newDt = new java.util.Date(timeStampInMillis);
Here, timeStampInMillis is in long which I converted from the datetime selected in GUI.
Here it is saving the given timestamp in the current Timezone(UTC + 01:00) of that server and that is how the saved datetime is getting lagged by 4:30 Hrs.
But I want to save as it is i.e. the date time selected from calendar GUI.
I have some couple of solution like, instead of passing timestamp for constructing Date object in my server side controller, I can pass the String representation of the selected date time from my calendar GUI and then parse the format and save into DB.
But I am wondering if there is anything I can do with Timezone APIs.
Hope my problem is clear to you. If not, please clarify your doubt.
Need your help...
Thanks.
Unclear Question
Your question could be better written. You should try to narrow it down to a very specify example. You don't even specify the milliseconds value under discussion.
Server Time
Servers should almost always be set to a UTC/GMT time zone without Daylight Saving Time. On some systems such as Mac OS X, that is difficult. In that case, set time zone of machine to "Atlantic/Reykjavik" because Iceland stays on UTC/GMT year-round without any Daylight Saving Time nonsense.
Avoid java.util.Date
The java.util.Date & .Calendar classes bundled with Java are notoriously troublesome.
One of the pain points is that while a Date has no time zone assigned, its toString method uses the default time zone in rendering the string. So to the naïve programmer it seems like Date has a time zone when it does not.
Use either the Joda-Time library or the new java.time.* classes bundled with Java 8. Search StackOverflow for many examples of both.
Think Globally, Present Locally
Most of your business logic and your database storage should all be done in UTC/GMT (no time zone offset). A competent database such as Postgres will do so by default.
Only switch to a time zone for presentation to a user, as a general rule.
Time Zone
Always specify a time zone. Do not rely on default time zones as that causes surprises in production or any time machines change their time zone.
Avoid the three letter codes, as they are neither standardized nor unique. Use proper time zone names.
Look up your time zone names in a list like this one (slightly outdated, read details). Your mention of "India/Kolkata" in your question is, I believe, incorrect. Should be "Asia/Kolkata".
ISO 8601
If you must serialize, use only the ISO 8601 format. This format is human-readable, unambiguous, and clearly defined.
Example for India time zone: 2014-01-19T12:38:31+05:30
Example for UTC/GMT "Zulu": 2013-11-22T18:28.023Z
java.sql.* Classes
Use the java.sql.* classes for communicating to your database via JDBC.
You construct a java.sql.Timestamp object by passing the milliseconds since 1970 began. In Joda-Time, call getMillis to obtain a value to pass.
Avoid Milliseconds
Generally, I prefer to avoid dealing with milliseconds for tracking time. People tend to get into trouble since some systems track time from an epoch in seconds, milliseconds, or nanoseconds. Furthermore, there are many epochs in use, not always the Unix-style of first day of 1970.
I try to pass around either:
Date-time objects, such as Joda-Time DateTime instances
ISO 8601 strings.
Example Code
But if you are sure your milliseconds value represents the true number of milliseconds since the first day of 1970 in UTC/GMT, then use this kind of code with Joda-Time. Note the 'L' flagging the number as a long integer.
DateTime dateTime = new DateTime( 1390276603054L );
DateTime dateTimeSpain = dateTime.toDateTime( DateTimeZone.forID( "Europe/Madrid" ) );
DateTime dateTimeIndia = dateTime.toDateTime( DateTimeZone.forID( "Asia/Kolkata" ) );
DateTime dateTimeUtcGmt = dateTime.toDateTime( DateTimeZone.UTC );
// For database.
java.sql.Timestamp timestamp = new java.sql.Timestamp( dateTimeSpain.getMillis() );
Dump to console…
System.out.println( "dateTime (default time zone): " + dateTime );
System.out.println( "dateTimeSpain: " + dateTimeSpain );
System.out.println( "dateTimeIndia: " + dateTimeIndia );
System.out.println( "dateTimeUtcGmt: " + dateTimeUtcGmt );
System.out.println( "timestamp: " + timestamp ); // "toString" uses default time zone.
When run…
dateTime (default time zone): 2014-01-20T19:56:43.054-08:00
dateTimeSpain: 2014-01-21T04:56:43.054+01:00
dateTimeIndia: 2014-01-21T09:26:43.054+05:30
dateTimeUtcGmt: 2014-01-21T03:56:43.054Z
timestamp: 2014-01-20 19:56:43.054
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?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
timestamp in millis has no timezone. If you create new Date() at the same time in Kolkata and Madrid the Dates will have the same timestamp in millis. The problem can rise only when you interprete textual representation. Again, if textual represenation includes timezone like this Wed Jan 15 14:35:00 IST 2014 SimpleDateFormat.parse in Madrid and Kolkata will produce Date object with the same timestamp in millis.
Related
I am thinking of using the new java 8 Date Time API. I googled a bit and found jodaTime as good choice for java but still kind of interested to see how this new API works.
I am storing all time in UTC values in my datastore and will be converting them to Local Time Zone specific value based on user's timezone. I can find many articles showing how to use new Java Date Time API. However I am not sure if the API will take care of DST changes ?
Or do we have any better way of handling Date ?
I am just learning the new Date API , so thought of hearing your thoughts on handling the DateTime and displaying it on the basis of Users TimeZone.
It depends on which class you use:
Instant is an instantaneous point on the global time-line (UTC), and is unrelated to time-zone.
LocalDate and LocalDateTime have no concept of time-zone, but calling now() will of course give you your correct time.
OffsetDateTime has a time-zone, but doesn't support Daylight Savings Time.
ZonedDateTime has full time-zone support.
Converting between them usually requires a time-zone, so to answer your question:
Yes, Java 8 Date/Time can take care of DST, if you use it right.
The Answer by Andreas is spot-on correct.
Example Code
Let's test it with some code. DST in the United States & Canada expires this year at 02:00 on November 1, 2015.
Let‘s start with 1 AM in “local” date-time, meaning not tied to the timeline and ignoring the issue of time zones. Add an hour, and we get 2 AM. Makes sense.
LocalDateTime localDateTime = LocalDateTime.of( 2015 , Month.NOVEMBER , 1 , 1 , 0 ); // 1 AM anywhere. Not tied the timeline nor to any time zone.
LocalDateTime localDateTimeOneHourLater = localDateTime.plusHours( 1 ); // 2 AM anywhere, in no particular time zone, ignoring DST.
Next we get specific, with a particular time zone. We take that 1 AM anywhere and put it into the time zone of America/Los_Angeles (west coast of United States).
ZoneId zoneId_LosAngeles = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime before = localDateTime.atZone( zoneId_LosAngeles ); // Assign a time zone, tying this vague date-time idea/generality to an actual moment on the time line.
Now add an hour, and see what we get. If DST is ignored, we’ll get 2 AM. If DST is respected, we’ll get 1 AM… when reaching 2 AM the wall-clock time jumps back to 1 AM but with a new offset-from-UTC. This is colloquially known as "fall back" in the fall (autumn).
ZonedDateTime after = before.plusHours( 1 ); // 2 AM? Nope, 1 AM because DST Daylight Saving Time expires at 2 AM Nov 1, 2015.
Dump to console.
System.out.println( "localDateTime : " + localDateTime );
System.out.println( "localDateTimeOneHourLater : " + localDateTimeOneHourLater );
System.out.println( "before : " + before );
System.out.println( "after : " + after );
When run, we get this output. Without a time zone, 1 AM + 1 hour = 2 AM. Remember these are "local" date-time values, not UTC. They represent only the vague idea of a date-time, not an actual moment on the timeline.
localDateTime : 2015-11-01T01:00
localDateTimeOneHourLater : 2015-11-01T02:00
But with time zones applied on the day DST expires, we get different results. Note how the time-of-day remains 01:00 but the offset-from-UTC changes from -07:00 to -08:00.
before : 2015-11-01T01:00-07:00[America/Los_Angeles]
after : 2015-11-01T01:00-08:00[America/Los_Angeles]
Perhaps this would more clear and easier to verify if we adjust into UTC. We can do that simply by accessing the before and after objects as Instant objects. The System.out.println then implicitly calls the toString method.
System.out.println( "before.toInstant : " + before.toInstant() );
System.out.println( "after.toInstant : " + after.toInstant() );
When run.
before.toInstant : 2015-11-01T08:00:00Z
after.toInstant : 2015-11-01T09:00:00Z
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?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
Yes, the Java API will take DST changes into account.
This tutorial explains quite well how to convert dates between timezones and how to choose the right class to represent a date:
https://docs.oracle.com/javase/tutorial/datetime/iso/timezones.html
You can also look at this class which represents the rules for each zone:
http://docs.oracle.com/javase/8/docs/api/java/time/zone/ZoneRules.html
In particular, this method can tell you if a particular instant is in daylight savings:
http://docs.oracle.com/javase/8/docs/api/java/time/zone/ZoneRules.html#isDaylightSavings-java.time.Instant-
I am attempting to show transactions over a certain time period using Jodatime.
Our server requires a start date and end date to be in UTC (which is probably obvious). Therefore any business logic around these is using DateTime object with the timezone set to DateTimeZone.UTC, e.g.
mStartDate = DateTime.now(UTC).withTimeAtStartOfDay();
That works well except when it comes to display the time I don't know how to augment it for the local (system default) timezone. Ideally I would like to use the DateUtils formatDateRange function passing in two local timestamps. But the getMillis() function doesn't seem to account for local offsets:
I have also tried this:
mTimePeriodTitle.setText(DateUtils.formatDateRange(mContext, f, mStartDate.getMillis(),
mEndDate.getMillis(), DateUtils.FORMAT_SHOW_TIME,
TimeZone.getDefault().getID()).toString());
But it hasn't made any difference. So my question is how can I get a nicely formatted local date range with 2 UTC timestamps?
If your DateTime is in UTC and you want to convert it to another timezone, you can use the withZone method to do the conversion.
For the examples below, my default timezone is America/Sao_Paulo (you can check yours using DateTimeZone.getDefault()):
// create today's date in UTC
DateTime mStartDate = DateTime.now(DateTimeZone.UTC).withTimeAtStartOfDay();
// date/time in UTC
System.out.println(mStartDate); // 2017-06-13T00:00:00.000Z
// date/time in my default timezone (America/Sao_Paulo)
System.out.println(mStartDate.withZone(DateTimeZone.getDefault())); // 2017-06-12T21:00:00.000-03:00
The output is:
2017-06-13T00:00:00.000Z
2017-06-12T21:00:00.000-03:00
Note that the withZone method correctly converts the date and time to my timezone (in America/Sao_Paulo the current offset is UTC-03:00), so it was adjusted accordingly.
If you want to get just the time (hour/minute/second), you can use toLocalTime() method:
System.out.println(mStartDate.withZone(DateTimeZone.getDefault()).toLocalTime()); // 21:00:00.000
The output is:
21:00:00.000
If you want another format (for example, don't print the 3 digits of fraction-of-second), you can use a DateTimeFormatter. The good thing is that you can set a timezone in the formatter, so you don't need to convert the DateTime:
// create formatter for hour/minute/second, set it with my default timezone
DateTimeFormatter fmt = DateTimeFormat.forPattern("HH:mm:ss").withZone(DateTimeZone.getDefault());
System.out.println(fmt.print(mStartDate)); // 21:00:00
The output is:
21:00:00
To get your range, you can use one of the methods above with your DateTime's (mStartDate and mEndDate), and use the DateTimeFormatter to change to whatever format you need.
PS: what I think you're missing when using getMillis() is that both datetimes (in UTC and in default timezone) represents the same instant. You are just converting this instant to a local time, but the millis is the same (think that, right now, at this moment, everybody in the world are in the same instant (the same millis), but their local times might be different depending on where they are). So, when converting a UTC DateTime to another timezone, we're just finding what is the local time in that zone, that corresponds to the same millis.
You can check this using the getMillis() method on both objects:
System.out.println(mStartDate.getMillis()); // 1497312000000
System.out.println(mStartDate.withZone(DateTimeZone.getDefault()).getMillis()); // 1497312000000
Note that, even if I convert the object to another timezone, the millis remains the same (1497312000000). That's because both represent the same instant, I'm just moving them to another timezone where the respective local time is different.
Java new Date/Time API
Joda-Time it's being discontinued and replaced by the new APIs, so I don't recommend start a new project with it. If that's your case, you can consider using the new Date/Time API, but if you have a big codebase using Joda or don't want to migrate it now, you can desconsider the rest of the answer.
Anyway, even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".*
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. I'm not sure if it's already available to all Android versions (but see the alternative below).
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 a way to use it, with 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 get the current date at start of the day in UTC, you can do:
// UTC's today at start of the day
ZonedDateTime utc = LocalDate.now(ZoneOffset.UTC).atStartOfDay(ZoneOffset.UTC);
System.out.println(utc); // 2017-06-13T00:00Z
First I did LocalDate.now(ZoneOffset.UTC) to find the current local date in UTC. If I use just LocalDate.now(), it'll get the current date in my default timezone, which is not what we want (it might be different from UTC, depending on where - and when - you are and what the default timezone is).
Then I used atStartOfDay(ZoneOffset.UTC) to get the start of the day at UTC. I know it sounds redundant to use UTC twice, but the API allows us to use any timezone in this method, and IMO it makes explicit what timezone we want (if the date is in a timezone with Daylight Saving changes, the start of day might not be midnight - the timezone parameter is to guarantee that the correct value is set).
The output is:
2017-06-13T00:00Z
To convert to my default timezone, I can use ZoneId.systemDefault(), which in my case returns America/Sao_Paulo. To convert it and get only the local time part, just do:
System.out.println(utc.withZoneSameInstant(ZoneId.systemDefault()).toLocalTime()); // 21:00
The output is:
21:00
If you want to change it, you can also use a formatter:
// formatter for localtime (hour/minute/second)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
System.out.println(fmt.format(utc.withZoneSameInstant(ZoneId.systemDefault()))); // 21:00:00
The output is:
21:00:00
java.time
The Joda-Time project is now in maintenance mode, with the project advising migration to the java.time classes.
Zones
Our server requires a start date and end date to be in UTC (which is probably obvious).
Yes, use UTC for much of your business logic, as well as for logging, storing, and exchanging date-time values. Think of UTC as the One True Time, with other zones being mere variations. Apply a time zone only when required for specific rules in your business logic or for presentation to the user.
For a value 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.
To see the same moment in a particular time zone, assign a ZoneId to get a ZonedDateTime object. Same moment, same point on the timeline, different wall-clock time.
ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;
See the same moment in yet another zone.
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland );
Strings
So my question is how can I get a nicely formatted local date range with 2 UTC timestamps?
After making the adjustments from UTC to a time zone as shown above, generate strings to represent their value.
To generate a String representing any of those objects in standard ISO 8601 format, simply call toString.
String output = instant.toString() ;
2018-01-23T01:23:45.123456Z
String output = zdtAuckland.toString() :
2018-01-23T14:23:45.123456+13:00[Pacific/Auckland]
To generate a String in other formats, define a formatting pattern. Or let java.time automatically localize.
To localize, specify:
FormatStyle to determine how long or abbreviated should the string be.
Locale to determine (a) the human language for translation of name of day, name of month, and such, and (b) the cultural norms deciding issues of abbreviation, capitalization, punctuation, separators, and such.
Example:
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
mardi 23 janvier 2018 à 14:23:45 heure avancée de la Nouvelle-Zélande
Note that time zone has nothing to do with Locale. One is for content, the other is for presentation.
Interval
When representing a pair of moments, a couple of start-stop moments, use the Interval class found in the ThreeTen-Extra library (linked below). This class represents a pair of Instant objects.
Its toString method generates a string is standard ISO 8601 format. For other formats, and for adjustments into other zones, use the code seen above to apply to each Instant a ZoneId to generate a ZonedDateTime. Access each Instant via getStart & getEnd.
Interval interval = Interval.of( start , stop ) ;
interval.toString(): 2007-12-03T10:15:30/2007-12-04T10:15:30
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?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
I have a Europe/Moscow timezone in my Mageia 4.
The code like this
System.out.println(new java.util.Date());
System.out.println(System.getProperty("user.timezone"));
returns
Fri Oct 24 13:43:22 GMT+03:00 2014
GMT+03:00
if I set the system date in 24.10.2014
and that code returns
Sun Oct 26 14:44:26 GMT+03:00 2014
GMT+03:00
if I set the system date in 26.10.2014
In my point of view it is wrong behavior of java zoneinfo system.
I downloaded the tzupdater and run it, the file Europe/Moscow was updated and now its size is 705 kB.
I try the code below:
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));
System.out.println(new java.util.Date());
System.out.println(java.util.TimeZone.getDefault());
and it returns
Fri Oct 24 15:10:34 MSK 2014
sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null]
and
Sun Oct 26 15:32:03 MSK 2014
sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,dstSavings=0,useDaylight=false,transitions=79,lastRule=null]
Why so? Why the offset is the same in these two cases?
tl;dr
Do not use an offset (+03:00) when you mean a time zone (Europe/Moscow)
Never rely on JVM’s current default time zone.
Never use java.util.Date.
For a moment in UTC, use java.time.Instant.
Instant.now()
For a moment in a time zone, use java.time.ZonedDateTime.
ZonedDateTime.now(
ZoneId.of( "Europe/Moscow" )
)
Offset versus time zone
As the comment by Jon Skeet notes, your JVM’s initial default time zone was not a time zone, it was merely an offset-from-UTC.
What is the difference? An offset is simply a number of hours, minutes, and seconds, positive (ahead of UTC) or negative (behind UTC). A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. The offset for a region can change whenever the politicians so deem. For example, many politicians buy into the lunacy of Daylight Saving Time (DST), and change the offset twice a year.
So if you set your time zone to a mere offset such as +03:00 (three hours ahead of UTC/GMT) rather than a time zone such as Europe/Moscow, your current date-time will always be reported as three hours ahead of UTC. Changes in offset for your region such as DST will be ignored, because you said so, you said "Always three hours ahead of UTC".
java.time
You are using terrible date-time classes that were supplanted years ago by the java.time classes defined in JSR 310.
Instead of TimeZone, use ZoneId.
ZoneId z = ZoneId.of( "Europe/Moscow" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ; // Capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone).
Avoid setting default time zone
You should only set the default time zone of your JVM as a last-ditch act of desperation.
Setting the default time zone (and default locale, by the way) immediately affects all code in all threads of all apps running within that JVM. You will be rudely changing the zone behind the backs of other programmers. You might even find that their code changes the zone behind your back, during runtime.
Better to write all your date-time handling to never rely on the current default zone (or locale). Specify explicitly your desired/expected time zone by passing optional arguments. Personally, I wish those time zone arguments were required rather than optional, to help educated programmers about date-time issue.
We can see an example of this in the code above. Notice how we pass a ZoneId for Russia to the now method. Otherwise, we would be capturing the current moment in the wall-clock time of whatever region happens to be the JVM’s current default time zone.
Tip: If critical, always confirm the time zone with the user.
java.util.Date::toString lies
Be aware that the toString method on the Date objects you were calling has the anti-feature of dynamically applying the JVM’s current default time zone while generating the text to represent that moment. While well-intentioned, this unfortunate design decision has confused countless programmers trying to wrangle date-time values in Java. A java.util.Date is actually in UTC, as a count of milliseconds since the first moment of 1970 in UTC. The time zone shown in the string is not actually in the Date object.
But this is moot, as this is one of many reasons to avoid this class entirely. Use java.util.Instant instead. Instead of GregorianCalendar, use ZonedDateTime.
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
The problem was solved by adding the definition of correct timezone.
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Moscow"));
Your second test (26.10.2014) is after the change to wintertime so you probably need to correct the time as well by -1 hour
please see if you can tell me how to handle the DST issue in my case.
first, my application is a logistic system and it is for global user,so it involves timezone problem,i will handle it as following when set user local date of booking:
1.when user login application,we could get user's timezone according to login IP,but is is just an offset (i don't remember the term of this stuff) e.g "GMT+08"(BeiJing) or "GMT-06"(Chicago) .
2.before user save booking ,we need to set booking local date,as i can't get user local date direct .so i will get the server date first(in my case,it is BeiJing time),then calculate local date according to server date and user timezone,e.g if user timezone is "GMT-08",server date is 2013-08-29 17:45:00. server timezone is "GMT+08",then i will use server date-8-8 and the result will be 2013-08-29 01:45:00.but as i don't consider the DST,the calculated local date will be difference from the actual date.e.g now in San Francisco,the actual local date will be earlier one hour than the result that i calculated using this way,
i find the java TimeZone have already considered the DST problem,but i need to provide "location" name(e.g US/Alaska,Pacific/Apia) when construct TimeZone . while in my case, what i can get is just the offset.so can you tell me how to fix the DST issue in my case?
Yes, you should use either Joda-Time or the new java.time package in Java 8 (inspired by Joda-Time).
An offset is the number hours-minutes-seconds from UTC (GMT) that is represented by a certain date-time value. West coast is -08:00 (ignoring Daylight Saving Time nonsense), meaning 8 hours behind UTC.
Beware that java.time in its initial release has a small bug where it fails to handle an offset of just hours (such as +08) without minutes (such as +08:00).
A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region.
Use proper time zone names (mostly continent slash city). Avoid the 3 or 4 letter codes, such as EST, which are neither standardized nor unique.
A java.util.Date has no time zone, while a Joda-Time DateTime does.
To get a web browser's time zone, see this question. But often, this does not work well. As you've probably seen, many web sites ask the user to choose a time zone.
Your exact use-case is confusing. Generally the best approach is to use date-time values for UTC, then adjust to user's local time as needed. Usually best for your software to work and store date-times as UTC. Then present a local date-time adjusted to suit the user. In other words, think globally (UTC), present locally (local time zone adjusted).
Usually sysadmins keep their server computers set to UTC (no time zone offset). If your OS (like Mac OS X) does not offer UTC, then use Reykjavik as Iceland uses UTC year-round without any Daylight Saving Time. Likewise, database engines almost always convert date-time values to UTC for storage.
Joda-Time does offer a LocalDate class for when you truly do not care about time zone or time. But often it is better to use a date-time (a DateTime instance), and format for a date-only string as needed.
Example code in Joda-Time 2.3.
DateTimeZone timeZoneChina = DateTimeZone.forID( "Asia/Shanghai" );
DateTime dateTimeChina = new DateTime( 2013, 8, 29, 17, 45, 00, timeZoneChina );
DateTime dateTimeUtc = dateTimeChina.withZone( DateTimeZone.UTC );
DateTime dateTimeParis = dateTimeChina.withZone( DateTimeZone.forID( "Europe/Paris" ) );
DateTimeZone timeZoneUsWestCoast = DateTimeZone.forID( "America/Los_Angeles" );
DateTime dateTimeUnitedStatesWestCoast = dateTimeChina.withZone( timeZoneUsWestCoast );
DateTimeFormatter formatter = ISODateTimeFormat.date();
String outputDateOnlyForUnitedStatesWestCoast = formatter.withZone( timeZoneUsWestCoast ).print( dateTimeUtc );
Dump to console…
System.out.println( "dateTimeChina: " + dateTimeChina );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeParis: " + dateTimeParis );
System.out.println( "dateTimeUnitedStatesWestCoast: " + dateTimeUnitedStatesWestCoast );
System.out.println( "outputDateOnlyForUnitedStatesWestCoast: " + outputDateOnlyForUnitedStatesWestCoast );
When run…
dateTimeChina: 2013-08-29T17:45:00.000+08:00
dateTimeUtc: 2013-08-29T09:45:00.000Z
dateTimeParis: 2013-08-29T11:45:00.000+02:00
dateTimeUnitedStatesWestCoast: 2013-08-29T02:45:00.000-07:00
outputDateOnlyForUnitedStatesWestCoast: 2013-08-29
java.time
Similar code in java.time.
Current moment in UTC.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
Adjust into a time zone for Chicago area, yielding a ZonedDateTime object.
ZoneId zChicago = ZoneId.of( "America/Chicago" ) ;
ZonedDateTime zdtChicago = instant.atZone( z ) ;
Adjust into China time zone.
ZoneId zShanghai = ZoneId.of( "Asia/Shanghai" ) ;
ZonedDateTime zdtShanghai = zdtChicago.withZoneSameInstant( zChicago ) ;
All three objects (instant, zdtChicago, and zdtShanghai) represent the same moment, the same point on the timeline. Simultaneous, but seen through various wall-clock times.
Apparently you want just the date portion for some purpose, without the time-of-day and without time zone. For that extract a LocalDate object.
LocalDate ld = zdtChicago.toLocalDate() ;
Of course that may be a different date than returned by zdtShanghai.toLocalDate().
You may need to construct a specific moment.
ZonedDateTime zdtShanghai = ZonedDateTime.of( 2013, 8, 29, 17, 45, 0, 0 , zShanghai ) ;
Location and time zone
You said:
i need to provide "location" name
Location has nothing to do with time zone. A businessperson from Québec might want to see a particular schedule in America/Montreal zone while physically traveling in Tokyo Japan.
You can ask the user’s web browser or local JVM for its current default time zone. But ultimately, if critical, you must confirm the desired/expected time zone with the user.
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
It's a common source of headache
In my experience, location by IP address is not always reliable, for example when people are using corporate VPNs.
You are correct, region-based time zones ("Europe/Paris", "CET") are preferable for properly handling DST.
I solved a similar problem with the following approach :
You associate a precise timezone to each user in your server-side database. When user fills a booking form you display a TZ selector, pre-filled with his default TZ. So he can double check it (IMHO much safer than guessing by IP) and on server side, Dates can be safely converted from local to server time and back.
Joda time might be able to solve your problem:
http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
We know that getTime method of java.util.Date returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object.
I noticed a weird situation as below;
System time zone is:(UTC+02:00) Istanbul
Date currentDate = new Date();
System.out.println(currentDate .getTime());
System.out.println(currentDate);
Java ConsoleOutput:
1360753217219
Wed Feb 13 13:00:17 VET 2013
Then my javascript plugin is using this long object like below;
Javascript:
console.log(new Date(1360753217219));
Browser ConsoleOutput:
Date {Wed Feb 13 2013 13:00:17 GMT+0200 (Turkey Standard Time)}
Thats all ok, however! After change my local time zone as (UTC-04:30) Caracas, situation and hour is changing as below with same number of milliseconds;
Javascript:
console.log(new Date(1360753217219));
Browser ConsoleOutput:
Date {Wed Feb 13 2013 06:30:17 GMT-0430 (Venezuela Standard Time)}
Can someone explain this? Is that js bug? Or more importantly, how should I handle this on java side, to get same date with same number of miliseconds for different time zones on js side?
Thanks!
The milliseconds are time zone agnostic. Time is measured as an absolute since Jan 1, 1970 GMT. So, the idea is that you get the milliseconds and then work out what the local time for a given time zone is after the fact. If you think about it, it makes sense. The number of milliseconds that have passed since 1970 are the same no matter where you are.
It gets a bit confusing but do NOT noodle around with the milliseconds in order to adjust for time zones. Every Date library has mechanisms to translate a millisecond stamp into a time zone specific local time.
So if your specific question is how to communicate the date effectively between the server and the client (what languages you're using is not important), the answer is it's perfectly safe to pass milliseconds back and forth and work out on either side what global specific time you're talking about, if that's important to the context of what you're doing with that time.
not a bug, that's just how time zones work.
if you call someone in Venezuela right now and ask him what time it is, he'll tell you it's
6.5 (according to your example) hours earlier than the time in turkey.
as you mentioned, the number you're dealing with represents the number of milliseconds since 1970, 00:00:00 GMT, in Caracas at that very same second, the time was 31.12.1969 19:30 GMT-0430
so, however many seconds later, the time in Venezuela will still be 4:30 hours earlier compared to GMT.
you can't get the exact same date in different time zones if you use the same input (milliseconds) because that would simply be wrong.
if you want to get the same result, you could add the difference in timezones (6.5 hours in this case) to the output. following Dr.Dredel's advice, you probably shouldn't mess with the milliseconds.
tl;dr
Instant.ofEpochMilli( 1_360_753_217_219L ) // UTC
2013-02-13T11:00:17.219Z
Instant.ofEpochMilli( 1_360_753_217_219L )
.atZone( ZoneId.of( "Europe/Istanbul" ) ) // Same moment, two hours *ahead* of UTC.
2013-02-13T13:00:17.219+02:00[Europe/Istanbul]
Instant.ofEpochMilli( 1_360_753_217_219L )
.atZone( ZoneId.of( "America/Caracas" ) ) // Same moment, four-and-a-half hours *behind* UTC.
2013-02-13T06:30:17.219-04:30[America/Caracas]
Using java.time
You are using troublesome old date-time classes bundled with the earliest versions of Java. They are now legacy, supplanted by the java.time classes, the best date-time library on any platform.
Start with your number of milliseconds since an epoch reference date of the beginning of 1970 in UTC (1970-01-01T00:00:00Z). Others point out that you may not grasp that an epoch reference date has a time zone, and here with this epoch that zone is UTC, an offset of zero hours. All other offsets are measured against this one, a number of hours and minutes ahead of UTC or behind UTC.
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).
long input = 1_360_753_217_219L ;
Instant instant = Instant.ofEpochMilli( input ) ;
instant.toString(): 2013-02-13T11:00:17.219Z
If you want to see that same moment through the lens of a particular region’s wall-clock time, apply a time zone.
A time zone is a history of past, present, and future changes to the offset in use by a particular region.
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 zEurope_Istanbul = ZoneId.of( "Europe/Istanbul" ) ;
ZonedDateTime zdtEurope_Istanbul = instant.atZone( zEurope_Istanbul ) ;
zdtEurope_Istanbul.toString(): 2013-02-13T13:00:17.219+02:00[Europe/Istanbul]
You can apply another time zone.
ZoneId zAmerica_Caracas = ZoneId.of( "America/Caracas" ) ;
ZonedDateTime zdtAmerica_Caracas = zdtEurope_Istanbul.withZoneSameInstant( zAmerica_Caracas ) ;
zdtAmerica_Caracas.toString(): 2013-02-13T06:30:17.219-04:30[America/Caracas]
See this code live at IdeOne.com.
These three objects, instant & zdtEurope_Istanbul & zdtAmerica_Caracas, all represent the very same simultaneous moment, the same point on the timeline.
Your count-from-epoch represents 11 AM in UTC. Istanbul is two hours ahead of UTC, so the time-of-day there at the same moment is two hours after 11 AM, 1 PM (13:00). Venezuela is four and a half hours behind UTC, so the time-of-day there at the same moment is 6:30 AM. These all make sense, all the same moment but different wall-clock time.
ISO 8601
Do not use a count-from-epoch for exchanging or storing date-time values. That is error-prone, being impposible to read meaningfully by a human, and ambiguous as there are at least a couple dozen epoch reference dates in use by various software systems and at different granularities (whole seconds, milliseconds, microseconds, nanoseconds, etc.).
When passing date-time values outside your JVM, use the standard ISO 8601 formats for textual representation. The java.time classes use the standard formats by default when parsing/generating strings. You can see those formats in this Answer’s example code.
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.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….
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.