Calendar returns date in wrong time zone
because Date
object doesn't have timezone as part of its state, You need SimpleDateFormat to format and print the date in your required timezone
Why is Calendar returning the wrong hour with the correct time-zone?
1) Set the TimeZone in SimpleDateFormat
2) Either use UTC, or a "real" Time Zone like ("Austria/Vienna");
(Country name, and biggest city in TimeZone look it up to be sure)
EST (=UTC-5) is not a time zone very suitable for computational purpose, because it is the time without daylight saving.
One more example from central europe:
In Winter we use MEZ (CET), in Summer (Daylight savings) we use (MESZ = CEST).
But you want that your computer caluclates that for you, so don't use that:
TimeZones are geo poilitical, therfore the name of the country is needed.
Each country can decide to change its time zone when they want (e.g russia some time ago, and spain is discussing now.)
Calendar.getInstance().getTime() returning date in GMT instead of Default TimeZone
The second output in your question is the correct and expected behaviour on a JVM running Irish time (Europe/Dublin). On September 12, 2017 Ireland is on summer time (DST). While it is not clearly documented, Date.toString()
(which you invoke implicitly when printing the Date
you get from c.getTime()
) prints the date and time in the JVM’s time zone, which in September is rendered as IST for Irish Summer Time.
When you set the date on the Calendar
object also using Irish time, the hour of day is preserved; in your case you get Jan 01 2007 12:36:24 Irish standard time. Now imagine the confusion if both Irish Summer Time and Irish Standard Time were rendered as IST. You would not be able to distinguish. Instead, since Irish standard time coincides with GMT, this is what Date.toString()
prints when the date is not in the summer time part of the year (which January isn’t).
My guess is that your first output is from a JVM running India time. It too is rendered as IST, and since India doesn’t use summer time, the same abbreviation is given summer and winter.
java.time
Before understanding the explanation for the behaviour you observed, I posted a comment about the outdated and the modern Java date and time classes. I still don’t think the comment is way off, though. This is the modern equivalent of your code:
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
System.out.println(zdt);
zdt = zdt.with(LocalDate.of(2007, Month.JANUARY, 1));
System.out.println(zdt);
It prints
2017-09-12T11:45:33.921+01:00[Europe/Dublin]
2007-01-01T11:45:33.921Z[Europe/Dublin]
If you want to use the JVM’s time zone setting, use ZoneId.systemDefault()
instead of ZoneId.of("Europe/Dublin")
. As the name states, contrary to Date
, ZonedDateTime
does include a time zone. It corresponds more to the old Calendar
class. As you can see, its toString
method prints the offset from UTC (Z
meaning zero offset) and the time zone name in the unambiguous region/city format. I believe that this leaves a lot less room for confusion. If you want to print the date in a specific format, use a DateTimeFormatter
.
Appendix: sample output from your code
For the sake of completeness, here are the outputs from your code when running different time zones that may be rendered as IST:
Europe/Dublin (agrees with your second output)
Tue Sep 12 11:19:28 IST 2017
Mon Jan 01 11:19:28 GMT 2007Asia/Tel_Aviv
Tue Sep 12 13:19:28 IDT 2017
Mon Jan 01 13:19:28 IST 2007Asia/Kolkata (agrees with your first output)
Tue Sep 12 15:49:28 IST 2017
Mon Jan 01 15:49:28 IST 2007
Java Calendar returns wrong hour in MS Windows for `America/Santiago` zone
Summary
The authorities in Chile changed this year’s (2016) time zone rules at the last minute. The tz
database in Java is not yet updated.
Details follow.
java.time
You are using the old date-time classes bundled with early Java that have proven to be troublesome. In Java 8 and later those classes were supplanted by the java.time framework (inspired by the Joda-Time library). Avoid the old classes.
Focus on UTC
Programmers should think first in UTC. Always verify the current time in UTC to be sure the computer even has the correct time. Flipping around time zones and Daylight Saving Time (DST) changes will make your brain hurt, and UTC is your aspirin.
In Java 8 and later, the Instant
class represents a moment on the timeline in UTC with a resolution in nanoseconds.
Instant now = Instant.now();
System.out.println( "Instant.now(): " + now );
Always include the UTC value in a post such as this Question.
Time zone of host OS does not matter
The JVM holds it own representation of its current default time zone. By default in most implementations that zone is picked up from the host OS’ time zone setting. But Java launch configurations can override that default. And any Java code in any thread of any app within the JVM can change the JVM’s current default time zone during runtime with a call to TimeZone.setDefault
.
So the current time zone setting of the host OS is irrelevant. What matters is the current setting in your JVM.
The part of your host OS that does matter is if its hardware clock knows the current moment correctly. In most JVM implementations, the current moment is determined from the host hardware clock, and then the JVM applies its own current default time zone.
Time Zone redefined for Chile in early 2016
Politicians enjoy fiddling with time zones and Daylight Saving Time (DST). Chile is no exception. See the Wikipedia article, Time in Chile.
From what I could discern… Last year, 2015, Chile stayed on DST permanently at -03:00
and deciding to never revert back to the standard time of -04:00
. Then they changed their minds again, announcing in March 2016 that they would re-introduce standard time for their winter (southern hemisphere, winter being now, late May, June, July). On 2016-05-15 midnight (a couple weeks ago), the old standard offset of -04:00
kicked in. That standard/winter time will endure until 2016-08-14 (or until they change their minds again). [Take all that with a grain of salt. Please verify that I got the facts correct.]
These frequent changes wreak havoc with computers which need to have their tz
time zone database files updated in order to reflect the latest fiddling.
Your operating system has its own copy of the time zone definitions. Java has its own copy. And other software such as your database engine may have its own copy.
Current Java 8 Update 92 lacks recent Chile changes
So let's run a test in Java 8 Update 92 to see if the Java copy of tz
is up-to-date.
long millis = 1464645007120L;
Instant instant = Instant.ofEpochMilli ( millis ); // UTC, always.
ZoneId zoneId = ZoneId.of ( "America/Santiago" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Dump to console.
System.out.println ( "millis: " + millis + " | instant: " + instant + " | zoneId: " + zoneId + " | zdt: " + zdt );
millis: 1464645007120 | instant: 2016-05-30T21:50:07.120Z | zoneId: America/Santiago | zdt: 2016-05-30T18:50:07.120-03:00[America/Santiago]
You can see the offset in the last part is -03:00
. So it seems to me that the current version of Java released mid-April is unfortunately not up-to-date with the Chile changes announced in March.
From my readings it seems that today’s (2016-05-30) correct offset for America/Santiago
should be -04:00
as seen here on time.is/Santiago.
Usually I would suggest using the Java tzupdater
tool. But the current download is labeled with the word 2015
so I presume it is not up-to-date either.
I suggest filing a bug report at the Java team at Oracle.
Workaround
It may be a while before you can obtain a corrected version of the tz
database (though perhaps you could hack your own update -- I do not know). Until then, as a workaround, perhaps you could specify your own offset in Java code using the ZoneOffset
class, a subclass of ZoneId
that represents only the offset-from-UTC without any rules for adjusting for anomalies such as DST. I’ve not thought through the implications; be wary.
ZoneOffset zoneOffset = ZoneOffset.of( -4 ); // Negative four hours for '-04:00'.
How to set time zone of a java.util.Date?
Use DateFormat. For example,
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");
Gregorian Calendar Wrong Hour
tl;dr
Problem: You are mistakenly changing the date, not just the time-of-day. On top of that, a time zone is implicitly being applied.
Solution: Instead use the modern java.time classes.
LocalDate
.now() // Better to explicitly pass the desired/expected time zone as a `ZoneId` object.
.atStartOfDay() // Again, better to explicitly pass the desired/expected time zone as a `ZoneId` object.
Returns a LocalDateTime
(BEWARE: Not a moment, not a point on the timeline).
2018-11-01T00:00
Much better to specify time zone.
LocalDate
.now(
ZoneId.of( "Pacific/Auckland" )
)
.atStartOfDay(
ZoneId.of( "Pacific/Auckland" )
)
Returns a ZonedDateTime
. This is a moment, is a point on the timeline.
2018-11-02T00:00+13:00[Pacific/Auckland]
GregorianCalendar::setTimeInMillis
is not setting time-of-day
Apparently you mistakenly thought the GregorianCalendar::setTimeInMillis
would set the time-of-day without affecting the date. Among the many flaws in these legacy date-time classes is some very poor choices in naming classes and methods.
But, no, that method redefines the moment as a count of milliseconds since epoch reference date of 1970-01-01T00:00Z.
Add in the time zone implicitly assigned to GregorianCalendar
, and you have unexpected results.
I started to test around a bit with the Java-GregorianCalendar
Don’t.
Those old date-time classes bundled with the earliest versions of Java are terrible. They were supplanted years ago by the java.time classes defined in JSR 310.
Specifically, to track a moment in UTC, use Instant
.
initializing the Object with milliseconds
Don’t.
Tracking time as a count-from-epoch-reference is prone to error. There are many different epoch reference dates in use in the industry. There are different granularities in use in the industry (whole seconds, milliseconds, microseconds, nanoseconds).
So a count-from-epoch is ambiguous. Also prone to confusing and missed errors because humans cannot read the meaning of the values.
When exchanging date-time values, use strings in standard ISO 8601 format instead.
When handed a count of milliseconds from the Unix epoch of first moment of 1970 in UTC, parse as an Instant
.
Instant instant = Instant.ofEpochMilli( … ) ;
java.time
The modern solution uses java.time classes instead.
Get your date.
LocalDate
The LocalDate
class represents a date-only value without time-of-day and without time zone or offset-from-UTC.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 2-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" ) ;
LocalDate today = LocalDate.now( z ) ;
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Or specify a date. You may set the month by a number, with sane numbering 1-12 for January-December.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Or, better, use the Month
enum objects pre-defined, one for each month of the year. Tip: Use these Month
objects throughout your codebase rather than a mere integer number to make your code more self-documenting, ensure valid values, and provide type-safety.
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
ZonedDateTime
Apparently you want the first moment of the day. By the way, do not think of this as “midnight” as that term is ambiguous.
The first moment of the day may not be 00:00. Anomalies such as Daylight Saving Time (DST) mean that the first moment on some dates in some zones may be another time such as 01:00. Let java.time determine the first moment.
Specify a time zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = localDate.atStartOfDay( z ) ;
If you want to see that same moment in UTC, extract a Instant
.
Instant instant = zdt.toInstant() ;
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.
Java: Fix incorrect timezone in date object
tl;dr ⇒ use ZonedDateTime
for conversion
public static void main(String[] args) {
// use your date here, this is just "now"
Date date = new Date();
// parse it to an object that is aware of the (currently wrong) time zone
ZonedDateTime wrongZoneZdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
// print it to see the result
System.out.println(wrongZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// extract the information that should stay (only date and time, NOT zone or offset)
LocalDateTime ldt = wrongZoneZdt.toLocalDateTime();
// print it, too
System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// then take the object without zone information and simply add a zone
ZonedDateTime correctZoneZdt = ldt.atZone(ZoneId.of("GMT"));
// print the result
System.out.println(correctZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
Output:
2020-01-24T09:21:37.167+01:00[CET]
2020-01-24T09:21:37.167
2020-01-24T09:21:37.167Z[GMT]
Explanation:
The reason why your approach did not just correct the zone but also adjusted the time accordingly (which is good when desired) is your use of a LocalDateTime
created from an Instant
. An Instant
represents a moment in time which could have different representations in different zones but it stays the same moment. If you create a LocalDateTime
from it and put another zone, the date and time are getting converted to the target zone's. This is not just replacing the zone while keeping the date and time as they are.
If you use a LocalDateTime
from a ZonedDateTime
, you extract the date and time representation ignoring the zone, which enables you to add a different zone afterwards and keep the date and time as it was.
Edit: If the code is running in the same JVM as the faulty code, you can use ZoneId.systemDefault()
to get the same time zone as the faulty code is using. And depending on taste you may use ZoneOffset.UTC
instead of ZoneId.of("GMT")
.
Java Date gives wrong time
This can be caused by one of two things:
- The date in the underlying operating system is incorrect (but you may accidentally have configured it so you cannot tell).
- The timezone determined by the JVM is incorrect (including that daylight savings information may be outdated).
Perhaps you could add the output of your new Date()
along with information about what you were expecting and where you are?
Related Topics
Is Jdk "Upward" or "Backward" Compatible
Why Do Many Collection Classes in Java Extend the Abstract Class and Implement the Interface as Well
What Does It Mean to Program to an Interface
Write a File in Hdfs with Java
Best Way to Format Multiple 'Or' Conditions in an If Statement
Safe String to Bigdecimal Conversion
What Is the Breakdown for Java's Lambda Syntax
How to Open a File with the Default Associated Program
How to Sort Date Which Is in String Format in Java
Java Conditional Compilation: How to Prevent Code Chunks from Being Compiled
The Meaning of Noinitialcontextexception Error
Synchronizing on an Integer Value
Windows Shortcut (.Lnk) Parser in Java
Reverse Each Individual Word of "Hello World" String with Java
Can the Jvm Recover from an Outofmemoryerror Without a Restart
How to Add Unicode in Truetype0Font on PDFbox 2.0.0