Let's say I have a ZonedDateTime:
ZonedDateTime zonedDateTime =
ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("US/Pacific"));
I would like to know which date/time it is let's say in Berlin.
I have two methods :
zonedDateTime.withZoneSameInstant(ZoneId.of("Europe/Berlin")); // probably this is the right one to get the corresponding date/time in Berlin
zonedDateTime.withZoneSameLocal(ZoneId.of("Europe/Berlin"));
The docs for the withZoneSameLocal method say: "The local date-time is only changed if it is invalid for the new zone..." and it's not clear when this really can happen (any example ? =)).
Which date/time each of them represents and what is the difference?
If you want to convert a timestamp from one timezone to another, use withZoneSameInstant(). withZoneSameLocal() will change the zone but keep all the other fields the same. The exception is where it would be an invalid date in that timezone.
For example,
ZonedDateTime dtUTC = ZonedDateTime.parse("2019-03-10T02:30:00Z");
ZoneId pacific = ZoneId.of("US/Pacific");
System.out.println(dtUTC.withZoneSameInstant(pacific));
System.out.println(dtUTC.withZoneSameLocal(pacific));
prints
2019-03-09T18:30-08:00[US/Pacific]
2019-03-10T03:30-07:00[US/Pacific]
The first line is the original timestamp converted to another timezone. The second tries to preserve the date/time fields, but 2:30 is not a valid time on that date (because of the Daylight Savings jump), so it shifts it by an hour.
Related
Let's assume that I have client's time saved in my database as 2020-09-22T10:50:37.276240900
I need to present this date in web-service for client app depending on client timezone, for example I need to add 2 hours to saved date if client lives in UTC+2 timezone.
So what am I doing for ?
Getting date from entity and adding timezone to time taken from database (startDate: LocalDateTime)
entity.startDate.atZone(ZoneId.of("Europe/Vienna"))
what gives me the value of ZonedDateTime 2020-09-22T10:50:37.276240900+02:00[Europe/Vienna]
This value is what I'm expecting for, basically "initial time plus 2 hours". After that I would to format this time to have output with this 2 hours of being added, some kind of this
12:50 22.09.2020
but when I do format like this
entity.startDate
.atZone(ZoneId.of("Europe/Vienna"))
.format(DateTimeFormatter.ofPattern(NotificationListener.EUROPEAN_DATE_FORMAT, Locale.ENGLISH))
where const val EUROPEAN_DATE_FORMAT = "HH:mm dd.MM.yyyy"
I get this output 10:50 22.09.2020 which looks like my format is not applied properly, so I cannot see my 2 hours.
So my questions are:
am I correct to adding timezone of client app in described way ?
how to apply timezone in more precise way and format this date to see timezone zone applied ?
LocalDateTime.atZone does not "move" the point in time. In fact it tries to present the point in time where the local time in the given timezone is exactly what the LocalDateTime shows.
In other words: if your LocalDateTime represented 10:00 at some date, then the ZonedDateTime output of atZone will also represent 10:00 local time at the specified time zone (except in cases where that local time doesn't exist due to DST changes).
So if your stored time is actually in UTC, you need to add one more step:
ZonedDateTime utcTime = entity.startDate.atZone(ZoneOffset.UTC);
ZonedDateTime localTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Vienna"));
Alternatively you can avoid calculating the localTime each time and instead configure the DateTimeFormatter to use a given time zone (which means it'll do the necessary calculations internally) using DateTimeFormatter.withZone. If you do this then you can pass the utcTime to it directly.
An external API returns an object with a date.
According to their API specification, all dates are always reported in GMT.
However, the generated client classes (which I can't edit) doesn't set the timezone correctly. Instead, it uses the local timezone without converting the date to that timezone.
So, long story short, I have an object with a date that I know to be GMT but it says CET. How can I adjust for this mistake withouth changing my local timezone on the computer or doing something like this:
LocalDateTime.ofInstant(someObject.getDate().toInstant().plus(1, ChronoUnit.HOURS),
ZoneId.of("CET"));
Thank you.
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").
I am afraid you will not get around some calculations here. I'd strongly suggest to follow an approach based on java.time classes, but alternatively you might use the java.util.Calendar class and myCalendar.get(Calendar.ZONE_OFFSET) for those calculations:
https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#ZONE_OFFSET
Can someone please state when these two types of ways to adjust a ZonedDateTime to UTC differ?? If possible provide some test event date times also.
String eventDate = "2016-11-28T10:56:28+11:00"; // my example date time
ZonedDateTime zonedDateTime = ZonedDateTime.parse(eventDate.trim(),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"));
// defaulting to UTC Zone
//1st way
System.out.println(zonedDateTime.withZoneSameInstant(ZoneId.of("UTC")).toInstant());
//2nd way
System.out.println(zonedDateTime.toInstant());
my question was are these two ways any different for other date time inputs that might not necessarily have the same formats or timezone or offsets.
There cannot be any difference. You will always get the same instant from both ways.
The reason is: A ZonedDateTime always uniquely defines a point in time, an instant. After converting to another time zone using withZoneSameInstant the new ZonedDateTime will always define the same point in time, the same instant.
BTW deHaar is correct in the comment: Your string contains an offset from UTC, +11:00, and no time zone like for example Asia/Shanghai, so OffsetDateTime is a more appropriate class than ZonedDateTime for your purpose.
I've got various java.util.Date objects with values of this format: 2014-01-21 10:28:57.122Z. I would like to convert them all to ZonedDateTime objects.
According to this SO question, and ZonedDateTime's ofInstant(), one way is like so:
ZonedDateTime z = ZonedDateTime.ofInstant(dateObject.toInstant(), ZoneId);
The problem is, how do I figure out what to use for the ZoneId parameter? I can't use my system time (ZoneId.systemDefault()) because my Date objects all have different timezones.
Your java.util.Date object does not have a time zone. Dates are always stored internally in UTC.
When you parse a text string into a Date object, the parser applies a time zone, either as given in the text, as a parameter to the parser, or by default.
When you display a Date object, the formatter will generate the text as requested, in the time zone requested, whether the time zone is displayed or not.
So, without time zone information in the Date object, you must specify the time zone you want when converting to ZonedDateTime.
Better yet, parse the text directly to a ZonedDateTime, so it can remember the original time zone from the text.
I have a data source with joda time DateTime objects stored. I need to convert them into java ZonedDateTime objects, keeping the original timezone.
It is not sufficient to keep the offset since some DateTime objects represents daily repetitive tasks, and these tasks must occur at a specific time in a specific time zone for every date. They must thus follow the specified TimeZone transitions for example summer and winter time. I cannot tell the final usage of the DateTime objects, so I need to keep the Time Zone information on all objects to be safe.
How to convert from org.joda.time.DateTime to java.time.ZonedDateTime?
Will all
ord.joda.time.DateTimeZone.getId()
map to the id's available in
java.time.ZoneId
You should avoid providing each field separately if you are working with daylight saving transitions. Convert using epochMillis instead, as in the following example.
Instant instant = Instant.ofEpochMilli(dt.getMillis());
ZoneId zoneId = ZoneId.of(dt.getZone().getID(), ZoneId.SHORT_IDS);
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);
Otherwise you will lose one hour on the date of transition. For example, Germany transitioned from summer time (GMT+2) to winter time (GMT+1) on 29.10.2017 at 03:00 GMT+2, which becomes 02:00 GMT+1. On that day, you have 2 instances of 02:00 - an earlier one with GMT+2 and a later one with GMT+1.
Since you are working with ZoneIds and not with offsets, there's no way of knowing which one of the 2 instances you want. By default, the first one is assumed during conversion. Both 02:00 GMT+2 and 02:00 GMT+1 are going to be converted to 02:00 GMT+2 if you provide hourOfDay along with ZoneId.
Not all time-zone strings from Joda-Time will match java.time but the vast majority will as they are both based on the IANA tz data. Compare DateTimeZone.getAvailableIDs() to ZoneId.getAvailableZoneIds() to determine the mismatch. Additional identifiers can be mapped using ZoneId.of(String, Map).
To do the main conversion in the most efficient way, you have to pass in each field:
ZonedDateTime zdt = ZonedDateTime.ofLocal(
LocalDateTime.of(
dt.getYear(),
dt.getMonthOfYear(),
dt.getDayOfMonth(),
dt.getHourOfDay(),
dt.getMinuteOfHour(),
dt.getSecondOfMinute(),
dt.getMillisOfSecond() * 1_000_000),
ZoneId.of(dt.getZone().getID(), ZoneId.SHORT_IDS),
ZoneOffset.ofTotalSeconds(dt.getZone().getOffset(dt) / 1000));
Note the use of ZoneId.SHORT_IDS as the Map in this case.
For a simpler solution that handles most use cases, but at lower performance, use this:
ZonedDateTime zdt = dt.toGregorianCalendar().toZonedDateTime();