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.
Related
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();
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
Given the following code
public static void main(String[] args) {
org.joda.time.format.DateTimeFormatter _timestampFomatNYCJoda = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd HHmmss.SSS").withZone(DateTimeZone.forID("America/New_York"));
DateTimeFormatter _timestampFomatNYC = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS").withZone(ZoneId.of("America/New_York"));
LocalDateTime localDateTime = LocalDateTime.now();
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime();
System.out.println("System Time " + new Date());
System.out.println("Java Version " + localDateTime.format(_timestampFomatNYC));
System.out.println("Joda Version " + _timestampFomatNYCJoda.print(jodaLocalDateTime.toDateTime(DateTimeZone.UTC)));
}
Why does the Java Version and Joda Version dont match ? I am running this on IST clock.
Below is the output
System Time Fri Mar 27 17:01:33 IST 2020
Java Version 20200327 170133.933
Joda Version 20200327 130133.938
I can reproduce your results. I can also explain them. Joda-Time and java.time have been designed to behave differently in this case. Let’s look at them in turn.
Joda-Time
In Joda-Time DateTimeFormatter.withZone() gives you a formatter with an override zone, that is, a zone that will always be used for formatting dates and times. In other words, any date and time will be converted to this zone for printing. The documentation says:
When printing, this zone will be used in preference to the zone from
the datetime that would otherwise be used.
When you do new org.joda.time.LocalDateTime(), you are getting a LocalDateTime representing the current date and time in your default time zone. The Local in some class names means without time zone or offset from UTC. I figure that you must have got a value equal to 2020-03-27T17:01:33.938.
Apparently what happens when you format a LocalDateTime with a formatter with an override zone, is that the formatter assumes that your LocalDateTime is in UTC (which yours isn’t) and converts it from there, in your case to America/New_York time zone. Since summer time (DST) is in effect in New York, the offset is -04:00, so 17:01 becomes 13:01.
This is the wrong result. When the time is 17:01 in your time zone, it is not 17:01 UTC, so the conversion is based on a false premise. It is also not 13:01 in New York, so the converted result is telling a lie.
java.time
With java.time setting an override zone on a formatter works similarly for formatting, but with a difference that matters here: the override zone is only used when printing a date-time object that identifies an instant (a point in time). From the docs:
When formatting, if the temporal object contains an instant, then it
will be converted to a zoned date-time using the override zone.
Whether the temporal is an instant is determined by querying the
INSTANT_SECONDS field. If the input has a chronology then it will be
retained unless overridden. If the input does not have a chronology,
such as Instant, then the ISO chronology will be used.
… In all other cases, the override zone is added to the temporal,
replacing any previous zone, but without changing the date/time.
Again LocalDateTime.now() gives you the current date and time of day (a few milliseconds earlier than the query through Joda-Time), 2020-03-27T17:01:33.933. Local still means without offset or time zone.
Because your LocalDateTIme hasn’t got offset or time zone, it cannot identify an unambigous point in time, an instant. Therefore when formatting it neither the date nor the time of day is changed. And since your format pattern contains no time zone or offset, none is printed. So you just get the date and time in your time zone (not in New York), 20200327 170133.933.
To get the date and time in New York time zone
DateTimeFormatter timestampFormat
= DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS");
ZonedDateTime timeInNy = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(timeInNy.format(timestampFormat));
When I ran this code just now, the output was:
20200327 122359.683
Documentation links
Joda-Time DateTimeFormatter.withZone()
java.time DateTimeFormatter.withZone()
This question already has answers here:
How can I get the current date and time in UTC or GMT in Java?
(33 answers)
Closed 4 years ago.
How can I get UTC value in Java of any given time and date with the respective time-zone?
Say for example my current time zone is Asia/Kolkata, now how can I get UTC value of say 1.00 am on 21/07/2018?
For getting currect time in UTC.
Instant.now() // Current time in UTC.
For getting current time in any desired TimeZone.
ZonedDateTime.now( ZoneId.systemDefault() ) // Current time in your ZoneId.
Kolkata Example :
ZoneId zoneKolkata = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zoneDTKolkata = instant.atZone( zoneKolkata ) ;
To adjust back to UTC, extract an Instant from the ZonedDateTime.
Instant instant = zoneDTKolkata.toInstant() ;
You can adjust from UTC to a time zone.
ZonedDateTime zoneDTKolkata = instant.atZone( zoneKolkata ) ;
Use the Java 8 time API instead of the older API (ie Date & SimpleDateFormat solution proposed by rajadilipkolli)
// System time (ie, your operating system time zone)
LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second);
// Time in Asia/Kolkata
ZonedDateTime kolkata = ldt.atZone(ZoneId.of("Asia/Kolkata"));
// Time in UTC
OffsetDateTime utc = ldt.atOffset(ZoneOffset.UTC);
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("h.mm a 'on' dd/MM/uuuu")
.toFormatter(Locale.ENGLISH);
ZoneId zone = ZoneId.of("Asia/Kolkata");
String localDateTimeString = "1.00 am on 21/07/2018";
Instant i = LocalDateTime.parse(localDateTimeString, formatter)
.atZone(zone)
.toInstant();
System.out.println("UTC value is: " + i);
This prints:
UTC value is: 2018-07-20T19:30:00Z
I wasn’t sure whether you needed to parse the exact string you gave, 1.00 am on 21/07/2018, into a date-time object, but in case I have shown how. The challenge is that am is in lowercase. In order to specify case insensitive parsing I needed to go through a DateTimeFormatterBuilder.
As you can see, the code converts to an Instant, which is the modern way to represent a point in time in Java. Instant.toString always prints the time in UTC. The Z at the end means UTC. If you want a date-time that is more explicitly in UTC you may use
OffsetDateTime odt = LocalDateTime.parse(localDateTimeString, formatter)
.atZone(zone)
.toInstant()
.atOffset(ZoneOffset.UTC);
System.out.println("UTC value is: " + odt);
The output is similar, only OffsetDateTime leaves out the seconds if they are 0 (zero):
UTC value is: 2018-07-20T19:30Z
Link: Oracle tutorial: Date Time explaining how to use java.time, the modern Java date and time API.
I have set my system data time to Asia/Kolkata and it is:
Date: 2015-06-12
Time: 12:07:43.548
Now i have written following program in Java 8
ZoneId paris = ZoneId.of("Europe/Paris");
LocalDateTime localtDateAndTime = LocalDateTime.now();
ZonedDateTime dateAndTimeInParis = ZonedDateTime.of(localtDateAndTime, paris );
System.out.println("Current date and time in a particular timezone : " + dateAndTimeInParis );
When i run the code it shows following time for Paris:
Current date and time in a particular timezone :
2015-06-12T12:07:43.548+02:00[Europe/Paris]
The above Europe/Paris is same as of Asia/Kolkata. Could anyone please explain where i am doing wrong?
Update: I prefer not using class from another Java package; as i have heard that this package java.time have enough functionalities to handle maximum date time jobs and i hope this one is included :)
A LocalDateTime is a date and time without a timezone. When you create a ZonedDateTime object out of it, you're explicitly attaching a timezone to the LocalDateTime.
It's not going to convert from your timezone to the Europe/Paris timezone; note that the LocalDateTime does not have a timezone at all; it doesn't know that you mean it to be Asia/Kolkata.
If you want to convert from Kolkata time to Paris time, start with a ZonedDateTime that uses the Asia/Kolkata timezone:
// Current time in Asia/Kolkata
ZonedDateTime kolkata = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
// Convert to the same time in Europe/Paris
ZonedDateTime paris = kolkata.withZoneSameInstant(ZoneId.of("Europe/Paris"));
(edit, thanks JBNizet): If you just want "now" in Europe/Paris time, you can do:
ZonedDateTime paris = ZonedDateTime.now(ZoneId.of("Europe/Paris"));