Why time not converted according to time zone? - java

I have LocalDateTime that keeps value in UTC.
I want to convert it to local date and time according to a time zone,
here how I do it:
public LocalDateTime convertUTC2LocalDateTimeZone(LocalDateTime dateTime){
System.out.println("dateTime:" + dateTime);
ZonedDateTime zonedDateTime = dateTime.atZone(ZoneId.of("Canada/Mountain"));
System.out.println("zonedDateTime:" + zonedDateTime);
LocalDateTime ldt = zonedDateTime.toLocalDateTime()
System.out.println("ldt:" + ldt);
return ldt;
}
Output:
dateTime:2018-07-15T10:00:46
zonedDateTime:2018-07-15T10:00:46-06:00[Canada/Mountain]
ldt:2018-07-15T10:00:46
As you can see the ldt value is the same as input, no time conversion occurred.
Any idea why time conversion not occurred?

A LocalDateTime represents a "local time and date". In other words: it's something abstract like "January 1st 2020, 10:00 AM" without any time zone information.
It does not represent anything in UTC. There simply is no time zone information contained in it.
So it doesn't represent a "physical" or exact point in time. To do this you need to convert it to a ZonedDateTime by adding some time zone. The way you do in your code basically says: "Give me a ZonedDateTime object that represents the local time provided by this LocalDateTime in the given time zone".
That means this ZonedDateTime does represent a fixed point in time (i.e. you can calculate the milliseconds since the epoch, basically).
Then you ask "given that ZonedDateTime, what would the local date/time be?", which will just return the value that you initially put in without any modification.
To actually convert from UTC to some other timezone, you need to explicitly create a ZonedDateTime in the UTC timezone first:
create ZonedDateTime representing UTC
calculate ZonedDateTime in the target timezone
get a LocalDateTime from the ZonedDateTime created in #2.
So in code:
ZonedDateTime utcDateTime = dateTime.atZone(ZoneOffset.UTC); // #1
ZonedDateTime mountainDateTime = utcDateTime.withZoneSameInstant(ZoneId.of("Canada/Mountain")); // #2
LocalDateTime localDateTimeAtMountain = mountainDateTime.toLocalDateTime(); // #3

Related

How to get time and date from UTC to EST without worrying about Daylight Saving Time(DST)

I have one service which requires the data of date and time which I am collecting from my database.
The data in the database is stored according to the UTC time zone.
Now I want to get those data from the data and present it to the user in the time zone whatever the want it to be in or just so the data of time and data as per there systems default time zone.
As right now I have some hard coded thing that makes the changes if the Daylight Saving Time changes
For example I the DST starts it substracts 4 hours(as per EDT) from the data that is in the system and if the DST ends I have to manually change the substracted hour with 5(as per EST) hours and the present it to the user.
I am currently doing it as bellow
Here I m using
Date from java.util.Date
getObtainedDate is for getting data from the database
I have added this minus so that it will substract that amount of time from the date.
Instant ins = attachment.getObtainedDate().toInstant();
ins = ins.minus(5, Chronounit.HOURS); // without DST
Date alteredDate = Date.from(ins);
Instant ins = attachment.getObtainedDate().toInstant();
ins = ins.minus(4, ChronoUnit.HOURS); // during DST
Date alteredDate = Date.from(ins);
I want to get rid of this hard coded thing as I have to manually change the 5 and 4 whenever the DST starts and ends.
Thank you in advance
tl;dr
You said:
some time and dates data from the database and for which the time is stored in UTC timezone
and you said:
from UTC to EST without worrying about Daylight Saving Time (DST)
myResultSet
.getObject( … , OffsetDateTime.class ) // Returns a `OffsetDateTime` object, the appropriate class mapping to the standard SQL type `TIMESTAMP WITH TIME ZONE`.
.atZoneSameInstant( ZoneId.of( "America/New_York" ) ) // Returns a `ZonedDateTime` object.
If handed an object of the terribly flawed legacy class java.util.Date, immediately convert to its modern replacement class java.time.Instant. Use new conversion methods added to the old class.
Instant instant = myJavaUtilDate.toInstant() ;
ZonedDateTime zdt = instant.atZone( ZoneId.systemDefault() ) ;
Details
Your Question is confusing and convoluted. You may be trying too hard.
You said:
time and dates data from the database and for which the time is stored in UTC timezone
If your database stores moments as a date with time with an offset of zero hours-minutes-seconds from UTC, then your database table column must be of a type akin to the standard SQL type TIMESTAMP WITH TIME ZONE. If so, you should retrieve using the Java type OffsetDateTime.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
You said:
when the DST starts the data that I am collecting should start substracting
If you want to see that same moment through the wall-clock time of a particular time zone, apply a ZoneId to get a ZonedDateTime object.
Note that EST is not a real time zone name. Perhaps you meant America/New_York.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
If you want to use the JVM‘s current default time zone:
ZoneId z = ZoneId.systemDefault() ;
You said:
I am currently doing it as bellow
Instant ins = …
Instant represents a moment in UTC, always in UTC. But this class does not map to any standard SQL type. Use OffsetDateTime to exchange a moment (a specific point on the timeline) with your database.
You said:
Date alteredDate = Date.from(ins);
As for either of the Date classes, java.util.Date & java.sql.Date: (a) I don’t know which you intended, and (b) neither should be used. Both Date classes are terribly flawed, and are now legacy, supplanted by the modern java.time classes.
You said:
should start substracting 4 hours from the time as in EST the DST offset is -400 and when it ends it should substract 5 hours
No need for you to do the math. No need for you to track the DST cutover dates.
Do your logging, debugging, data storage, data exchange, and most of your business logic in UTC (an offset of zero).
Apply a time zone only for presentation to the user, and where required by a particular rule in your business logic.
By using ZoneId and ZonedDateTime classes, the DST cutovers and adjustments are handled for you.
I manage to get the time as per the timezone where the system is using this code.
This code also manages to change the offset as per the timezone.
I am here using
Date from java.util.Date
ZonedDateTime from java.time.ZonedDateTime
DateTimeFormatter from java.time.DateTimeFormatter
Instant from java.time.Instant
The code for solving this issue
Date date = attachment.getObtainedDate(); // for getting the date in the system
// Here I m setting the zone to system default
ZoneId zoneId = ZoneId.systemDefault(); // for setting the zoneId as per the location you want
ZonedDateTime zonedDateTime = Instant.ofEpochMilli(date.getTime()).atZone(zoneId);
// for formatting date and time
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm");
String requiredDate = zonedDateTime.format(dtf);
If you want to check the offset for the time zone while debugging for more information, you can add one more line to the code
ZoneOffset offset = zonedDateTime.getOffset();

Convert UTC offset date to different time zone

I would like to convert date with its UTC offset to the different timezone.
Suppose that I get 2021-06-14 06:56:00 (this is local date time) with UTC offset +3 hours and I need to convert this local date time to LosAngeles timezone (UTC -8 hours). In order to implement this, I wrote the following snippet:
public static LocalDateTime toPstTimeZone(LocalDateTime localDateTime, int utcOffset) {
final var pstUtcOffset = ZoneOffset.ofHours(-8);
return localDateTime
.atOffset(ZoneOffset.ofHours(utcOffset))
.withOffsetSameInstant(pstUtcOffset)
.toLocalDateTime();
}
need to check if this is valid approach to do.
Your approach is correct. However, I would suggest if possible to work to begin with, with ZonedDateTime or OffsetDateTime. in this case switching between timezones is much easier. For ZonedDateTime switching to different time zone is just one method: public ZonedDateTime withZoneSameInstant(ZoneId zone)

Convert UTC time to Europe/London timezone in java

I've current datetime in UTC but I need date time of timezone (Europe/London). I tried but everytime time is not adding instead of this offset is appending in current date time.
My code -
LocalDateTime utcTime = LocalDate.now().atTime(0,1);
System.out.println("utc time " + utcTime);
ZoneId europeLondonTimeZone = ZoneId.of("Europe/London");
ZoneOffset offset = europeLondonTimeZone.getRules().getOffset(utcTime);
OffsetDateTime offsetDateTime = utcTime.atOffset(offset);
System.out.println(offsetDateTime);
It will print:
"2021-06-18T00:01+01:00"
but I want
"2021-06-17T23:01"
as +01:00 is ahead in daylight saving time.
Thanks
If you just want the current time in Great Britain, there is no need to convert from UTC. You can have that time directly.
ZoneId europeLondonTimeZone = ZoneId.of("Europe/London");
OffsetDateTime offsetDateTime = OffsetDateTime.now(europeLondonTimeZone);
System.out.println(offsetDateTime);
Output when I ran the code just now:
2021-06-18T19:18:39.599+01:00
If you do need to have the UTC time first, avoid using LocalDate or LocalDateTime for that. The local in some java.time class names means without time zone or UTC offset. Prefer OffsetDateTime, which itself keeps track of its offset, as the name says. So when it’s in UTC, it “knows” this fact itself.
// Sample UTC time
OffsetDateTime utcTime = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println("UTC time: " + utcTime);
ZoneId europeLondonTimeZone = ZoneId.of("Europe/London");
OffsetDateTime offsetDateTime = utcTime.atZoneSameInstant(europeLondonTimeZone)
.toOffsetDateTime();
System.out.println("UK time: " + offsetDateTime);
UTC time: 2021-06-18T18:18:39.669Z
UK time: 2021-06-18T19:18:39.669+01:00
The atZoneSameInstant method converts from whatever offset the OffsetDateTime was in (in this case UTC) to the time zone passed as argument, thus typically altering the clock time (sometimes even the date).
What went wrong in your code?
A LocalDate contains a date without time of day only, so LocalDate.now() only gives you which day it is in the default time zone of your JVM (so not even which day it is in UTC), not the time of day. .atTime(0,1) converts that day to a LocalDateTime representing the time of 0 hours 1 minute, that is, 00:01, on that day, still without any time zone.
Also a ZonedDateTime not only knows its time zone but can also handle its time zone rules. So there is no reason for you to deal with the offset at a particular time yourself.
Finally LocalDateTime.atOffset() converts to an OffsetDateTime but neither changes the date nor the time of day. Since the LocalDateTime did not have any time zone, the method cannot be used for converting between time zones.

Java: Local Standard Time to Instant

A service like this has an explicit note that
All times are specified in Local Standard Time (LST). Add 1 hour to
adjust for Daylight Saving Time where and when it is observed.
Given a LocalDateTime object ldt, creating an instant is as simple as
Instant.of(ldt,zoneId). However, if I supply the ZoneId, then Instant will assume the daylight-savings (DST) adjustment has already been made, and ldt is with respect to DST, which is not the case.
What is the most Java-esque way to make the Instant's constructor apply the "standard" offset rather than taking into account the DST?
EDIT: I mean, short of hard-codedly checking, for the given year, the exact dates of DST transitions? Sure, I can get the offset as
ZoneOffset offset= ZoneOffset.of(zoneId) and then subtract offset.getTotalSeconds() from the given timestamp, to move to the "reference" time. But giving that reference-adjusted timestamp to Instant still
has the same problem of the DST.
EDIT: As has been suggested below, I tried this:
String str = String.format("%04d-%02d-%02dT%s:00", year, month, day, hourMinutes);
LocalDateTime ldt = LocalDateTime.parse(str);
Instant i1= ldt.toInstant(zoneId.getRules().getOffset(ldt));
Instant key= ldt.toInstant(zoneId.getRules().getStandardOffset(i1));
You can get the standard offset of a ZoneId by calling getRules() and then getStandardOffset().
However, you need an Instant. Because sometimes the standard offset changes too...
So one way is to first convert the LocalDateTime to Instant, using the offset at that LocalDateTime. Then we get the standard offset at that instant, and use that to get the actual Instant we want:
LocalDateTime ldt = ...;
Instant i = ldt.toInstant(someZoneId.getRules().getOffset(ldt));
Instant i2 = ldt.toInstant(someZoneId.getRules().getStandardOffset(i));
System.out.println(i2);
I think this wouldn't work for some specific local times if both the standard offset and the DST offset changes at the same time, because the first instant i is calculated with the offset before the transition. You can calculate i with the offset after the transition by doing this:
Instant i;
ZoneOffsetTransition transition = someZoneId.getRules().getTransition(ldt)
if (transition == null) {
i = ldt.toInstant(someZoneId.getRules().getOffset(ldt));
} else {
i = ldt.toInstant(transition.getOffsetAfter());
}
Update:
But even still, this will produce the wrong result for some really corner cases. The problem is that java.time does not provide a way of getting the transitions in standard offset.
The ZonedDateTime description states:
This class handles conversion from the local time-line of
LocalDateTime to the instant time-line of Instant. The difference
between the two time-lines is the offset from UTC/Greenwich,
represented by a ZoneOffset.
Convert the LocalDateTime object to ZonedDateTime with atZone using the zone identifier.
Then call the default toInstant method, which is inherited from the ChronoZonedDateTime class, to get an Instant object.

How to check LocalDateTime instance is local or UTC in java?

I have want to find whether my LocalDateTime instance holds local date&time or UTC date&time (like DateTime.Kind property in C#)
LocalDateTime date1=LocalDateTime.now(); // it is local
LocalDateTime date2=LocalDateTime.now(ZoneId.of("UTC")); // it is UTC
Anything like (date1.getKind() == Kind.UTC || date1.getKind() == Kind.Local) in Java?
The LocalDateTime object itself doesn't store the timezone information - it has only the date and time related fields: day, month and year; hour, minute, seconds and nanoseconds. But the now method uses a timezone or an offset to get the correct values for those fields.
That's because the answer to the questions "What day is today?" and "What time is it?" is not as simple as we might think.
It's common to think that the answer is as simple as taking a look at our calendar/cell phone/whatever and seeing the current date/time. But the technically correct answer is: "It depends".
It depends, basically, on where you are. At this moment, each place in the world has its own local date and time. For example, in July 5th, 2017: while it was 14h (or 2 PM) in São Paulo, it was 6 PM in London and 5 PM in UTC, but in Tokyo it was 2 AM of the next day (July 6th).
Each region in the world has specific rules to determine what's their local time during history, and of course it affects their local date.
And the concept that maps a country/city/region to these rules is a timezone.
That's why the now method needs a timezone. The ZoneId object loads all the timezone data to check what's the current date and time in that zone and adjust the day/month/year/hour/minute/second/nanosecond values accordingly. The version that receives no parameters (LocalDateTime.now()) will use the system's default timezone, so the API always uses some timezone in the end.
The timezone (or the offset, such as ZoneOffset.UTC) is used to get the correct values for day, month, year, hour, minute, second and nanosecond, and then - in the case of LocalDateTime and any other classes that don't keep the zone - discarded.
So, the concept might be a little different from what you're thinking. If I do:
// ZoneOffset.UTC is equivalent to ZoneId.of("UTC")
LocalDateTime date = LocalDateTime.now(ZoneOffset.UTC);
What this code does is: "take the current date and time in UTC, and get just the date and time fields, discarding the timezone/offset information".
When I ran this code, the current date/time in UTC was 2017-09-25T12:15:43.570Z, so the LocalDateTime has the value equivalent to 2017-09-25T12:15:43.570 (without any timezone information, just the date and time fields). If I call now() without arguments, it'll use the JVM default timezone (in my case, it's America/Sao_Paulo), and the value will be 2017-09-25T09:15:43.570.
So, with a LocalDateTime you can get the values, but you can't know from which timezone those values came from, because it doesn't keep this information.
If you want a UTC date, you must use another classes, designed to keep this information:
Instant.now() - this will always get the current UTC instant
OffsetDateTime.now(ZoneOffset.UTC) - with this you can query for date and time fields (such as getDayOfMonth() or getHour())
ZonedDateTime.now(ZoneOffset.UTC) - for UTC, it's the same as OffsetDateTime, but if you use a different timezone, it handles all timezone specific data, such as Daylight Saving Time changes.
To check if such object is in UTC, one way is to use the getZone() method:
ZonedDateTime z = ZonedDateTime.now(ZoneOffset.UTC);
System.out.println(z.getZone().equals(ZoneOffset.UTC)); // true
But if you use equivalents like ZoneId.of("UTC"), the equals method return false. So you could also check if z.getZone().getId() is equals to Z or UTC. With OffsetDateTime, it's similar:
OffsetDateTime odt = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(odt.getOffset().equals(ZoneOffset.UTC)); // true
With Instant you don't need to check, because it's always in UTC.
You can check all the available types in Oracle's date/time tutorial.
Both ZonedDateTime and OffsetDateTime can be converted to a LocalDateTime using the toLocalDateTime() method:
// dt will have the current date and time in UTC
LocalDateTime dt = ZonedDateTime.now(ZoneOffset.UTC).toLocalDateTime();
// or
LocalDateTime dt = OffsetDateTime.now(ZoneOffset.UTC).toLocalDateTime();
With this, the dt variable will have all the date and time fields (day/month/year, hour/minute/second/nanosecond) that corresponds to the current date/time in UTC. But it won't keep any timezone/offset information, so the LocalDateTime object itself can't know from which timezone those values came from.
I realize this question is a bit old, but I am learning Java and found myself trying to do something very similar. After some reading I found I could do what you are asking with this:
public static void main (String args[]) {
LocalDateTime now = LocalDateTime.now(Clock.systemDefaultZone()); // The clock argument is not really needed here.
String pattern = "dd MMM yyyy HH:mm:ss"; // Setup your format for output
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
System.out.println("Local time is: " + dtf.format(now));
LocalDateTime utc = LocalDateTime.now(Clock.systemUTC()); // Define alternate timezone
System.out.println("GMT/UTC is: "+dtf.format(utc));
}

Categories