Converting Instant to LocalDate and back - java

I have a problem with converting dates between Instant and LocalDate and back. I need to change my date to monday in its current week (if it's wednesday then I'm changing to monday):
public static Instant getStartDateAsMonday(Instant startTime) {
LocalDate monday = startTime.atZone(ZoneId.systemDefault()).toLocalDate()
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
startTime = monday.atStartOfDay(ZoneId.systemDefault()).toInstant();
return startTime;
}
When I test it ...
#Test
public void testGetStartDateAsMonday() {
Instant instant = DateHelperService.getStartDateAsBeginningOnMonday(
Instant.parse("2020-05-27T00:00:00Z"));
assertThat(instant).isEqualTo("2020-05-25T00:00:00Z");
}
... the test doesn't pass and gives the output:
Expected : 2020-05-25T00:00:00Z
Actual : 2020-05-24T22:00:00Z
My system default timezone is GMT+2. The test passes when I do atStartOfDay(ZoneId.of("UTC")), but I don't understand why I can't use my system default in that conversion.

Firstly, calling ZoneId.systemDefault() twice is bad practice. The JVM’s current default time zone may have changed between calls. Any code in any thread of any app within that JVM can change the current default time zone with immediate effect.
So, capture the current default.
ZoneId z = ZoneId.systemDefault() ;
You said:
My system default timezone is GMT+2
GMT+2 represents an offset not a time zone. An offset is merely a number of hours-minutes-seconds, positive or negative. A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. A time zone has a name in the format of Continent/Region.
You said:
Expected : 2020-05-25T00:00:00Z
Actual : 2020-05-24T22:00:00Z
Your expectation is incorrect. If you have a moment representing the first moment of the day in a zone such as Europe/Brussels or Africa/Cairo for a time-of-day of 00:00, and you know that zone runs two hours ahead of UTC on that date, then intuitively you know UTC is two hours earlier. If the clock strikes midnight in Brussels or Cairo, you know the clock cannot yet be striking midnight in UTC, at the Greenwich Royal Observatory, or in Iceland. Midnight won’t strike there for two more hours. So the date is still “yesterday” in UTC. And two hours earlier than 00:00 is 22:00. So your actual result is correct.

Let's go through it method by method:
LocalDate monday = startTime // startTime is 2020-05-27 00:00:00 as an Instant.
// 2020-05-27 02:00:00, but with GMT+2 information as it is a ZonedDateTime now.
.atZone(ZoneId.systemDefault())
// (1) 2020-05-27 02:00:00 without any zone information.
// (2) 2020-05-27 00:00:00 because hour is dropped as it's a LocalDate here.
.toLocalDate()
// Changed to 2020-05-25 00:00:00, which is what you want.
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
startTime = monday // monday is 2020-05-25 00:00:00 as LocalDate.
// You say that this is at GMT+2 and you want the start of day.
// You get a ZonedDateTime 2020-05-25 00:00:00 with information about GMT+2.
.atStartOfDay(ZoneId.systemDefault())
// Instant has no zone offset, so this is what the ZonedDateTime needs to consider.
// 2020-05-25 00:00:00 becomes 2020-05-24 22:00:00 as the +2 offset is subtracted.
.toInstant();
When you are using ZoneId.of("UTC") no problems will happen as the offset of ZoneId.of("UTC") is zero (daylight saving stuff ignored for simplicity). A datetime minus zero hours is the same datetime.

Related

Alfresco : Format date to current time zone

Let's say I have a Java "Date" object. I would like to convert/format it to the current time zone of my Spring Boot/Alfresco service application.
How can I achieve this ? I know Alfresco does it to display the date in formularies.
For example, I have a date property set to 3 pm (UTC), the system will display "17:00:00" (5pm, french time).
I would like to get the same result but with Java.
Any idea ?
Thank you!
If you could use java.time (Java 8+ needed), you could get the default zone id of a system with
ZoneId sysDefault = ZoneId.systemDefault();
You can get the current time with the speaking method now(), in this case use a ZonedDateTime.now() for the time in the system default zone or apply a zone to now(ZoneId) if you want the current time in a specific zone.
Having a zone and/or a ZonedDateTime, you can safely switch between zones, the conversion will be handled by the class(es). You can use DateTimeFormatters in order to alter display styles, like 24h or 12h format, double-digit units or single-digit ones and so on.
Here's a simple example for switching a datetime from UTC to Paris zone. The zones in this example are fixed, but you can use the system default zone id of your server instead of the Paris zone.
public static void main(String[] args) {
// define the zones needed for conversion, one for UTC
ZoneId utc = ZoneId.of("UTC");
// and a second one for Paris
ZoneId paris = ZoneId.of("Europe/Paris");
// create date and time of day in utc
ZonedDateTime threePmUtc = ZonedDateTime.of(2022, 8, 5, 15, 0, 0, 0, utc);
// display its time of day once
System.out.println(threePmUtc.toLocalTime()
.format(DateTimeFormatter.ISO_LOCAL_TIME));
// then convert to Paris time
ZonedDateTime fivePmParis = threePmUtc.withZoneSameInstant(paris);
// display its time of day
System.out.println(fivePmParis.toLocalTime()
.format(DateTimeFormatter.ISO_LOCAL_TIME));
/*
* if you want more control over the output,
* e.g. don't print seconds if they are zero or switch to 12h format,
* then build a formatter with your desired format
*/
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("h.mm a", Locale.ENGLISH);
// and print the formatted value
System.out.println(fivePmParis.toLocalTime()
.format(dtf));
}
Output:
15:00:00
17:00:00
5.00 PM

LocalDate and LocalDateTime in a server which runs in EST vs UTC

I am trying to understand LocalDate and LocalDateTime. Since they do not carry zone info, how does it work for now() on two different time zone.
Example:
Server 1(EST time zone):
LocalDateTime.now() -> 2020-04-06T23:00:00.040
LocalDate.now(). -> 2020-04-06
Server 2(UTC time zone):
LocalDateTime.now() -> What would be the value?
LocalDate.now(). -> What would be the value? (Note in EST, time it executed was 11 PM)
Also,
If I convert below date string to LocalDateTime and then toLocalDate, what would be the outcome?
2020-04-06T23:00:00.000
Offsets vary over time
On April 6, 2020, most time zones on the east coast of North America such as America/Montreal and America/New_York use an offset of four hours behind UTC.
So, in those zones 11 PM on 2020-04-06 is simultaneously 3 AM on the 7th in UTC. Add four hours to 2020-04-06T23:00 to get 2020-04-07T03:00. Adding four hours brings us from the wall-clock time used in America/Port-au-Prince and America/Nassau to UTC.
The offset used by the time zones mentioned above are four hours behind UTC only half the year. The politicians in those locations have decided to observe Daylight Saving Time (DST) for about half the year. So half the year they jump their clocks ahead an hour for an offset-from-UTC of -04:00, and later in the year they fall back an hour for an offset-from-UTC of -05:00. For example, earlier in the year 11 PM in America/New_York would be 04:00 in UTC rather than the 03:00 time seen in April.
Standard time, in January, is five hours behind UTC. So, 11 PM plus five hours is 4 AM next day.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ZonedDateTime.of( 2020 , 1 , 6 , 23 , 0 , 0 , 0 , zNewYork ) ;
Instant instant = zdt.toInstant() ; // 2020-01-07T04:00Z
Daylight Saving Time, in April, is four hours behind UTC. So, 11 PM plus four hours is 3 AM next day.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ZonedDateTime.of( 2020 , 4 , 6 , 23 , 0 , 0 , 0 , zNewYork ) ;
Instant instant = zdt.toInstant() ; // 2020-04-07T03:00Z
By the way, DST is only one of many reasons politicians have for changing the offset used by the time zone(s) of their jurisdiction. Politicians around the world have shown a penchant for changing their offsets surprisingly often. Your programming should always expect a time zone to change its offset. Just because your particular zone of concern does not currently observe DST does not mean the offset will never change.
As for parsing 2020-04-06T23:00:00.000 as a LocalDateTime, you will get 2020-04-06T23:00:00.000. Ignoring time zones is the entire point of LocalDateTime. So no adjustments are made.
You may be thinking of LocalDateTime as representing a particular locality. But, no, it represents any locality or all localities. But never any one particular locality. For a particular locality, use ZonedDateTime.
Pseudo time zones
Another point: EST is not a time zone. Such 2-4 letter letter codes are not real time zones, are not standardized, and are not even unique. For example, by EST, did you mean Australian Eastern Standard Time or North American Eastern Standard Time? Is CST Central Standard Time or China Standard Time? Avoid these pseudo-zones in your date-time-handling.
And you happened to use the wrong pseudo-code. On April 6 2020, most of those east coast time zones are observing Daylight Saving Time (DST). So they would be considered to be in “EDT” rather than “EST”. Avoid the problem by specifying the real time zone name.
Real time zones are named using Continent/Region format. See Wikipedia for a list of real time zones.
Never call LocalDateTime.now
I cannot imagine a case where calling LocalDateTime.now would be the right thing to do. Determining the current moment requires a time zone (or offset). And LocalDateTime by definition has no time zone or offset. When a programmer writes LocalDateTime.now, you can bet they do not fully understand the necessary concepts.
When you call LocalDateTime.now, the JVM’s current default time zone is implicitly used to capture the current time as seen by the people in the region of that zone. And then the fact of that time zone is deleted.
This:
LocalDateTime.now()
…is the same as this:
LocalDateDate.now( ZoneId.systemDefault() )
…which is the same as getting the current moment as seen in a particular time zone, followed by removing the time zone information:
ZonedDateTime.now( ZoneId.systemDefault() ).toLocalDateTime()
For more code examples demonstrating LocalDateTime.now, see the correct Answer by Ole V.V.
Where to use LocalDateTime
If LocalDateTime is not appropriate for getting the current moment, and is not appropriate for tracking any moment, what is the appropriate use for this class? Three things: representing any locality, representing all localities, and booking future appointments.
Any locality would be something like stating when Christmas starts. This year Christmas starts at 2020-12-25T00:00 wherever you are in the world. Of course this means Christmas starts first in Kiribati after midnight, later in Japan after midnight, even later in Tunisia after midnight, and still later in Chicago after midnight.
All localities would be something like stating our company policy that lunch breaks at all our factories in Delhi, Düsseldorf, and Detroit are scheduled for 12:30. So on April 6 2020, the break will be at 2020-04-06T12:30:00. This break will occur first in Delhi, several hours later in Düsseldorf, and even more hours later in Detroit.
Booking future appointments where you intend to keep the same time-of-day regardless of changes to the time zone’s offset must recorded without the offset. If your next dental appointments is in six months at 3 PM, we want to record the 3 PM without regard for the offset. If the offset were to be changed by politicians, we still want the appointment to start when the clock strikes three on that date in that zone.
Set the appointment.
LocalDateTime appointment = LocalDateTime.of( 2021 , 1 , 23 , 15 , 0 , 0 , 0 ) ;
Determine a moment for producing a calendar. Every time you do this, you may get a different result if the politicians have changed the rules of this time zone.
ZoneId z = ZoneId.of ( "America/Panama" ) ;
ZonedDateTime dueToArrive = appointment.atZone( z ) ;
LocalDate
As for LocalDate, we saw in the examples above that the date depends on time zone. For any given moment, the date varies around the globe by time zone. It may be “tomorrow” in Tokyo Japan while still “yesterday” in Montréal Québec Canada.
So, you must specify a time zone when asking for the current date.
LocalDate ld = LocalDate.now( ZoneId.of( "Asia/Kolkata" ) ) ;
If you omit the time zone, the JVM’s current default time zone is implicitly applied. So LocalDate.now() becomes LocalDate.now( ZoneId.systemDefault() ). I recommend specifying your desired/expected zone explicitly, rather than rely on implicit default.
Server time zone
You said:
server which runs in EST vs UTC
FYI, servers should generally be set to UTC. Most of your thinking, programming, logging, and so on should be done in UTC. Learn to think of UTC as The One True Time, with all other zones but mere variations.
As a programmer, you should never rely on the default time zone. That is far outside your control. That default can be changed so easily by the user or sysadmin. Furthermore, any code in any thread of any app within the JVM can instantly change the JVM’s current default time with a call to TimeZone.setDefault. So even during execution of your app, the default can be changed at any moment.
To prevent confusion pass explicit time zone to now() and see for yourself:
ZoneId easternTime = ZoneId.of("America/Montreal");
System.out.println(LocalDateTime.now(easternTime));
System.out.println(LocalDateTime.now(ZoneOffset.UTC));
System.out.println(LocalDate.now(easternTime));
System.out.println(LocalDate.now(ZoneOffset.UTC));
Output when I ran just now:
2020-04-06T09:56:17.381558
2020-04-06T13:56:17.385215
2020-04-06
2020-04-06
While you are correct that LocalDateTime and LocalDate don’t contain any time zone information, their now methods do use time zones. Either the one passed to them, or if you use the no-arg variant, the default time zone of the JVM.
You also asked:
Also, If I convert below date string to LocalDateTime and then
toLocalDate, what would be the outcome?
2020-04-06T23:00:00.000
Why not try out that too?
LocalDateTime ldt = LocalDateTime.parse("2020-04-06T23:00:00.000");
System.out.println(ldt);
LocalDate ld = ldt.toLocalDate();
System.out.println(ld);
2020-04-06T23:00
2020-04-06
Converting from LocalDateTime to LocalDate involves no time zone (or UTC offset) whatsoever. The time part is simply discarded and the date part kept unchanged.

How to convert UTC Time to LocalDateTime by using ZonedDateTime

I have found many way to convert localDateTime to LocalDateTime in UTC.
But I could not find any way to convert UTC time at localDateTime by using ZonedDateTime. Do you know a way to convert it ?
This is what I used to convert it to UTC. I need a vice versa method .
ZonedDateTime zonedDate = ZonedDateTime.of(localDateTime,
ZoneId.systemDefault());
localDateTime.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("UTC)
Don’t use LocalDateTime for a date and time for which you know the UTC offset or time zone. For a date and time in your time zone or another known time zone, use ZonedDateTime. For a date and time for which you know an offset (and here UTC counts as an offset) use OFfsetDateTime.
Why? LocalDateTime (confusing class name) is a date and time without time zone or offset. Not storing the known offset or time zone is throwing away vital data and is an error waiting to happen.
One exception: For a date and time in a known time zone in a further future, do store a LocalDateTime and make sure to store the time zone as a separate ZoneId object. This will allow the offset and/or summer time rules (DST rules) for the time zone to be changed between now and that time (which happens more often than we like to think). Only when time draws near and our Java installation may have been updated with the latest zone rules, can we correctly combine the date-time and the zone and obtain the correct moment in time.
Convert UTC date and time to your time zone
OffsetDateTime utcDateTime = OffsetDateTime.of(2019, 9, 10, 12, 0, 0, 0, ZoneOffset.UTC);
System.out.println("UTC date and time: " + utcDateTime);
ZonedDateTime myDateTime = utcDateTime.atZoneSameInstant(ZoneId.systemDefault());
System.out.println("Date and time in the default time zone: " + myDateTime);
After I set my time zone to Asia/Istanbul, this snippet output:
UTC date and time: 2019-09-10T12:00Z
Date and time in the default time zone: 2019-09-10T15:00+03:00[Asia/Istanbul]
Convert from your time zone to UTC
For the opposite conversion I prefer:
OffsetDateTime convertedBackToUtc = myDateTime.toOffsetDateTime()
.withOffsetSameInstant(ZoneOffset.UTC);
System.out.println("UTC date and time again: " + convertedBackToUtc);
UTC date and time again: 2019-09-10T12:00Z
Still not using any LocalDateTime.

how to get and format date with timezone

I'am creating a date and store it into database, I want to get current time which is timezone = "Asia/Istanbul" not my local time.
I am creating the date in my local computer, my local timezone is also "Asia/Istanbul".
when i deploy it into my server, server timezone is utc, it is turning to utc everytime.
I have different 2 machine, 2 machines have different timezone so I need to set my data dates with the timezone.
here is what i have done. it is ok in my local computer, but fails on server which is UTC
LocalDateTime localDateTime = LocalDateTime.now();
// it gives my local date time, 2019-07-09T10:30:03.171
// local date is now 1:30 pm, UTC is 3 hours after, it looks ok.
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("Asia/Istanbul"));
//2019-07-09T10:30:03.171+03:00[Asia/Istanbul]
// it looks +3. I dont want to see +3, I want the date like 01:30 which is shiefted
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
zonedDateTime.format(formatter);
//2019-07-09T07:30:03.171Z
// zone is disappeared, this is 3 hours before UTC
I expect the date like Asia/Istanbul when i created it.
I wouldn’t use LocalDateTime at all. Use ZonedDateTime throughout to eliminate any and all doubt about the time. Also always pass a ZoneId (if not a Clock) to the now method. This makes your code independent of the time zone settings of the computer and JVM.
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Istanbul"));
System.out.println(zonedDateTime);
2019-07-09T14:14:17.280852+03:00[Asia/Istanbul]
You might have misunderstood the +03:00 part, people sometimes do. It means that the time shown is already 3 hours ahead of UTC. So the point in time shown is equal to 11:14:17 UTC.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(zonedDateTime.format(formatter));
2019-07-09 14:14:17
Your formatter does not include time zone, therefore it’s not shown. But it’s the time in İstanbul.
What went wrong in your code?
I am assuming that the comments in your code are from running on your server in UTC (it’s not perfectly clear) and that you ran the code around 10:30 UTC, the same as 13:30 in İstanbul.
A LocalDateTime is a date and time without time zone and without UTC offset. Its no-arg now method uses the JVM’s time zone setting, in this case UTC, so gives you 10:30 on the day in question. I think that ZonedDateTime.of is wrong here: it takes the 10:30 from the LocalDateTime and İstanbul time zone from the ZoneId object and gives you 10:30 in İstanbul, which is not what you wanted. You had wanted 13:30, AKA 1:30 PM.

JSR 310 :: System.currentTimeMillis() vs Instant.toEpochMilli() :: TimeZone

Could you please shed some light on how to obtain correct epoch time in milliseconds for a default system timezone and given timezone.
Given
1. TimeZone: GMT+3
2. The following code snippet:
import java.time.*;
public class Main {
public static void main(String[] args) {
System.out.println(LocalDateTime
.now()
.atZone(ZoneOffset.UTC)
.toInstant()
.toEpochMilli()
);
System.out.println(LocalDateTime
.now()
.atZone(ZoneOffset.of("+3"))
.toInstant()
.toEpochMilli()
);
System.out.println(System.currentTimeMillis());
}
}
3. Output:
1444158955508
1444148155508
1444148155508
4. JavaDoc for System.currentTimeMillis() that tells that returned value will be the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
So, why
the output of the LocalDateTime at GMT+3 is the same as of System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?
the output of the LocalDateTime at UTC differs from System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?
Both System.currentTimeMillis() and Instant.toEpochMilli() return the number of milliseconds since the Unix epoch. That isn't "in" any particular time zone, although the Unix epoch is normally expressed as "midnight on January 1st 1970, UTC". But an instant is just an instant in time, and is the same whichever time zone you're in - but it will reflect a different local time.
The output of LocalDateTime.atZone(UTC) differs because you're saying "Take the local date and time, and convert it to an instant as if it were in the UTC time zone" - even though when you created that LocalDateTime you did so implicitly in the UTC+3 time zone... that's why it's "wrong".
LocalDateTime.now() takes the local date and time in the system default time zone. So if your time zone is UTC+3, the current instant in time is 2015-10-06T16:57:00Z, then LocalDateTime.now() will return .2015-10-06T19:57:00. Let's call that localNow...
So localNow.atZone(ZoneOffset.of("+3")) will return a ZonedDateTime representing 2015-10-06T19:57:00+03 - in other words, the same local date/time, but "knowing" that it's 3 hours ahead of UTC... so toInstant() will return an Instant representing 2015-10-06T16:57:00Z. Great - we still have the current date/time.
But localNow.atZone(ZoneOffset.UTC) will return a ZonedDateTime representing 2015-10-06T19:57:00Z - in other words, the same local date/time, but "thinking" that it's already in UTC... so toInstant() will return an Instant representing 2015-10-06T19:57:00Z.. which isn't the current time at all (it's in three hours).
Short version:
There is no way to compute LocalDateTime -> Instant, you need to specify a timezone.
With a timezone you get a ZonedDateTime and can compute ZonedDateTime -> Instant
Instant == System.currentTimeMillis() if the timezone of the ZonedDateTime equals the system default time zone.
Long version:
LocalDateTime is the time on your clock(plus date information). Which is not enough if you don't tell us which timezone your are in. 13:00 o'clock in Tokyo is not the same Instant as 13:00 o'clock in Paris.
Once you add a timezone to your LocalDateTime you get a ZonedDateTime and we can know in which Instant of time you actually are. E.g. are you 13:00 o'clock in Tokyo or in Paris?
To get the correct Instant the timezone of the ZonedDateTime needs to be correct. If it is 13:00 o'clock in Tokyo but you claim that you are 13:00 o'clock in Paris you will get a wrong Instant.
LocalDateTime:
It cannot represent an instant on the time-line without additional information such as an offset or time-zone.
ZonedDateTime:
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.
To get an Instant you need to convert LocalDateTime to ZonedDateTime first. If you did this correctly(by stating the correct timezone) your Instant will agree with System.currentTimeMillis().
System.currentTimeMillis():
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
the output of the LocalDateTime at GMT+3 is the same as of System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?
If your timezone is GMT+3 then ZonedDateTime.toInstant() will give you the correct Instant and therefore agree with System.currentTimeMillis()
the output of the LocalDateTime at UTC differs from System.currentTimeMillis(), although the docs for the System.currentTimeMillis() mention UTC?
If your timezone is not UTC then ZonedDateTime.toInstant() will give you an incorrect Instant.
System.out.println("Output 1 : " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("Output 2 : " + System.currentTimeMillis());
Output 1 : 1576047664910
Output 2 : 1576047664911

Categories

Resources