Timezone confusion with getRawOffset method - java

One of my project implemented the below method, i am looking into the one of the date issue and trying to understand below method which converts given date to GMT, but confused with the output.
Input Date value : 2010-11-29 04:00:00.0
Output Date value : Sun Nov 28 20:00:00 PST 2010
My machine is running in pacific timezone(PST), if it is returning GMT, i would expect "2010-11-29 11:00:00.0", can you please clarify what is the purpose of getRawOffset() method and why it is returning that output?
public static Date convertToGMT(Date date) {
TimeZone jvmTimeZone = TimeZone.getDefault();
long newTime = date.getTime() + jvmTimeZone.getRawOffset();
if (jvmTimeZone.inDaylightTime(date)) {
newTime = newTime + jvmTimeZone.getDSTSavings();
}
return new Date(newTime);
}

PST is UTC-8, therefore getRawOffset() returns a negative value:
2010-11-29 04:00:00.0 + (-8 hours) = 2010-11-28 20:00:00.0
However, the whole thing you are trying to do is wrong.
Date represents an instant, a point on the timeline that's not associated with any timezone. Therefore it makes no sense to convert Date from one timezone to another. The only thing you can do with Date is to convert it to local date/time in certain timezone, and vice versa.
Also I'd suggest you to use Joda Time too. Jode Time makes distinction between an instant (DateTime) and a local representation of that instant (LocalDateTime) more clear.

The code is just crap because a java.util.Date is always GMT. It is never a local timestamp so trying to convert it from an imaginary local timestamp to GMT is conceptual nonsense.
The original intention was probably to misuse a Date as a kind of local timestamp (in contradiction to its specification) that is as thin wrapper around local time millis. Remember following relation: [UTC-millis] + [offset-millis] = [local-millis] I would just have used a long primitive for this calculation, not j.u.Date.
So you can see many inconsistencies in the code. The newTime variable appears to be a kind of local millis but is wrapped then as j.u.Date and returned result of a method which pretends to convert to GMT (more chaos is hardly possible).
Edit 8 years later when we have now got the java.time-package.
An instance of java.util.Date can be converted to a java.time.Instant. New conversion methods had been added to java.util.Date for this purpose. And we should all know that an instant is related to GMT, and nothing else. So will someone try to tell us that following code makes any sense?
public static Instant convertToGMTNew(Instant instant) {
java.util.Date date = java.util.Date.from(instant);
return convertToGMTOld(date).toInstant(); // using the method above
}
Not really. It is the same error. The new changed!!! instant would indeed be based on a fake calculation. Instant and java.util.Date have no conversion methods involving any zone calculations for a good reason. Both are just related to GMT and not to any "local" instants.

Related

Set timeZone in Date Java

Trying to set the timeZone in Date( I know Date is deprecated)
LOG.error(modifiedDate + "Date in service"); //Mon Sep 21 06:15:00 CDT 2020
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.ENGLISH);
format.setTimeZone(TimeZone.getDefault());
String dateString = format.format(modifiedDate);
LOG.error(dateString + " dateString"); //2020-09-21 06:15:00 -0500
Date date = format.parse(dateString);
LOG.error(date + " date after parsing"); //Mon Sep 21 06:15:00 CDT
timeZone isn't changing after the parse, what am I missing?
A java.util.Date object is lying. It's not a date. It is a moment in time, and it has no timezone information. It's just a wrapper around millis-since-epoch. It physically doesn't have the neccessary fields to store that.
(let's stop calling an instance of j.u.Date a 'date', because it just isn't one. You might as well call all instances of Integer a 'string', and all files 'a Grandma', about as sane). Let's call them by their right name: "crappy instant".
What you've done is set the timezone of the formatter object. This is working as intended: When you format this crappy instant object with your formatter-with-timezone-info-attached, you get the 'instant in time', translated to human-consumable form, with timezone info, adjusted for that timezone.
Then you ask to parse it back in but that's an issue: that's a very broad job; 'parse this bunch of characters into a "crappy instant"'.
In this case, the bag o' chars you've given includes timezone info already so that will be used. Remember, Date objects are crappy instants - they contain no timezone!!
Which should then raise some eyebrows - as last act you log date, which effectively logs the result of invoking the toString() method on your crappy instant instance. This will.... create a formatter with the system default timezone and use that to render it in human form, but make no mistake, that is NOT actually what is stored in that crappy instant object you have. All that is stored in there is 1600686900000. That's it.
I strongly suggest you follow deHaar's comment: No, you don't want crappy instant. If you have a value handed to you of type crappy instant (java.util.Date), convert it to Instant immediately.
If you must hand off a calculated time value back to some method or interface or field or whatnot and must be in date form, convert your nice instant instance (java.time.Instant) back to a crappy instant form.

Possible bug in Java 8 SimpleDateFormat? [duplicate]

This question already has answers here:
Get GMT Time in Java
(12 answers)
How to Parse Date from GMT TimeZone to IST TimeZone and Vice Versa in android
(4 answers)
Getting the current time millis from device and converting it into a new date with different timezone [duplicate]
(2 answers)
How to set time zone of a java.util.Date?
(12 answers)
Closed 3 years ago.
Java 8 here, I have the following code:
public class PossibleBug {
public static void main(String[] args) {
new PossibleBug().run();
}
public void run() {
buildDate("20181205");
}
public Date buildDate(final String yyyyMmDd) throws ParseException {
TimeZone expectedTz = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.setTimeZone(expectedTz);
TimeZone actualTz = sdf.getTimeZone();
Date answer = sdf.parse(yyyyMmDd);
return answer;
}
}
So pretty basic stuff:
create a SimpleDateFormat and set its timezone to EST
Use the SDF to parse a date string
Result should be a date in EST as well
However at runtime, look at the debugger results:
How is this possible?!?! sdf.parse(yyyyMmDd) is returning a date formatted in GMT. Is there something I'm missing on my end or is this a bug in SimpleDateFormat?
I am able to invoke buildDate and run it from inside a different class and it seems to work fine:
Date stores no timezone. It's essentially just a wrapper around a long, storing millis after epoch.
When you print it (or when your debugger invokes the toString() method to get a string representation to display), your JVM's default timezone is used, irrespective of how it was created.
Date, despite the name, doesn't model a date: it's an instant in time.
Given that your input is "20181205", don't use Date: use classes from java.time like java.time.LocalDate.
If you take a look at the Java-Doc for SimpleDateFormat.parse(), you can see that the TimeZone might be overwritten:
The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
The documentation says: "This parsing operation uses the calendar to produce a Date. All of the calendar's date-time fields are cleared before parsing, and the calendar's default values of the date-time fields are used for any missing date-time information. For example, the year value of the parsed Date is 1970 with GregorianCalendar if no year value is given from the parsing operation. The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations."
In short, SimpleDateFormat is a formatter/parser, not a utility for performing time zone conversions. If there's no TZ in the string you are parsing, you get the default value from Calendar.
Consider what would happen if you called setTimeZone, then parsed a string that actually contained a time zone itself? What would you expect to happen?
Also, note that Date doesn't contain a time zone. It's specifically defined as being the number of milliseconds since January 1, 1970, 00:00 UTC. Library functions apply a time zone when needed (like when converting to a String) and if you don't specify one, you'll get the default time zone. You see GMT because your default time zone is GMT or because your IDE always displays Date objects in GMT, and the person who said he got EST must have is default time zone set to EST.
In your case, you're parsing a string that does not contain a time zone at all. In fact, it doesn't even contain a time. Using Date to handle, uh, dates (I realize this is confusing, I mean dates without times), is likely to lead to mistakes, especially when your default time zone isn't UTC/GMT. I recommend using LocalDate and the LocalDate.parse method.

How to handle CST to CDT or vice versa using XMLGregorianCalendar

I had the below issue During daylight change CST-CDT reset.
Am getting the Input from Was8.5 server 2018-03-11-05.00 (UTC-5) as expected, but when it comes to WAS7 server, the below method returns Sun Mar 10 00.00.00 CST 2018 instead of Sun Mar 11 00.00.00 CDT 2018
/*
* Converts XMLGregorianCalendar to java.util.Date
*/
public static Date toDate(XMLGregorianCalendar calendar){
if(calendar == null) {
return null;
}
return calendar.toGregorianCalendar().getTime();
}
I know the server date/timezone reset didn’t take place properly, but in case if I want to get right Time when CST to CDT change or vise versa. How can I rewrite the code to convert XMLGregorianCalendar to java.util.Date in Java?
Something like,
If incoming request was CST(UTC-6), the toDate(XMLGregorianCalendar calendar) returns CDT (UTC-5). then I want toDate() should return CST (UTC-6).
the same way,
If incoming request was CDT(UTC-5), the toDate(XMLGregorianCalendar calendar) returns CST(UTC-6). then i want toDate() should return CDT(UTC-5).
java.util.Date doesn't have a timezone. It just have a long value that represents the number of milliseconds since unix epoch.
What you see (Sun Mar 10 00.00.00 CST 2018) is the result of toString() method, and it uses the JVM default timezone to convert the long value to a date and time in that timezone. See this article for more details:
https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/
Anyway, one way to really know what's happening is to check this long value:
long millis = calendar.toGregorianCalendar().getTimeInMillis();
And then you can print this value in UTC:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date(millis)));
Or, if you use Java 8:
System.out.println(Instant.ofEpochMilli(millis));
This will tell you the UTC instant that the Date corresponds to, so you can debug your code a little better than relying on Date::toString() method, which is confusing and misleading.
Regarding your main issue, I've tried to reproduce (I'm using Java 8 because it's easier to manipulate than using Date). First I created a date/time corresponding to 2018-03-11 in UTC-05:00, and I assumed the time to be midnight:
// March 11th 2018, midnight, UTC-05:00
OffsetDateTime odt = OffsetDateTime.parse("2018-03-11T00:00-05:00");
Then I converted this to America/Chicago timezone, which is a zone that uses CST/CDT:
// get the same instant in Central Time
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("America/Chicago"));
Then I printed this:
// print the date/time with timezone abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx z", Locale.US);
System.out.println(zdt.format(fmt)); // 2018-03-10 23:00 -06:00 CST
Note that the result is 2018-03-10 23:00 -06:00 CST: March 10th in UTC-06:00.
That's because in 2018, Daylight Saving Time starts only at 2 AM of March 11th. At midnight, DST has not started yet, so the offset is still UTC-06:00.
Anyway, your conversion code is correct, because Date just represents a point in time (a count of elapsed time since epoch) and doesn't have timezone attached to it. Perhaps the problem lies somewhere, and checking the millis value might help you to understand what's going on (my guess is that XmlGregorianCalendar sets the time to midnight when it's not present, which would explain the result of Sun Mar 10 00.00.00 CST 2018).
If that helps, the exact UTC instant where DST transition occurs (March 11th 2018 at 2 AM in UTC-06:00) corresponds to the millis value 1520755200000. If your dates in March 2018 have a value lower than that, it means they're before DST starts, and they'll be in CST.
My first suggestion is that you don’t need what you are asking for. As I see it, you’ve got a date and a UTC offset, and I don’t really see that the offset adds any useful information. Just take the date. I believe what has happened was that a point in time after the transition to summer time on March 11 was stripped of the time-of-day, but the UTC offset was kept for whatever reason or maybe for no reason at all. When giving the time at start of day (00:00), the offset disagrees with your time zone of America/Chicago (or Central Time Zone, but the ID in region/city format is unambiguous and recommended).
And don’t use java.util.Date for your date. That class is long outdated. Today we have so much better in java.time, the modern Java date and time API. Furthermore its LocalDate class is better suited for a date without time-of-day because this is exactly what it is, while a Date is really a point a in time, that is, a whole different story. Depending on taste conversion from XMLGregorianCalendar can happen in two ways.
The direct way
return LocalDate.of(calendar.getYear(), calendar.getMonth(), calendar.getDay());
With your XMLGregorianCalendar of 2018-03-11-05:00 the result is a LocalDate of 2018-03-11.
The indirect way via GregorianCalendar and ZonedDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toLocalDate();
The result is the same. The advantage of the latter is you don’t need to concern yourself with the individual fields of year, month and day-of-month. Among other things this means you don’t risk putting them in the wrong order.
If you do insist on keeping the time zone or UTC offset, at least take the offset. Sun Mar 11 00.00.00 CDT 2018 doesn’t make sense because March 11 at 00:00 hours DST was not yet in effect (it began at 02:00). Such a non-existing time will just confuse everyone. Convert your calendar object to OffsetDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toOffsetDateTime();
Result: 2018-03-11T00:00-05:00. This point in time exists.:-)
Since your calendar comes from a foreign system, you will probably want to validate it since any field may be undefined and return DatatypeConstants.FIELD_UNDEFINED. When using LocalDate.of(), you may decide that its argument validation is enough since it will object to DatatypeConstants.FIELD_UNDEFINED being passed as an argument. toGregorianCalendar() on the other hand will tacitly use default values, so when using it I would consider validation indispensable.
What went wrong in your code?
I ran your code, and similarly to iolus (see the other answer) I got Sat Mar 10 23:00:00 CST 2018. This the correct point in time. As iolus also explained, this is Date.toString rendering the point in time this way. The Date object itself doesn’t have a time zone or UTC offset in it. So I should say that your code was correct. It was just you getting confused by the toString method. Many have been before you, and the good solution is to avoid the Date class completely. Also I would think that your observations have nothing to do with any difference between WAS 7 and WAS 8.5.

Date.getTime() in Java returning different time in different server

I need to calculate a value that is based on dates. So I'm parsing the date first using date format class and then i'm using getTime() to get milliseconds. With those milliseconds i'll calculate some value. But getTime() is returning different values in different servers. We develop here in India, where i'm getting correct value but in US server i'm getting different value.
Scenario:
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
String now = "11/03/2018";
Date UsualDateformat = sdf.parse(now);
System.out.println(UsualDateformat.getTime());
}
}
Above is a sample code, but my actual code is a rule in a drl file (drools).
This program returns
"1541183400000" which i convert to date is "Sat 3 November 2018 00:00:00".,
but in US server im getting "1541217600000" equal to date "Sat 3 November 2018 09:30:00".
So when i use this value i'm getting marginal decimal point formatting issue.
How to resolve this issue?
Thanks in advance!
You need to set the time zone on SimpleDateFormat to be consistent across servers in different regions. For example:
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
....
}
}
You get these different values, because the time difference between your server in the US and the one in India is 9h 30.
This has nothing to do with floating points, but with time zones.
One way to solve this problem is to work always within the same time zone (e.g. India)
This code snippet might work for you,
public static String getGmtTime(String timezone) {
return ZonedDateTime
.now()
.withZoneSameInstant(ZoneId.of(timezone))
.toLocalDateTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
pass timezone as America/Los_Angeles to get time with proper timezone,
Timezones can be found here
java.time
I recommend you use java.time, also known as JSR-310, for this. The classes you use, Date and SimpleDateFormat, are long outdated, and SimpleDateFormat in particular has a reputation for producing surprising results, which one may say also happened in your case. The modern API is generally so much nicer to work with.
As I think you have suspected already, your issue comes from the fact that your servers are running different time zones, and the conversion of a date to millis since the epoch is a time zone dependent operation since the epoch is always the same point in time (Jan 1 1970 at midnight in UTC). As mweiss I am using UTC for the conversion to make sure it gives the same result no matter the time zone of the server:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String now = "11/03/2018";
LocalDate date = LocalDate.parse(now, dtf);
long millisInUtc = date.atStartOfDay(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println(millisInUtc);
As my code stands, it prints
1541203200000
This is between the values you got in India and US because UTC is between those two time zones. If you consider it more correct to use for example Asia/Kolkata time, simply substitute ZoneId.of("Asia/Kolkata") instead of ZoneOffset.UTC in the code, and you should get the same output as you already got when you ran your code on your server in India (please remember to rename the variable too).
My code is a bit longer than yours. I consider this an advantage in this case. Yes, indeed. The code using java.time is making explicit that we are using the time at the start of the day (0:00 midnight) and that we are using a time zone or offset for the conversion. This forces you as the coder to think about these issues, and you will be very unlikely to write code that produces results that differ unexpectedly across time zones, that is, your issue would never arise. At the same time it makes explicit to the reader that the operation depends on time zone, and that you have made a conscious choice of which zone to use. These advantageous are well worth a few more code lines.

Java Date getTime() method

I use getTime() method of Date class in java.
when I perform it in my local it return an value different with when I perform it in other pc whereas date value is same.
start.getTime()
The Date object is effectively just a container for milliseconds-since-the-Epoch values (milliseconds since Jan 1st 1970 at midnight UTC), which is the value you get from getTime. You've said "...whereas date value is same..." which suggests you're looking at other aspects of the Date object, like getHours and such, but note all those "Deprecated" notices on those methods. They're there for a reason.
If you use something that's designed to handle timezones well (the new java.time stuff; at a pinch the old java.util.Calendar, but "well" is stretching it), you could easily have a Date object in one timezone which those mechanisms say is (for instance) 2017-02-01 at 11:06, and another in another timezone which they also say is 2017-02-01 at 11:06, but get different values from getTime. That's because of the difference in timezones.
See javadoc for Date.getTime():
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object
Note that the String representation of the Date instance (the String returned by toString()) may differ depending on locale and time zone settings, but the getTime() method will always behave as described above.

Categories