Date Format with different Timezone in Java - java

I am confused with Timezone conversions in Java. I have a few cases which I will list out below.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// sdf.setTimeZone(TimeZone.getTimeZone("Asia/kolkata"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); //. O/P - Sun Jan 31 00:00:00 IST 2021
Now lets uncomment the Timezone part and see the time difference
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/kolkata"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); // O/P - Sun Jan 31 05:30:00 IST 2021
Now lets set the TimeZone to IST and see the time difference
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("IST"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); // O/P - Sun Jan 31 00:00:00 IST 2021
Now lets set the TimeZone to UTC and see the time difference
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); // O/P - Sun Jan 31 05:30:00 IST 2021
Can anybody please explain me why this shift in time is happening (+- 5:30) when I change the Timezone?
For IST and Asia/Kolkata, time should have remain same because they are same Timezone, but why the shift?
Why When using the UTC Timezone, time gets increased by 5:30 hours? What I understand is IST is 5:30 hrs ahead of UTC, so cnverting to UTC should have decreased the time by 5:30 hrs
Why even after converting to UTC, my time displays IST 2021?
I still have confusion here.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1.getTime()); // 1612051200000
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf1.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
Date date2 = sdf1.parse("2021-01-31");
System.out.println(date2.getTime()); // 1612031400000
Why instant of time in UTC is greater than instant of time in Asia/Kolkata ?

Here are some things for you to note:
When a Date is printed, it will be formatted in your computer's local timezone (that's what Date.toString does). Presumably, your computer is in the Asia/Kolkata timezone, so the output is always displayed as a date & time in that timezone.
A Date represents a point in time (i.e. an instant). It is not a tuple of year, month, day, hour, minute, seconds and timezone
Since there are no time in your input string, the time 00:00:00 is used for the time when parsing.
Just a date and a time is not enough to produce a point in time. You also need a timezone to specify a point in time. Since there is no timezone in your input string, the local timezone of your computer is used, or if you have set it, sdf.getTimeZone().
Although a timezone is used in parsing the date, the timezone is not part of the Date object.
Can anybody please explain me why this shift in time is happening (+- 5:30) when I change the Timezone?
When you use the "IST" timezone (first and third code snippet), sdf gets the following pieces of information:
Date: 2021-01-31
Time: 00:00:00
TimeZone: Asia/Kolkata
With these pieces of information, it can produce a point in time, represented by a number of milliseconds since the Java Epoch - 1970-01-01 00:00:00 UTC. This is the Date object. Then you print the Date object, which gets formatted to your local timezone. Your local timezone just so happens to be the same as the one that sdf is provided with, so you see Sun Jan 31 00:00:00 IST 2021.
When you use UTC (second and fourth code snippets), these information are provided to sdf:
Date: 2021-01-31
Time: 00:00:00
TimeZone: UTC
That represents a different point in time than 2021-01-31T00:00:00 in Kolkata. How different? 2021-01-31T00:00:00 in UTC is exactly 5 and a half hours later than 2021-01-31T00:00:00 in Kolkata. Recall that to convert a UTC time to Kolkata, you add 5 and a half hours.
For IST and Asia/Kolkata, time should have remain same because they are same Timezone, but why the shift?
Because you have misspelled Asia/Kolkata. The first "K" in "Kolkata" should be capitalised. Unknown zone IDs are treated as UTC by the TimeZone class. This is why you should move to the new java.time classes. ZoneId throws an exception if you supply it with an unknown zone ID.
Why When using the UTC Timezone, time gets increased by 5:30 hours? What I understand is IST is 5:30 hrs ahead of UTC, so converting to UTC should have decreased the time by 5:30 hrs
You are thinking about formatting dates, not parsing, because remember that the timezone is not part of Date, but part of SimpleDateFormat. Your code does not format Date, only parses them. Without formatting, Dates are always printed in your local timezone.
To see your desired behaviour using SimpleDateFormat, you'd first parse the date string once, and then format it using SimpleDateFormats with different timezones.
Really though, you should change to java.time. Using that API, your zone changing code could be written like so:
ZonedDateTime zdt = LocalDate.parse("2021-01-31")
.atStartOfDay()
.atZone(ZoneId.of("Asia/Kolkata"));
System.out.println(zdt);
ZonedDateTime utcDateTime = zdt.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println(utcDateTime);
// output:
// 2021-01-31T00:00+05:30[Asia/Kolkata]
// 2021-01-30T18:30Z[UTC]

java time
I recommend you use java.time, the modern Java date and time API, for your date work
LocalDate date = LocalDate.parse("2021-01-31");
System.out.println(date);
Output is:
2021-01-31
A LocalDate is a date without time of day and without time zone or UTC offset, so using it frees you completely from all time zone trouble. Furthermore we don’t need any explicit formatter. Your string is in ISO 8601 format, and LocalDate parses the most common ISO 8601 variant as its default. As you can see, it also prints the same ISO 8601 format back when we print it, implicitly calling its toString method.
What went wrong in your code?
The SimpleDateFormat, TimeZone and Date classes that you are using are poorly designed and long outdated. No wonder that their behaviour confuses you.
I am assuming that Asia/Kolkata (or Asia/Colombo or Asia/Calcutta) is the default time zone of your JVM. In your first example the SimpleDateFormat is using your default time zone and is parsing the string into the first moment of the day in that time zone.
In your second example, as Elavya has spotted so well, you have got a lower case k in Asia/kolkata which causes TimeZone not to recognize the intended time zone. And this is where TimeZone excels in bad design: it just tacitly gives you GMT instead. Next the Date class is poorly designed too and still prints the time in the default time zone of the JVM, giving the illusion that the Date object contains a time zone. This has confused very many. The start of the day in GMT is the same point in time as 05:30:00 IST, so this is what you get.
In your third and fourth example, even though the three letter time zone abbreviations are deprecated, IST (contrary to what Eklavya said) is interpreted as Asia/Kolkata and UTC as Etc/UTC. Even though as Eklavya also said, IST is ambiguous.
So in short:
The change happens because the start of the day is a different point in time in different time zones.
Because of your typo in Asia/kolkata. Time zone IDs are case sensitive.
You are not converting to UTC. You are parsing in UTC thereby converting from UTC, and Date.toString() further converts to Asia/Kolkata (IST) as the output also says.
Because the Date object hasn’t got a time zone and because Date.toString() grabs the default time zone of your JVM and uses it for rendering the string to be returned.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
All about java.util.Date

Java doc for getTimeZone
ID - the ID for a TimeZone, either an abbreviation such as "PST", a
full name such as "America/Los_Angeles", or a custom ID such as
"GMT-8:00". Note that the support of abbreviations is for JDK 1.1.x
compatibility only and full names should be used.
TimeZone abbreviation is not supported. So you can't use IST
And in TimeZone Doc for Three-letter time zone IDs
For compatibility with JDK 1.1.x, some other three-letter time zone
IDs (such as "PST", "CTT", "AST") are also supported. However, their
use is deprecated because the same abbreviation is often used for
multiple time zones (for example, "CST" could be U.S. "Central
Standard Time" and "China Standard Time"), and the Java platform can
then only recognize one of them.
Problem is IST abbreviation is used for multiple time zones like Irish Standard Time, Isreal Standrad Time, Indian Standard Time. And you mistyped Asia/Kolkata as Asia/kolkata.
So, the GMT zone will return if the given ID cannot be understood from TimeZone.getTimeZone()

As an addition to the accepted answer, for the last part of your question;
Why instant of time in UTC is greater than instant of time in Asia/Kolkata in below code?
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1.getTime()); // 1612051200000
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf1.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
Date date2 = sdf1.parse("2021-01-31");
System.out.println(date2.getTime()); // 1612031400000
First, you have a point T in time regardless of timezone. In our example T=2021-01-31 00:00:00.
When we set timezone as UTC and print the time using java.util.Date.getTime() method, it will print milliseconds since the Unix epoch, which occurred at midnight January 1st 1970, UTC. So it will print 1612051200000. As you see the epoch and our date has the same timezone which is UTC. So the time is printed directly, no adjustment necessary for timezone.
Now, when we set timezone as Asia/Kolkata, during SimpleDateFormat.parse, timezone information will be added to date. That means +5:30h(19800000ms) will be added to time T. Therefore our time T is increased by 19800000ms. However T must be pointing to the same point in time. How do we fix that? It is fixed on SimpleDateFormat.parse method by subtracting 19800000ms from the time 1612051200000ms so that getTime() method will now show 1612031400000ms so that our actual time T will still show the same point in time(which is 1612051200000ms) because in this date object we have an extra 19800000ms which comes from timezone.

Related

Date is not properly calculated in Java. Issue with zone

I have a date format stored in DB, for example:
Thu Aug 27 2020 00:00:00 GMT-0400 (Eastern Daylight Time)
I want to display the same date as output. Seems like I am missing something zone. It's evolving to be one day prior to this date.
I did the following:
DateTimeFormatter etFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy 'at' hh:mma 'ET'");
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTime = ((Timestamp) date).toLocalDateTime().atZone(zoneId);
etFormat.format(zonedDateTime)
Output:
08/26/2020 at 08:00PM ET
What am I doing wrong?
In your database you have the date time with offset UTC-04:40 (which is 4 hr behind from UTC assuming America/New_York timezone). And when it converts into Timestamp it will be stores in UTC without offset which is 08/26/2020 at 08:00PM.
So first convert the Timestamp into Instant of UTC and then convert the Instant into ZonedDateTime with the zone information
ZonedDateTime dateTime = timestamp.toInstant()
.atZone(ZoneOffset.UTC)
.withZoneSameInstant(ZoneId.of("America/New_York"));
etFormat.format(dateTime); //08/27/2020 at 00:00PM ET
The central issue is this:
java.sql.Timestamp, which is what e.g. resultSet.getTimestamp() returns, does not contain any timezone data. It is simply an instant in time, and it is stored as milliseconds since the epoch (jan 1st, 1970), UTC zone.
This does not match what most DBs store, because most DBs do in fact explicitly store the timezone with that. If your DB does not do this, or you picked a column type which does not do this, you should strongly consider changing that.
So, if the database has stored 'midnight in new york, aug 27th', and the database is forced by JDBC to put this in java.sql.Timestamp terms, there's nothing the DB engine can do about it, other than do its best, which is to return that exact time, in UTC terms. If you then print the UTC timestamp in human terms, you end up with '4 at night', and not 'midnight' (because new york is 4 hours earlier than UTC).
You then, with your code say: Okay, take the timestamp, turn it into a local date time (that'd be the notion of '27th of august, 4 o clock at night', without any inkling of in which czone that is in, and by itself not a thing that can ever be turned back into an epoch with more info), and then you put this at the new york zone, giving you '4 at night in new york', which is 4 hours later than where we started.
Okay, but how do I fix this?
Every other answer (so far) is just giving you silly ways to fight the symptoms.
I propose you fix the disease.
The actual error occurs when you ask the DB to transfer the fully timezoned information from its tables into the timezoneless java.sql.Timestamp object. Stop doing that.
Don't call (I assume your column is called 'mark', fill in whatever it might be):
resultSet.getTimestamp("mark").
Call:
resultSet.getObject("mark", ZonedDateTime.class);
or possibly try LocalDateTime.class, or possibly OffsetDateTime.class, but ZDT is preferred.
Then if that does not work, complain to your DB and/or JDBC driver because they're messing up and making it next to impossible to do timezone stuff properly when interacting with that DB from the java side.
Actually, the DB should store just a moment-in-time
If truly the time being stored represents the notion of an 'instant in time' and not so much 'as humans would ever talk to you about it', then there are data types for that too, but convert your java.sql.Timestamp object to a java.time.Instant asap (via .toInstant()), or straight up ask for it: resultSet.getObject("colName", Instant.class) and have java and the db line up the datatypes straight away.
Eh, whatever. Cures are for wussies, just work around it
Eh, well, the only thing you really need to do then is not to magically add 4 hours. This will do it:
ZonedDateTime dateTime = timestamp.toInstant()
.atZone(ZoneOffset.UTC)
.withZoneSameInstant(ZoneId.of("America/New_York"));
even if the tz stored in the DB is something else (it'll then give you that instant in time, but in new york, e.g. if the db has stored 'midnight in amsterdam', this will give you a time 6 hours earlier (or possibly 7 or 5, there are a few days in the year where things go ape due to US and europe having different shift days for daylight savings).
The format that you have used is not correct. I hope you will be able to understand the difference by comparing your pattern with mine. The reason why I've presented the parsing logic is that you have not made it clear the type of date-time. Whatever type it may be, it looks like you have a date-time string, Thu Aug 27 2020 00:00:00 GMT-0400 (Eastern Daylight Time) which you want to parse into ZonedDateTime and display the same into the pattern of the date-time string you have. I guess, the main problem you are having is how to format the ZonedDateTime instance into the same form.
Do it as follows:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
public class Main {
public static void main(String[] args) {
// Given date-time string
String dateStr = "Thu Aug 27 2020 00:00:00 GMT-0400 (Eastern Daylight Time)";
// Define the formatter for parsing
DateTimeFormatter parsingFormat = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd uuuu HH:mm:ss zX")
.appendLiteral(" (")
.appendGenericZoneText(TextStyle.FULL)
.appendLiteral(")")
.toFormatter();
// Parse the given date-time into ZonedDateTime
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr, parsingFormat);
// Display in default format [i.e. zonedDateTime.toString()]
System.out.println(zonedDateTime);
// Define the formatter for output
DateTimeFormatter outputFormat = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd uuuu HH:mm:ss z")
.appendLiteral(" (")
.appendPattern("zzzz")
.appendLiteral(")")
.toFormatter();
// Get the string representation in the custom format
String strDate = zonedDateTime.format(outputFormat);
// Display the string representation in the custom format
System.out.println(strDate);
}
}
Output:
2020-08-27T00:00-04:00[America/New_York]
Thu Aug 27 2020 00:00:00 GMT-04:00 (Eastern Daylight Time)
Note: By any chance, if you also have difficulty to convert the timestamp into ZonedDateTime, you can refer other answers on this page and use this answer to solve the problem with formatting.
java.time
I recommend that you use java.time, the modern Java date and time API, exclusively for your date work. Instead of getting a Date or Timestamp from your database, since JDBC 4.2 (in the case of MySQL that’s many years now) get a modern LocalDate from your result set. An example:
PreparedStatement ps = yourDatabaseConnection.prepareStatement("select your_date from your_table;");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
LocalDate date = rs.getObject("your_date", LocalDate.class);
// Do something with date
}
A LocalDate is a date without time of day and without time zone. So this will relieve you of all time zone trouble.
If you want to print the start of the day in North American Eastern time zone to the user in the format used in the question:
DateTimeFormatter etFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy 'at' hh:mma v");
ZoneId zoneId = ZoneId.of("America/New_York");
LocalDate date = LocalDate.of(2020, Month.AUGUST, 27);
ZonedDateTime startOfDay = date.atStartOfDay(zoneId);
String result = startOfDay.format(etFormat);
System.out.println(result);
Output from this example is:
08/27/2020 at 12:00AM ET
Do use pattern letter v for time zone in the format pattern rather than hard-coding ET. The latter will produce false and confusing results when one day a junior programmer feeds a ZonedDateTime in an other time zone into the code.
What went wrong in your code?
It’s not clear to me how you got your date from your database. Apparently date even though declared a Date was really a Timestamp (a bad practice since the inheritance relationship between the two classes is really one of implementation, not a conceptual one) denoting the start of the day in UTC. toLocalDateTime() is a dangerous and often meaningless call: it uses the time zone of the JVM for converting the Timestamp to a LocalDateTime. At 0:00 UTC it is 8 PM the evening before in Eastern time zone, so your LocalDateTime becomes 2020-08-26T20:00. Next atZone(zoneId) only gives the correct time because zoneId happens to coincide with the JVM’s time zone used in the previous step.
Link
Oracle tutorial: Date Time explaining how to use java.time.
The date is converted with timezone set to GMT.
final static String datePattern = "EEE MM/dd/yyyy HH:mm:ss 'GMT'Z '('z')'";
DateFormat df = new SimpleDateFormat(datePattern, Locale.getDefault());
simpledateformat.setTimeZone(TimeZone.getTimeZone("GMT"))
simpleDateFormat.format(givenDate)

DateFormat returning wrong hour

I am trying to parse the string "20/08/18 13:21:00:428" using the DateFormat class and a formatting pattern of "dd/MM/yy' 'HH:mm:ss:SSS". The Timezone is set to BST.
The date returned for the above is correct but the time is getting returned as 08 for the hours instead of 13 - "Mon Aug 20 08:21:00 BST 2018"
The following snippet prints the date and time just mentioned:
String toBeParsed = "20/08/18 13:21:00:428";
DateFormat format = new SimpleDateFormat("dd/MM/yy' 'HH:mm:ss:SSS");
format.setTimeZone(TimeZone.getTimeZone("BST"));
Date parsedDate = format.parse(toBeParsed);
System.out.println(parsedDate);
Is this something to do with my timezone or have I misunderstood the pattern?
BST is Bangladesh Standard Time. The correct time zone to use is "Europe/London" if you want automatic summer time, or "UTC+1" if you want British Summer Time always.
See https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#SHORT_IDS
java.time
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/uu H:mm:ss:SSS");
String toBeParsed = "20/08/18 13:21:00:428";
ZonedDateTime dateTime = LocalDateTime.parse(toBeParsed, formatter)
.atZone(ZoneId.of("Europe/London"));
System.out.println(dateTime);
Output from this snippet is:
2018-08-20T13:21:00.428+01:00[Europe/London]
What went wrong in your code?
While I always recommend against the long outdated and poorly designed classes Date, TimeZone and DateFormat, in this case they are behaving particularly confusingly. Printing a Date on a JVM with Europe/London as default time zone gives time zone as BST if the date is in the summer time part of the year:
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
Date oldFashionedDate = new Date();
System.out.println(oldFashionedDate);
Mon Aug 20 15:45:39 BST 2018
However, when I give time zone as BST, Bangladesh time is understood, but it comes out with the non-standard abbreviation BDT:
TimeZone.setDefault(TimeZone.getTimeZone("BST"));
System.out.println(oldFashionedDate);
Mon Aug 20 20:45:39 BDT 2018
(I have observed this behaviour on Java 8 and Java 10.)
Another lesson to learn is never to rely on three and four letter time zone abbreviations. They are ambiguous and not standardized.
BST may mean Brazil Summer Time or Brazilian Summer Time, Bangladesh Standard Time, Bougainville Standard Time or British Summer Time (note that S is sometimes for Standard, sometimes for Summer, which is typically the opposite of Standard Time).
BDT may mean Brunei Darussalam Time or British Daylight Time (another name for British Summer Time (BST)), but I wasn’t aware that Bangladesh Time was also sometimes abbreviated this way.
PS Thanks to DodgyCodeException for spotting the time zone abbreviation interpretation issue.
Link
Time Zone Abbreviations — Worldwide List

Joda DateTime gmt issue

I wanted to raise issue directly on joda github, but there was a hint, that i should at first ask it on stackoverflow.
If you try to use joda DateTime object in Greenwich Mean Time Zone (UK), you are getting bad DateTime value
Step to reproduce
Write method like this
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z");
DateTime dt = formatter.parseDateTime("2014-02-16T07:00:00.000Z");
System.out.println(dt);
Expected value
2014-05-16T07:00:00.000+00:00
Actual value
2014-05-16T07:00:00.000+Z
In UK, time is changed every 29 March. This is the last day in year, when time is +00:00. From 30 march till next time change there is +01:00.
If think that joda has problem with processing +00:00 gmt.
UK Time is not GMT... If you want UK time, use a full timezone name: Europe/London.
UK could be in GMT or BST based on the time of the year. The timezone can be mentioned like below:-
DateTime dt = formatter.parseDateTime("2014-02-16T07:00:00.000Z").withZone(DateTimeZone.forID("Europe/London"));

Convert Time from one time zone to another using Java 8 Time

I am trying to convert Date with GMT +5:30 to EST with java 8 ZonedDateTime.
String inputDate = "2015/04/30 13:00";
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm", Locale.US);
LocalDateTime local = LocalDateTime.parse(inputDate, sourceFormatter);
// local : 2015-04-30T13:00
//Combining this local date-time with a time-zone to create a ZonedDateTime.
ZonedDateTime zoned = local.atZone(TimeZone.getTimeZone("GMT+5:30").toZoneId());
// zoned : 2015-04-30T13:00+05:30[GMT+05:30]
ZonedDateTime zonedUS = zoned.withZoneSameInstant(TimeZone.getTimeZone("GMT-5:00").toZoneId());
// zonedUS : 2015-04-30T02:30-05:00[GMT-05:00]
I am expecting 3:30 AM EST but what I am getting is 2:30 AM EST as 1 PM IST= 3:30AM EST. What am I missing?
It seems that whatever service you found was being over-helpful in interpreting what you meant and assumed North American Eastern Daylight Time (EDT) when you specified EST (Eastern Standard Time). Most, not all of the places using EST as standard time are using daylight saving time and hence were on EDT or offset UTC-04:00 on the date you use, April 30, 2015.
If it makes sense in your situation, you should always prefer to give time zone in the region/city format, as Asia/Kolkata and America/New_York. If you intended Eastern Time as in New York or Montréal, one may say that your “time zone” of GMT-5:00 was wrong and the cause of your unexpected result.
So your code becomes for example:
String inputDate = "2015/04/30 13:00";
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm", Locale.US);
LocalDateTime local = LocalDateTime.parse(inputDate, sourceFormatter);
// local : 2015-04-30T13:00
//Combining this local date-time with a time-zone to create a ZonedDateTime.
ZonedDateTime zoned = local.atZone(ZoneId.of("Asia/Kolkata"));
// zoned : 2015-04-30T13:00+05:30[Asia/Kolkata]
ZonedDateTime zonedUS = zoned.withZoneSameInstant(ZoneId.of("America/Montreal"));
// zonedUS : 2015-04-30T03:30-04:00[America/Montreal]
I have made one other change: When using the modern classes from java.time, there is no point in also using the outdated TimeZone class, so I have taken that out. The code is slightly simpler, and more importantly, ZoneId.of(String) includes validation of your time zone string so you will discover any spelling error in the time zone name (like when I just happened to type a ( instead of the / in Asia/Kolkata — such happens all the time).
Most of the above has already been said in comments by Jon Skeet and others. I thought it deserved to go into an answer so it’s plain to see that the question has been answered.
Though the question is old, felt like I could add more to the accepted answer.
A ZonedDateTime is different from an OffsetDateTime.
I would prefer to use ZonedDateTime when I'm getting a time for a specific location like "Asia/Kolkata", "Asia/Shanghai", "US/Pacific" (this time zone will change depending on the day of the year because of Daylight savings).
To illustrate with an example,
var pacific = ZonedDateTime.of(2020,11,01,1,59,0,0,ZoneId.of("US/Pacific"))
var afterAnHour = pacific.plusHours(1)
This will give me a time of
2020-November-01 01:59:00.000 AM -07:00[US/Pacific]
And if i add an hour to it, it will give me a time of
2020-November-01 01:59:00.000 AM -08:00[US/Pacific]
You can see that the hour component is same even after adding an hour to the time. This is because the daylight savings time has kicked in and the time zone is shifted from -07:00 to -08:00.
Now if i use an OffsetDateTime look what happens.
var offsetNow = OffsetDateTime.of(2020,11,01,1,59,0,0,ZoneOffset.of("-07:00"))
var offsetAfterAnHour = offsetNow.plusHours(1)
The offsetNow will be,
2020-November-01 01:59:00.000 -07:00
And adding an hour to it will be,
2020-November-01 02:59:00.000 -07:00
you can see that the hour component has become 2 after adding an hour.
The key point is a ZonedDateTime uses ZoneRules to calculate important properties like Daylight savings time so that it can adjust the time zone accordingly.
While the OffsetDateTime will not change the zone offset for anything.

Java and Windows Regional Settings

Sorry if this is a re-post. I did not find a suitable answer.
Please ignore best practices and use of deprecated apis.
I want to format the following date "Mon May 19 02:16:52 EDT 2003" into "MM/dd/yyyy HH:mm:ss". Again, I only want to change the output format and this is being executed from my laptop which is in EST.
My Windows Regional Settings are:
Current time zone: Eastern Daylight Time
Timezone: (GMT-05:00) Eastern Time (US & Canada)
Automatically adjust clock for daylight saving changes: Checked
This java code does it:
Date aDate = new Date("Mon May 19 02:16:52 EDT 2003");
SimpleDateFormat sdf = new SimpleDateFormat( "MM/dd/yyyy HH:mm:ss");
System.out.println("Formatted Date: " + sdf.format(aDate));
Output is
Formatted Date: 05/19/2003 02:16:52
Now I change the windows setting to the following (only uncheck the DST setting)
Current time zone: Eastern Daylight Time
Timezone: (GMT-05:00) Eastern Time (US & Canada)
Automatically adjust clock for daylight saving changes: Checked
Output is:
Formatted Date: 05/19/2003 01:16:52
Questions:
Why is the output off by an hour?
Does java use Windows DST settings even when formatting? I though java maintains its own data for DST settings times for various timezones.
Internally, dates are stored by a long value that represents the number of milliseconds past some epoch. When you clicked off the daylight savings time on you machine, you changed your timezone. So Java is using the same time number with a different timezone and that's why you have what you have
Because the summer time is one hour ahead of the normal one. Set your calendar to January and you'll see no difference.
When you create the Date Java will internally store the value as GMT. When you format the date to a String Java will use your computers time zone to convert the date to your local time zone, which is the reason that you get different results. As an example, if I run the following code on my computer (with time zone CEST):
System.out.println(new Date("Mon May 19 02:16:52 UTC 2003"));
System.out.println(new Date("Mon May 19 02:16:52 EDT 2003"));
System.out.println(new Date("Mon May 19 02:16:52 PST 2003"));
I get the following results:
Mon May 19 04:16:52 CEST 2003
Mon May 19 08:16:52 CEST 2003
Mon May 19 12:16:52 CEST 2003
If you want more predictable results you could either not supply a time zone in your input (e.g. as long as input and output is performed in the same time zone it stays the same) or use a more reliable method where you specify which time zone to use:
SimpleDateFormat sdf = new SimpleDateFormat( "MM/dd/yyyy HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("EDT"));
It's because by default SimpleDateFormat prints date in your system time zone. To check it, add time zone output to format: "MM/dd/yyyy HH:mm:ss z" So you'll see in what time zone it actually prints the value.
If you want to control, in what timezone you want to format value, use DateFormat.setTimeZone(TimeZone z) method.

Categories