SimpleDateFormat converting UTC to timezone incorrectly [duplicate] - java

This question already has answers here:
How to Format ISO-8601 in Java
(3 answers)
Closed 5 years ago.
I have a problem when converting the following ISO string 2017-09-01T01:00:00.000Z into a date.
I'm using SimpleDateFormat as follows
SimpleDateFormat stringToDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Date date = stringToDate.parse("2017-09-01T01:00:00.000Z");
The date object output has a date that looks like this
Fri Sep 01 01:00:00 MDT 2017
When I should get an output of
Fri Sep 01 01:00:00 UTC 2017
OR
Fri Aug 31 19:00:00 MDT 2017
It looks like it is not doing the timezone conversion correctly because the time is unchanged, but the timezone has been when neither or both should be changed.

The single quotes around the 'Z' mean that it's not interpreted as a time zone specifier for UTC: it's simply a literal Z in the string, which is discarded.
As you are not setting a timezone specifically on the SimpleDateFormat, the date is parsed in your JVM's default timezone.
Date.toString() uses your JVM's default timezone. There is no timezone in a Date. If you want to print in a specific timezone, you need to use a SimpleDateFormat to print it.

You should use the java.time classes for handling dates in modern Java.
Date doesn't store a timezone, and Date.toString() uses the system default timezone when rendering.

You could also try this function call to create Calendar instance, it should support yyyy-MM-dd'T'HH:mm:ss.SSSZ format.
Calendar cal = javax.xml.bind.DatatypeConverter.parseDateTime(ymd);
edit: hmm you said Android ok this class may not be available.

Related

Why does the LocalDateTime conversion to java.util.Date is shifting for very old date?

I am having trouble with converting old dates from java.time.LocalDateTime to java.util.Date
I tried a lot of variation and it still has the same shifted dates. I would assume that it is some weird calendar performed but it is failing.
Date to parse 1800-01-01 00:00:00
I used a very simple convert function.
Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
TimeZone
a.Converted via SimpleDateFormat
b.Converted via DateFormatter to LocalDateTime to java.util.Date
Asia/Tokyo
1800-01-01 00:00:00 JST
1799-12-31 23:41:01 JST
Europe/Brussels
1800-01-01 00:00:00 CET
1800-01-01 00:04:30 CET
Australia/Sydney
1800-01-01 00:00:00 AEST
1799-12-31 23:55:08 AEST
UTC
1800-01-01 00:00:00 UTC
1800-01-01 00:00:00 UTC
a. Convert String to java.util.Date via SimpleDateFormat
b. Convert String to java.time.LocalDatetime via DateFormatter, then convert it to java.util.Date
Now I see it only works for the UTC timezone, I cannot just change the software timezone as it will mess with the others. Anyone know any other way to convert a java.time.LocalDateTime to java.util.Date for an old day?
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
===== following is added after sweeper's answer to illustrate it is not for all ====
The curious thing is it only happens to really old dates, it does not happen to 1900-01-01 00:00:00 but have not check yet at which point the trouble started. I was thinking that maybe because of and adjustment / change at some point in 18XX year.
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1800-01-01T00:00:00")));
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format1.parse("1800-01-01T00:00:00").getTime()));
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1900-01-01T00:00:00")));
SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format2.parse("1900-01-01T00:00:00").getTime()));
Results to
+09:18:59
32400000
+09:00
32400000
Similar to this question, the old API disagrees with ZoneId.systemDefault() about what offsets those locations should be at on the date 1800-01-01.
You can see this in action:
System.out.println(ZoneId.of("Asia/Tokyo").getRules().getOffset(LocalDateTime.parse("1800-01-01T00:00:00")));
var format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(TimeZone.getTimeZone("Asia/Tokyo").getOffset(format.parse("1800-01-01T00:00:00").getTime()));
Output:
+09:18:59
32400000
As said in the linked post, +09:18:59 is the Local Mean Time in Japan, and 32400000ms is exactly 9 hours. This is because the old APIs don't support Local Mean Time. Note that Japan standardised their timezones in 1888, which explains why the outputs from the two APIs are consistent for 1900-01-01.
So ZoneId thinks the offset of Asia/Tokyo is 18 minutes and 59 seconds more than what TimeZone (which is used by SimpleSateFormat and Date.toString and so on) thinks it is.
This is exactly why there is a "shift" in the output. ldt.atZone(...) generates
1800-01-01T00:00:00+09:18:59
and toInstant turns this into an instant.
Assuming you are using Date.toString or some other old API that uses TimeZone to generate the string, the old API would think this instant is at +09:00:00 instead! What is the above date at +09:00:00? Well, just subtract the 18 minutes and 59 seconds (just like how you would subtract 9 hours to convert a UTC+9 date to UTC+0)!
And that's how you get:
1799-12-31 23:41:01
As for solutions, there really is nothing wrong with the "shifted" Date that you got. If you just convert it back to a ZonedDateTime and format it with DateTimeFormatter for display, everything should work as normal. In fact, if you can, consider not converting to Date at all, and use Instants instead.
If you cannot do that, I would suggest that you should not mix the two APIs.

LocalDate inconsistency

I am trying to produce a Date object (java.util.Date) from a LocalDate object (java.time.LocalDate) in which I have the following criteria:
Allow a parameter that can subtract a certain number of days from the Date object
Have the Date & Time be the date and time currently in UTC
Have the time at the beginning of the day i.e. 00:00:00
The Timezone stamp (i.e. CDT or UTC) is irrelevant as I remove that from the String
To meet this criteria, I have created a test program, however I am getting interesting results when I modify a certain property of the LocalDate. See code below:
public static void main (String args[]) {
Long processingDaysInPast = 0L;
LocalDate createdDate1 = LocalDate.now(Clock.systemUTC()).minusDays(processingDaysInPast);
LocalDate createdDate2 = LocalDate.now(Clock.systemUTC()).minusDays(processingDaysInPast);
System.out.println(createdDate1);
System.out.println(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC));
System.out.println(Date.from(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC)));
System.out.println((createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
System.out.println(Date.from(createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
}
Output:
2017-08-14
2017-08-14T00:00:00Z
Sun Aug 13 19:00:00 CDT 2017
2017-08-14
2017-08-14T05:00:00Z
Mon Aug 14 00:00:00 CDT 2017
When I add the value Date.from(createdDate1.atStartOfDay().toInstant(ZoneOffset.UTC)) I get the expected output of the date, with a 00:00:00 time field. However, if I do not add this parameter, such as: Date.from(createdDate2.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()) I get the resulting day before , at 19:00:00 why is this?
My main goal from this is to be able to capture a Date object, with the current UTC Date, and the Time zeroed out (StartOfDay).
When you do:
createdDate2.atStartOfDay().atZone(ZoneId.systemDefault())
First, createdDate2.atStartOfDay() returns a LocalDateTime, which will be equivalent to 2017-08-14 at midnight. A LocalDateTime is not timezone-aware.
When you call atZone(ZoneId.systemDefault()), it creates a ZonedDateTime with the respective date (2017-08-14) and time (midnight) in the system's default timezone (ZoneId.systemDefault()). And in your case, the default timezone is not UTC (it's "CDT", so it's getting midnight at CDT - just do System.out.println(ZoneId.systemDefault()) to check what your default timezone is).
To get the date at midnight in UTC, you can replace the default zone (ZoneId.systemDefault()) with UTC (ZoneOffset.UTC):
Date.from(createdDate2.atStartOfDay().atZone(ZoneOffset.UTC).toInstant())
Or (a shorter version):
Date.from(createdDate2.atStartOfDay(ZoneOffset.UTC).toInstant())
Of course you can also do the same way you did with createdDate1:
Date.from(createdDate2.atStartOfDay().toInstant(ZoneOffset.UTC))
They're all equivalent and will result in midnight at UTC.
Just a quick note: short timezone names like CDT or PST are not real timezones.
The API uses IANA timezones names (always in the format Region/City, like America/Chicago or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CDT or PST) because they are ambiguous and not standard.
There are lots of different timezones that can use CDT as abbreviation. This happens because a timezone is the set of all different offsets that a region had, has and will have during history. Just because many places uses CDT today, it doesn't mean they all used in the past at the same periods, nor that it'll be used by all in the future. As the history differs, a timezone is created for each region.

Format date by provided time zone in java [duplicate]

This question already has answers here:
Convert Date/Time for given Timezone - java
(16 answers)
Closed 2 years ago.
I need to format date time in java for provided time zone. E.g. mm/dd/yyyy for US and dd/mm/yyyy for DE. I have time zone and i can get zoneId, can someone help me. Many thanks.
Use the modern Java date and time classes for everything that has got to do with dates or times.
DateTimeFormatter usFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(Locale.US);
System.out.println(date.format(usFormatter));
DateTimeFormatter deFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(Locale.GERMANY);
System.out.println(date.format(deFormatter));
This will print something like
6/27/17
27.06.17
It’s not exactly the formats you asked for, but it’s the formats Java thinks are appropriate for those two locales. I’d try them, and if the users complaint, build my own DateTimeFormatter from a pattern string (like MM/dd/uuuu, for example).
I used a LocalDate for the date in the code, but the same code should work with a LocalDateTime, OffsetDateTime or ZonedDateTime.
If you meant to deduce the locale from the time zone in a ZonedDateTime, I don’t think you can do that reliably. There are often many countries in a time zone, each country having its own locale and its own way of formatting dates. Germany, for example, shares its time zone with Norway, Sweden, Denmark, Poland, France, Switzerland, Austria and Italy, Spain, Serbia and many others.
And if you meant to deduce it from the time zone in an oldfashioned Date object, you certainly cannot simply because a Date does not hold a time zone in it.
Hope this will help you,
import java.util.*;
import java.text.*;
public class DateDemo {
public static void main(String args[]) {
Date date1 = new Date();
System.out.println(date1);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Date date2 = new Date();
System.out.println(date2);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Date date3 = new Date();
System.out.println(date3);
}
}
Output:
Tue Jun 06 14:02:51 IST 2017
Tue Jun 06 08:32:51 UTC 2017
Tue Jun 06 03:32:51 EST 2017

Date Time conversion in user timezone

I have certain date field of a record in sqlite database that is saved at certain format.
To convert this date column to a certain format, I did some date formatting and add it in an array dbDates with the following code-
SimpleDateFormat dateIn = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
dbDates.add(dateIn.format(formatter.parse("2013-12-16T02:00:00.000Z");
dbDates.add(dateIn.format(formatter.parse("2013-12-15T01:30:00.000Z");
The dbDates as printed in console is now in the following format -
Mon Dec 16 02:00:00 GMT+05:30 2013
Sun Dec 15 01:30:00 GMT+05:30 2013
NOTE: As noticed, +05:30 is added at the end of each dbDate.
This caused a problem when I convert it to user timezone.
So adding this line ----
dateIn.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
Changes the output to -
Sun Dec 15 12:30:00 PST 2013
Sun Dec 14 12:00:00 PST 2013
However, the correct output should be:
Sun Dec 15 18:00:00 PST 2013
Sun Dec 14 17:30:00 PST 2013
So, there is +05:30 difference in the output. How to resolve that ?
The java.util.Date class bundled with Java has no time zone defined inside the instance. Yet its toString method renders a string using the JVM's default time zone. I'll bet your JVM is set to an Indian time zone, given to +05:30.
This is one of many reasons to avoid using the java.util.Date/Calendar classes. They are notoriously bad in both design and implementation.
Immediately download for yourself a copy of the Joda-Time library. Use that for all your business logic and calculations.
In Joda-Time, a DateTime instance does indeed know its own time zone.
When necessary to deal with other classes, convert to a j.u.Date by calling the toDate method on a DateTime instance.
Search StackOverflow.com for joda to find many examples.
In Java 8, either continue using Joda-Time or switch to the new java.time.* classes defined by JSR 310. The new classes are inspired by Joda-Time but are entirely re-architected.

Getting date in GMT from unix timestamp [duplicate]

This question already has answers here:
How can I get the current date and time in UTC or GMT in Java?
(33 answers)
Closed 9 years ago.
I have written the following code to get the date in GMT from a unix timestamp
private Date converToDate(String unixTimeStamp)
{
//unix timestamps have GMT time zone.
DateFormat gmtFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
gmtFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
//date obtained here is in IST on my system which needs to be converted into GMT.
Date time = new Date(Long.valueOf(unixTimeStamp) * 1000);
String result = gmtFormat.format(time);
return lineToDate(result, true);
}
this code upon execution has
Mon May 27 02:57:32 IST 2013
value in the date variable and
Sun May 26 21:27:32 GMT 2013
in the result variable , How do I directly get the value in result variable into date variable ?
This is the problem, conceptually:
//date obtained here is in IST on my system which needs to be converted into GMT.
Date time = new Date(Long.valueOf(unixTimeStamp) * 1000);
A Date doesn't have a time zone. This is the value you want. The fact that when you call toString() it converts it to your local time zone is irrelevant to the value that it's actually representing. A Date is just a number of milliseconds since the Unix epoch (1st January 1970, midnight UTC). So your whole method can be:
private static Date convertToDate(String unixTimeStamp)
{
return new Date(Long.valueOf(unixTimeStamp) * 1000);
}
You don't need any kind of formatter, as you're not really trying to get a textual representation.
I would advise you to use Joda Time for date/time work if you can, by the way - it's a much cleaner API.
A Date is just the wrapper for a long, which contains a number of milliseconds.
What you're seeing is the default toString() representation of the Date object, which uses your default timezone (IST) to transform the date into a readable string. If you want the date represented as a string using the GMT timezone, just do what you did: use a date format with the GMT time zone.
The Date object represents an instant on the universal timeline, and doesn't have any timezone.

Categories

Resources