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
Related
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.
for example, now it is 2020-03-16 11:23:23.121 in Vietnam, but my program is running in the USA, how to get a Date instance which is 2020-03-16 12:00:00.000 in Vietnam, which mean, I keep the year, month, day as the same, but set hour as 12, minute, second and nanosecond as 0, can LocalDateTime play a role?
ZonedDateTime zdt = ZonedDateTime.of(2020, 3, 16, 12, 0, 0, 0, ZoneId.of("Asia/Ho_Chi_Minh"));
ZonedDateTime
From java-8 you can use ZonedDateTime to get the date time from any zone
ZonedDateTime dateTime = ZonedDateTime.now(ZoneId.of("Asia/Ho_Chi_Minh"))
And the you can modify the time to 12:00:00 using with method. Pass the time of day as a LocalTime object obtained by calling LocalTime.of. In the new LocalTime object, the second and the nanosecond default to zero, so no need to pass those arguments to the factory method.
dateTime.with( LocalTime.of( 12 , 0 ) ) //2020-03-16T12:00+07:00[Asia/Ho_Chi_Minh]
Java util Date will not store any time zone information and it just represents a specific instant in time (which is only UTC), with millisecond precision. I will suggest to avoid using legacy util.Date
No, do not use LocalDateTime here
can LocalDateTime play a role?
LocalDateTime cannot represent a moment, as it lacks the context of a time zone or offset-from-UTC. So that is exactly the wrong classs to use here on your Question.
To represent a moment, a specific point on the timeline, use:
Instant (always in UTC)
OffsetDateTime (carries an offset-from-UTC, a number of hours-minutes-seconds)
ZonedDateTime (carries an assigned time zone, named in Continent/Region)
See the correct Answer by Deadpool showing the proper use of ZonedDateTime to solve your problem.
For more info, see What's the difference between Instant and LocalDateTime?
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.
I'm using https://github.com/JakeWharton/ThreeTenABP in my project.
I have org.threeten.bp
ZonedDateTime: 2019-07-25T14:30:57+05:30[Asia/Calcutta]
How can I get this printed with addition of the timezone hours? ie the result should have 2019-07-25T20:00:57
Get the offset in the form of seconds from ZonedDateTime
ZonedDateTime time = ZonedDateTime.parse("2019-07-25T14:30:57+05:30");
long seconds = time.getOffset().getTotalSeconds();
Now get the LocalDateTime part from ZonedDateTime
LocalDateTime local = time.toLocalDateTime().plusSeconds(seconds); //2019-07-25T20:00:57
toLocalDateTime
Gets the LocalDateTime part of this date-time.
If you want to get the local date time in UTC use toInstant()
This returns an Instant representing the same point on the time-line as this date-time. The calculation combines the local date-time and offset.
Instant i = time.toInstant(); //2019-07-25T09:00:57Z
You misunderstood. The offset of +05:30 in your string means that 5 hours 30 minutes have already been added to the time compared to UTC. So adding them once more will not make any sense.
If you want to compensate for the offset, simply convert your date-time to UTC. For example:
ZonedDateTime zdt = ZonedDateTime.parse("2019-07-25T14:30:57+05:30[Asia/Calcutta]");
OffsetDateTime utcDateTime = zdt.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
System.out.println(utcDateTime);
Output:
2019-07-25T09:00:57Z
greetings all
i am using the following method to get the current time in GMT timezone
public static Timestamp getCurrentTimeGMT() {
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
long time = c.getTimeInMillis();
long offset = TimeZone.getDefault().getOffset(time);
return new Timestamp(time - offset);
}
but when i try to use the same method with minor changes to get the current time in GMT+3
it gives me the same result of GMT ? i don't know why:
public static Timestamp getCurrentTimeGMT3() {
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT+3"));
long time = c.getTimeInMillis();
long offset = TimeZone.getDefault().getOffset(time);
return new Timestamp(time - offset);
}
any ideas why the above code doesn't work properly, and how to do such a method ?
Timestamp extends Date - it doesn't have a time zone, conceptually. It just represents an instant in time.
If you want to display it in a particular calendar with a particular time zone, that's a formatting issue. Create the appropriate calendar with the relevant time zone, and set the timestamp within it accordingly.
(As per normal, I'd like to recommend that you use Joda Time instead of the built-in API where possible. It's much cleaner.)
Why do you subtract the offset from the time in the last line? That is basically resetting your time back to GMT. You retrieve GMT+3 then you subtract 3 hours from that.
What Jon said. A Timestamp does not have a time zone, it's always UTC, and really shouldn't be abused for representing local time. If you really need objects to represent local time, Joda Time has a class for that.
And you should be aware that "GMT+3" is not a real valid time zone. A time zone has not just a base offset, but also a daylight savings time offset, which can be different for time zones with the same base offset, and can even change for the same time zone due to legislation. A real time zone ID is "Europe/Berlin" or "Australia/Darwin".
java.time
In March 2014, the modern Date-Time API supplanted the legacy date-time API and since then it is strongly recommended to switch to java.time, the modern date-time API.
Solution using java.time, the modern Date-Time API: The first important thing to understand is the difference between a timezone and a timezone offset.
A timezone is identified by an ID, typically in the form of a Region/City e.g. Europe/London. Here you can check a list of tz database time zones. The java.time API provides with ZonedDateTime to represent a date-time with timezone.
A timezone offset tells us the amount of time by which the time of a place is offset from the UTC e.g. 2011-12-03T10:15:30+01:00 which tells us that this date-time is offset by 01:00 hours from UTC i.e. we need to subtract 01:00 hours to get the equivalent date-time at UTC. The java.time API provides with OffsetDateTime to represent a date-time with time zone offset.
Some time zones observe DST i.e. America/New_York observes an offset of -05:00 hours in the winter while -04:00 hours in the summer.Here you can check an illustration.
A ZonedDateTime automatically reflects the applicable offset while we create an OffsetDateTime with a fixed offset value. Thus, GMT and GMT+3 mentioned in your question are not time zones, they represent time zone offset. A better and more modern way to write it is Z and +03:00 respectively.
Demo:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
class Main {
public static void main(String[] args) {
ZoneId zone = ZoneId.of("America/New_York");
// Current date-time in America/New_York
System.out.println(ZonedDateTime.now(zone));
// A sample winter date-time in America/New_York
System.out.println(ZonedDateTime.of(LocalDate.of(2023, 01, 14), LocalTime.of(10, 20, 30), zone));
// A sample summer date-time in America/New_York
System.out.println(ZonedDateTime.of(LocalDate.of(2023, 06, 14), LocalTime.of(10, 20, 30), zone));
// Current OffsetDateTime with an offset of 00:00 hours (UTC time)
System.out.println(OffsetDateTime.now(ZoneOffset.UTC));
// Current OffsetDateTime with an offset of 05:30 hours
OffsetDateTime odt = OffsetDateTime.now(ZoneOffset.of("+05:30"));
System.out.println(odt);
// The same OffsetDateTime converted to UTC time (will be 05:30 less)
System.out.println(odt.withOffsetSameInstant(ZoneOffset.UTC));
}
}
Output from a sample run:
2023-01-14T12:40:04.348474-05:00[America/New_York]
2023-01-14T10:20:30-05:00[America/New_York]
2023-06-14T10:20:30-04:00[America/New_York]
2023-01-14T17:40:04.352982Z
2023-01-14T23:10:04.353969+05:30
2023-01-14T17:40:04.353969Z
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Sample Code to get Time in Different Time Zone
public class GetZoneDateTime {
public static void main(String[] args) throws Exception {
String reqTzId = "Asia/Singapore";
LocalDateTime now = LocalDateTime.now();
System.out.println("Local Now: " + now + " TimeZone: " + TimeZone.getDefault());
ZoneId zoneId = ZoneId.of(reqTzId);
System.out.println("Req TimeZone : " + zoneId);
LocalDateTime localNow = LocalDateTime.now(zoneId);
System.out.println("tzLocalDateTime: " + localNow);
ZonedDateTime zoneNow = ZonedDateTime.now(zoneId);
System.out.println("tzZonedDateTime: " + zoneNow);
}
}
c.getTimeInMillis() returns the number of milliseconds since January 1st, 1970 0:00 UTC and will return the same value, no matter which time zone is used in the Calendar instance.
If you create a Calendar with a time zone and need access to the time fields, you should access these directly:
int h = c.get(Calendar.HOUR_OF_DAY);
int m = c.get(Calendar.MINUTE);
int s = c.get(Calendar.SECOND);
Use the Timezone static method.
TimeZone default = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("GMT+3"));
Calendar cal = Calendar.getInstance();
cal.setTime(new Date(System.currentTimeMillis()));
Date date = cal.getTime(); // converted date time
System.out.println(date.toString());
// Set Back to System Default
TimeZone.setDefault(default);