I try to convert a string into a datetime:
String dateString = "2015-01-14T00:00:00-04:00";
DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ");
DateTime dt = df.parseDateTime(dateString);
If I display dt.toDate()
I get: Tue Jan 13 23:00:00 EST 2015
So there is a time problem.
Without the DateTimeFormatter, I get the same issue.
It's getting the correct value - basically 4am UTC, which is midnight in a UTC offset of -04:00 (as per the original text), or 11pm on the previous day for EST (as per the displayed result).
The problem is that you're using java.util.Date.toString(), which always returns the date in the system time zone. Note that a java.util.Date only represents an instant in time - it has no notion of a time zone itself, so its toString() method just uses the system default.
If you want to retain the time zone information (or in this case, the offset from UTC information - you don't have a full time zone) then stick to DateTime instead of converting to Date. Ideally, avoid java.util.Date/java.util.Calendar entirely. Stick to Joda Time and/or java.time.*.
Related
I'm using "Asia/Bangkok" zone id.
That offset is from GMT UTC+07:00.
but when I did followings, then it is not +7:00 when set "01/01/1900 7:00:00.000"
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS");
Date date = dateFormat.parse("01/01/1900 7:00:00.000");
System.out.println(date);
System.out.println(date.getTimezoneOffset());
Date date2 = dateFormat.parse("01/01/1900 6:00:00.000");
System.out.println(date2);
System.out.println(date2.getTimezoneOffset());
The result is
Mon Jan 01 07:00:00 ICT 1900
-402
Mon Jan 01 06:00:00 ICT 1900
-420
I wondered if the offset had changed around 7:00 a.m. on January 1, 1900, so I looked it up on Wikipedia.
https://en.wikipedia.org/wiki/Time_in_Thailand
It was UTC+6:42, but from 1880 to 1920.
I have 3 questions.
Why it happen different time offset between "01/01/1900 7:00:00.000" and "01/01/1900 6:00:00.000"
Where can I see time zone history in Java.
How can I ignore different time offset in same Timezone.
-- additional question --
I understand that I should use LocalDateTime.
What is the best way to ignore offset and convert Date to LocalDateTime?
For example, in the following case, the value of convertedDate2 was converted based on an offset of -402.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy H:mm:ss.SSS");
LocalDateTime originalLdate = LocalDateTime.parse("01/01/1900 7:00:00.000", dateFormatter);
LocalDateTime originalLdate2 = LocalDateTime.parse("01/01/1900 6:00:00.000", dateFormatter);
System.out.println(originalLdate);
System.out.println(originalLdate2);
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS");
Date date = dateFormat.parse("01/01/1900 7:00:00.000");
Date date2 = dateFormat.parse("01/01/1900 6:00:00.000");
LocalDateTime convertedDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime convertedDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(convertedDate);
System.out.println(convertedDate2);
LocalDateTime convertedDate3 = LocalDateTime.parse(dateFormat.format(date), dateFormatter);
LocalDateTime convertedDate4 = LocalDateTime.parse(dateFormat.format(date2), dateFormatter);
System.out.println(convertedDate3);
System.out.println(convertedDate4);
The result is
1900-01-01T07:00
1900-01-01T06:00
1900-01-01T07:00
1900-01-01T05:42:04
1900-01-01T07:00
1900-01-01T06:00
If I convert it once to String and then to LocalDateTime, as in convertedDate3 and convertedDate4,
then I could convert as my expectation, but I wonder this is the most efficient way or not?
Java runtime timezone information for each version is available here
https://www.oracle.com/java/technologies/tzdata-versions.html
Inside the linked file (for a specific version) you can find links to the actual data used
https://www.iana.org/time-zones/repository/releases/tzcode2021a.tar.gz
https://www.iana.org/time-zones/repository/releases/tzdata2021a.tar.gz
https://www.iana.org/time-zones/repository/releases/tzdb-2021a.tar.lz
Inside the tzdata*.tar.gz you can find a file called asia which contains the data for Bangkok as well.
It contains these entries
# Thailand
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Bangkok 6:42:04 - LMT 1880
6:42:04 - BMT 1920 Apr # Bangkok Mean Time
7:00 - +07
Link Asia/Bangkok Asia/Phnom_Penh # Cambodia
Link Asia/Bangkok Asia/Vientiane # Laos
So the -402 timezone should be used for all dates before 1/4/1920, but it seems the implementation is using the -402 offset only from 1/1/1900 0:00:00.000 UTC (from 1/1/1900 6:42:04.000 in your timezone) and until 1/4/1920 in your timezone and -420 otherwise. I am not sure, if that is intended or a bug.
How can I ignore different time offset in same Timezone.
If you are actually using timezones in your application, then you should not ignore them.
However, if you are making an application that is intended to be used just in your local timezone, then you can use a DateTime class without timezone information, such as java.time.LocalDateTime.
Also worth noting: even if these timezones would be correct, the historical dates might still be inaccurate, due to modern time rules being applied for all time (see below). So in the end it depends on what your use case is.
A date-time without a time-zone in the ISO-8601 calendar system. The ISO-8601 calendar system is the modern civil calendar system used today in most of the world. It is equivalent to the proleptic Gregorian calendar system, in which today's rules for leap years are applied for all time. For most applications written today, the ISO-8601 rules are entirely suitable. However, any application that makes use of historical dates, and requires them to be accurate will find the ISO-8601 approach unsuitable.
java.util.Date and java.text.SimpleDateFormat are very old classes. Although they mostly work, they are difficult to use properly, especially where timezones are concerned.
Date.getTimezoneOffset is deprecated. Do not use deprecated methods.
The proper way to work with timezone rules is using the java.time, java.time.zone, and java.time.format packages:
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter dateFormatter =
DateTimeFormatter.ofPattern("MM/dd/yyyy H:mm:ss.SSS");
LocalDateTime date =
LocalDateTime.parse("01/01/1900 7:00:00.000", dateFormatter);
System.out.println(date);
System.out.println(zone.getRules().getOffset(date));
LocalDateTime date2 =
LocalDateTime.parse("01/01/1900 6:00:00.000", dateFormatter);
System.out.println(date2);
System.out.println(zone.getRules().getOffset(date2));
The entire history of a timezone is in the ZoneRules:
System.out.println();
zone.getRules().getTransitions().forEach(System.out::println);
System.out.println();
zone.getRules().getTransitionRules().forEach(System.out::println);
You also asked:
What is the best way to ignore offset and convert Date to LocalDateTime?
You can’t. It is not possible to convert a Date to a LocalDateTime without assuming a timezone.
A Date is a wrapper for the number of milliseconds since 1970-01-01 00:00:00 UTC. You cannot generate a LocalDateTime from that without knowing which timezone to apply to that millisecond count. For example, noon Eastern Time in the US is a different number of milliseconds since 1970 than noon Greenwich time.
You may not realize it, but when you use SimpleDateFormat, you are specifying a timezone. Every SimpleDateFormat has a timezone property. Since your code never set that timezone explicitly, your date format used the system’s default timezone.
That is one reason to avoid DateFormat and SimpleDateFormat: the implicit use of the default timezone leads to errors and confusing behavior (though it is predictable behavior). When you use the java.time package and its subpackages, there is no ambiguity, and far less chance of confusion.
DateTime dt = new DateTime("2014-09-15T21:20:14");
System.out.println(dt);
System.out.println(dt.plusMillis(581042272).toDateTime().toLocalDateTime().toDateTime(DateTimeZone.forID("GMT")));
the time in dt is in UTC, I want to set the time in dt plus milliseconds to GMT? However, the time is still printed as UTC (1 hour behind GMT). How can I set it so it's one hour in front?
2014-09-15T21:20:14.000+01:00
2014-09-22T14:44:16.272Z
I know the time is exactly one hour behind because I made this request at 15:44:16 GMT
Your DateTime is actually not in UTC - it's in the system default time zone. To fix it, you just need to tell it that the value you're passing in is in UTC:
DateTime dt = new DateTime("2014-09-15T21:20:14", DateTimeZone.UTC);
System.out.println(dt);
DateTime other = dt.plusMillis(581042272);
System.out.println(other);
Output:
2014-09-15T21:20:14.000Z
2014-09-22T14:44:16.272Z
Also note that you can't have made the request at 15:44:16 GMT, as that hasn't occurred yet. At the time I'm writing this, it's 16:05 British Summer Time, therefore 15:05 GMT. It's important to understand that the time zone in the UK isn't "GMT" - that's just the part of the time zone when we're not observing daylight savings.
If you want to convert to the UK time zone, you want:
DateTime other = dt.plusMillis(581042272)
.withZone(DateTimeZone.forID("Europe/London"));
For those that have trouble with converting datetime from a server to local datetime:
1.Make sure the server gives you a UTC time, meaning, the format should contain a timezone.
2.Convert with pattern, if the api does not give you an timezone, then you might get an exception because of the last 'Z'.
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
DateTime dt = formatter.parseDateTime(currentPost.postDate);
3.to check the time offset (optional)
DateTimeZone ActualZone = dt.getZone();
4.Convert to local time
TimeZone tz2 = TimeZone.getDefault();
DateTime localdt = new DateTime(dt, DateTimeZone.forID(tz2.getID()));
(if you control the API yourself, and it happens to be an asp.net api, check this, to set the Kind of the datetime, even though you might have saved it as UTC time in the database, you will send the datetime with the default server timezone)
val marketCentreTime = timeInAnotherTimezone.withZone(DateTimeZone.forID("yourCountryName/andyourCityName"));
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.
I have a load of dates that I'd like to store in a database running on a server using BST:
2015-09-23
2024-05-07
2024-03-13
However they are stored in the DB as:
2015-09-23 01:00:00
2024-05-07 01:00:00
2024-03-13 00:00:00 <-- I need this to be 01:00:00
The values are converted to Date prior to being stored in the DB. I noticed the following when debugging:
TimeZone timeZone = Calendar.getInstance().getTimeZone();
System.out.println(timeZone.getDisplayName(false, TimeZone.SHORT));
System.out.println(new SimpleDateFormat("zzz").format(new Date()));
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZone(DateTimeZone.UTC);
System.out.println(formatter.parseDateTime("2015-09-23").toDate());
System.out.println(formatter.parseDateTime("2024-05-07").toDate());
System.out.println(formatter.parseDateTime("2024-03-13").toDate());
The first two dates are using BST and the last one is GMT. Is is possible to make them all use the same time zone?
GMT
BST
Wed Sep 23 01:00:00 BST 2015
Tue May 07 01:00:00 BST 2024
Wed Mar 13 00:00:00 GMT 2024
First of all, keep in mind that java.util.Date doesn't have a timezone (more details about it can be read here).
What happens is that Date.toString() method uses the system's default timezone to print its value (check the value of TimeZone.getDefault() in your JVM, it'll probably be Europe/London).
And in Europe/London timezone, the offset is equals to UTC in the winter (which is printed as GMT) and is +01:00 in the summer (which is printed as BST, aka British Summer Time). These different 3-letter names denotes the offset change, but it doesn't mean the dates "changed" their timezone.
Also consider that timezone is not only the offset or the name, but the set of all offset changes that occur in a region during history (when the changes occur, and the offsets before and after each change).
So, the dates doesn't have different timezones, because:
In the same timezone there can be more than 1 offset. And some changes in the offset cause the change in the 3-letter name - although the use of these 3-letter names is widely used, they're ambiguous and not standard.
java.util.Date doesn't have a timezone, so it can't change it.
If you want to save these objects in a DB, what you should care about is the timestamp (the number of milliseconds from 1970-01-01T00:00:00Z), which is preserved when converting to Date.
If you check the timestamp millis in the objects created, you'll see that it wasn't changed:
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZone(DateTimeZone.UTC);
DateTime d1 = formatter.parseDateTime("2015-09-23");
DateTime d2 = formatter.parseDateTime("2024-05-07");
DateTime d3 = formatter.parseDateTime("2024-03-13");
// comparing timestamp millis between DateTime and java.util.Date
System.out.println(d1.getMillis() == d1.toDate().getTime());
System.out.println(d2.getMillis() == d2.toDate().getTime());
System.out.println(d3.getMillis() == d3.toDate().getTime());
All 3 cases above prints true, meaning that they represent the same instant in time (so the dates hasn't changed).
Actually, you can see that all DateTime objects were in UTC:
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
This prints:
2015-09-23T00:00:00.000Z
2024-05-07T00:00:00.000Z
2024-03-13T00:00:00.000Z
Conclusion:
you can save the Date objects without any problem, as their values are correct
if you want to display the dates to the user, you can use the DateTime objects (and use a DateTimeFormatter if you want a different format), because they don't use the default TimeZone in the toString() method.
Try this:
SimpleTimeZone UTCTimeZone = new SimpleTimeZone(0, "UTC");
TimeZone.setDefault(UTCTimeZone);
All the date object will use UTC as default timezone for you backend code
I thought 2011-10-23 12:00:00 would remain the same as UTC and that the Converted date would be 2011-10-23 17:00:00.
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dt = formatter.parse("2011-10-23 12:00:00");
LocalDateTime ldt = new DateTime(dt).withZone(DateTimeZone.UTC).toLocalDateTime();
LOGGER.warn("Original date: " + ldt.toDateTime().toDate().toString());
DateTime cvtldt = ldt.toDateTime(DateTimeZone.forID("-05:00"));
LOGGER.warn("Converted date: " + cvtldt.toLocalDateTime().toDateTime().toDate().toString());
I don't understand why the output is minus one hour?
Original date: Sun Oct 23 11:00:00 BST 2011
Converted date: Sun Oct 23 11:00:00 BST 2011
You're using Date.toString() which always uses the local time zone. See how your string contains "BST"?
Ideally, stick to just Joda Time for as much of the time as you can:
Parse with the Joda Time formatters
Don't convert back to Date unless you need to
Don't use Date.toString() if you can possibly avoid it; you have no control over its format.
It's not clear what you're really trying to achieve, but you almost certainly don't want to do this many conversions. For example, you're calling toLocalDateTime() followed by toDateTime() again - which means it's using the system default time zone, after you'd carefully specified UTC in the previous conversion...
Your code contains the following conversions (in this order):
String to Date
Date to DateTime
DateTime to DateTime in UTC
DateTime to LocalDateTime (*)
LocalDateTime to DateTime
DateTime to Date
Date to String
(From the results at *) LocalDateTime to DateTime
DateTime to LocalDateTime
LocalDateTime to DateTime
DateTime to Date
Date to String
What do you think the chances of all those conversions being both necessary and correctly specified are? ;)
Original has changed because conversion to the UTC is done with respect to HOST time zone, so it should change.
Converted had changed, in fact it's the problem of accessing method.
You are getting base time and the modifier is stored in other field.
Try going into debugging mode and you will see that after conversion cvtldt
has toString with modifier.
Regards,
Grzesiek