Why does LocalDateTime.ofInstant() requires ZoneId - java

In Java,
If Instant represents a point in time, encoded as date and time in
UTC.
And LocalDateTime represents a point in time, encoded as date and time in the JVM local timezone.
Why then does LocalDateTime.ofInstant() require a ZoneId as a second argument ?
This makes LocalDateTime not only confusing and potentially incorrect, but also makes it identical to ZonedDateTime; because the timezone of LocalDateTime can be any timezone just like ZonedDateTime.

And LocalDateTime represents a point in time, encoded as date and time in the JVM local timezone.
No, that's not true. Regardless of the "encoded as" part (which I highly doubt, but which I don't have significant knowledge of to disprove), a LocalDateTime does not represent a point in time. It represents a local date/time, without reference to a specific time zone. Any given LocalDateTime occurs at different points in time in different time zones.
Currently the local date and time in the Europe/London time zone is 2023-01-26T08:50. The same point in time in (say) America/New_York would result in a different LocalDateTime. Whereas in America/New_York, the LocalDateTime of 2023-01-26T08:50 occurs as a different point in time.
For some LocalDateTime / time zone combinations, there may be zero or two corresponding points in time - for example, the LocalDateTime 2022-11-06T01:30 will occur in America/New_York at both 2022-11-06 05:30:00Z and 2022-11-06 06:30:00Z.
Hopefully this is sufficient evidence that a LocalDateTime really isn't a point in time...

A LocalDateTime represents a date and time without time zone information. It is used to represent "local time" (as an example for me, right now the local time is 2023-01-26 09:50). An Instant is always at UTC (example, the time right now is 2023-01-26 08:50 UTC). To transform between an Instant and what the observer considers to be local time, you need to know the ZoneId of the location of the observer (e.g. for me Europe/Amsterdam), otherwise you cannot derive a local time.
And to be clear, a LocalDateTime does not represent a point in time (that is what Instant is for). To expand my example, for me the local time 2023-01-26 09:50 is now past, while for Jon Skeet (in Europe/London), that local time will happen in slightly less than an hour).

.ofInstant() is there to specify the instant and timezone.
If you don't want to specify the time zone and instant you can use .now() wich will use the local date and time.
If you want to specify the instant but not the ZoneId you can use ZomeId.systemDefault().

An Instant is independent from time zones or offsets, but a LocalDateTime is not. Although it does not have a ZoneId or ZoneOffset, it's values are partially based on the offset the specific zone currently has.
Here's a code example with different zones that lead to different values of the very same Instant:
public static void main(String[] args) throws DateTimeParseException {
// example instant (see output)
var instant = Instant.now();
// get the instant with different time zones
var ldtBerlin = LocalDateTime.ofInstant(instant, ZoneId.of("Europe/Berlin"));
var ldtCanberra = LocalDateTime.ofInstant(instant, ZoneId.of("Australia/Canberra"));
var ldtKolkata = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Kolkata"));
var ldtLA = LocalDateTime.ofInstant(instant, ZoneId.of("America/Los_Angeles"));
// print all the results
System.out.println("Instant " + instant.toEpochMilli() + " in different zones:");
System.out.println(ldtBerlin + " (Berlin)");
System.out.println(ldtCanberra + " (Canberra)");
System.out.println(ldtKolkata + " (Kolkata)");
System.out.println(ldtLA + " (Los Angeles)");
}
Output:
Instant 1674724841016 in different zones:
2023-01-26T10:20:41.016228 (Berlin)
2023-01-26T20:20:41.016228 (Canberra)
2023-01-26T14:50:41.016228 (Kolkata)
2023-01-26T01:20:41.016228 (Los Angeles)
View the different values, those are the local dates and times of the zones, but a LocalDateTime does not have any! You can use other objects, like ZonedDateTime (has zone and offset) and OffsetDateTime (has offset). Both of them have a method toLocalDateTime(), in case you need a LocalDateTime later on…

Related

Convert elapsed time between epoch time and current moment to ISO 8601 duration with Java

I have milliseconds since 1970 january 1 UTC (Epoch time).
1512431637067
I need to convert this to something like (ISO-8601 duration). This will be based off current today's date.
P5M4D
Any idea how to do this in a simple way using java code?
ZoneId zone = ZoneId.of("Europe/Guernsey"); // Specify a time zone by proper name `Contintent/Region`, never by 3-4 letter codes such as `PST`, `CST`, or `IST`.
LocalDate then = // Represent a date-only value, without time zone and without time-of-day.
Instant.ofEpochMilli(1_512_431_637_067L) // Parse your number of milliseconds since 1970-01-01T00:00Z as a value in UTC.
.atZone(zone) // Adjust from UTC to some other zone. Same moment, different wall-clock time. Returns a `ZonedDateTime`.
.toLocalDate(); // Extract a date-only value.
LocalDate today = LocalDate.now(zone); // Get the current date as seen in the wall-clock time in use by the people of a particular region.
Period diff = Period.between(then, today); // Determine the number of years-months-days elapsed.
System.out.println(diff); // Generate a String is standard ISO 8601 format: `PnYnMnDTnHnMnS`.
Output when running just now is exactly what you asked for:
P5M4D
The result is time zone dependent. For any given moment, the date varies around the globe by zone.
Therefore please substitute your desired time zone if it didn’t happen to be Europe/Guernsey. Use ZoneOffset.UTC and OffsetDateTime class if you want the calculation to happen in UTC.
For example running that code above for Europe/Guernsey results in P5M4D, while switching to Europe/Moscow results in P5M3D, for a difference of one day depending on which zone you specify.
Period.between(then, LocalDate.now(ZoneId.of("Europe/Moscow")))
Output on the day the question was asked would have been:
P5M3D
For a duration that includes units larger than a day you need to use the Period class of java.time The Duration class is for smaller units, days-hours-minutes-seconds-nanoseconds.
Strictly spoken, you cannot because a so called "epoch time" is really an instant and NOT a duration. But you might want to model the elapsed time since that epoch (Unix epoch) as duration. So here you go:
System.out.println(Duration.of(1512431637067L, ChronoUnit.MILLIS));
// output: PT420119H53M57.067S
The method java.time.Duration.toString() automatically normalizes the seconds and nanoseconds to HMS-format (otherwise we have to state that the printing capabilities of the new duration class are limited). If you wish more control over ISO-format then either consider your own workaround using methods like toHours() etc. or use a 3rd-party-library for duration printing.
Another thing: 1512431637067 seems to be in milliseconds not in seconds as you stated otherwise you would get an instant in far future:
System.out.println(Instant.ofEpochMilli(1512431637067L));
// output: 2017-12-04T23:53:57.067Z
System.out.println(Instant.ofEpochSecond(1512431637067L));
// far future: +49897-01-18T19:11:07Z
try this line
import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.Locale;
public class HelloWorld
{
public static void main(String[] args)
{
Date date=new Date (1512431637067L);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
System.out.print(df.format(date));
}
}
output date on iso 8601:
2017-12-04T23:53:57.067Z

Convert datetime string in one timezone to another using offset

I have a datetime string "2018-01-15 01:16:00" which is in EST timezone. I want to convert this into another timezone dynamically using the UTC offset. My javascript code passes this UTC offset as a parameter and the servlet has to convert/format this datetime string to the timezone identified by the provided offset.
I have tried many approaches including the one documented in the oracle tutorials but unable to arrive at a solution.
Below is my code that I am trying, any help is greatly appreciated.
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_TIME_ZONE = ZoneId.SHORT_IDS.get("EST");
public static void main(String[] args) throws Exception {
String dateTime = "2018-01-15 02:35:00";
//parse the datetime using LocalDateTime
LocalDateTime defaultDateTime = LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(DATE_FORMAT));
//get the datetime in default timezone
ZoneId defaultZoneId = ZoneId.of(DEFAULT_TIME_ZONE);
ZonedDateTime defaultZoneDateTime = defaultDateTime.atZone(defaultZoneId);
System.out.println("EST time: "+defaultZoneDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)));
ZonedDateTime utcZonedDateTime = defaultZoneDateTime.withZoneSameInstant(ZoneId.of("UTC"));
String utcTime = defaultZoneDateTime.withZoneSameInstant(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(DATE_FORMAT));
System.out.println("UTC : "+utcTime);
//IST timezone
ZoneOffset offset = ZoneOffset.of("+05:30");
OffsetDateTime offsetDate = OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset);
String targetTimeZone = offsetDate.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
System.out.printf("target time : "+targetTimeZone);
}
OUTPUT
EST time: 2018-01-15 02:35:00
UTC : 2018-01-15 07:37:00
target time : 2018-01-15 07:37:00
Expected target time : 2018-01-15 13:05:00
The immediate problem is this line:
OffsetDateTime offsetDate = OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset);
That's saying you want the same local date/time, but with the specified offset. That changes which instant in time is being represented.
Instead, you really want to represent the same instant in time, but at a particular offset. So the shortest fix is:
OffsetDateTime offsetDate = utcZonedDateTime.toInstant().atOffset(offset);
However, there are a number of other aspects which could do with changing:
Prefer ZoneOffset.UTC to ZoneId.of("UTC")
Using EST as a time zone is confusing - it's not clear whether you expect it to mean "Eastern Time" (changing between EST and EDT) or pure standard time of UTC-5. Assuming you actually mean "Eastern Time" it would be better to use America/New_York as a zone ID.
It's unclear what you want to happen if the input string represents a skipped or ambiguous value in Eastern time. These happen around DST transitions.
Next, you don't need to convert the ZonedDateTime in Eastern time into a ZonedDateTime in UTC at all. Either convert it directly to an instant:
OffsetDateTime target = defaultZoneDateTime.toInstant().at(offset);
Or create a ZonedDateTime for the target instead:
ZonedDateTime target = defaultZoneDateTime.withZoneSameInstant(offset);
Given that an offset isn't really a time zone, I'd probably go with the first of these.
You're using
OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset)
to create your target. You're thus constructing an OffsetDateTime in the target offset, having a LocalDateTime equal to the LocalDateTime in the UTC zone.
What you want is the exact same transformation as the one you're using to go from the EST time to UTC: keep the same instant, but go to a different timezone:
defaultZoneDateTime.withZoneSameInstant(offset);
or, if you really want an OffsetDateTime and not a ZonedDateTime:
OffsetDateTime.ofInstant(defaultZoneDateTime.toInstant(), offset);

How to get a TimeZone ID from a TimeStamp Value

Is it possible to get a TimeZone ID from a certain TimeStamp ? If it is please explain by a simple code.
private String getDate(long timeStamp) {DateFormat objFormatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
TimeZone timeZone = TimeZone.getTimeZone("GMT+4:30");
//Instead of the Above code I want to get the TimeZone ID from my timeStamp objFormatter.setTimeZone(timeZone);
Calendar objCalendar =
Calendar.getInstance(timeZone);
objCalendar.setTimeInMillis(timeStamp * 1000);
String result = objFormatter.format(objCalendar.getTime());
objCalendar.clear();
return result;
}
tl;dr
Impossible to derive offset/zone from a count-from-epoch-in-UTC. But you can adjust into a zone.
Instant.ofEpochSecond( yourCount )
.atZone( ZoneId.of( "Pacific/Auckland" ) )
Avoid count-from-epoch
Firstly, avoid using a count-from-epoch number to track date-time values. Do you mean a count of whole seconds, milliseconds, microseconds, nanoseconds, or something else? Do you mean the Unix/Java epoch of 1970-01-01T00:00:00Z or one of the couple dozen other epochs in use by many computer systems?
Apparently you have whole seconds, and I'll assume the Unix/Java epoch.
Impossible to get zone from count-from-epoch
You cannot “ get a TimeZone ID from a certain TimeStamp”, that is impossible. Your count-from-epoch was made while accounting for a certain time zone, usually UTC. If must know that intended zone used in creating that count-from-epoch, it cannot be deduced.
Perhaps your goal is actually adjusting this count-from-epoch into a date-time for a particular region’s time zone. Read on.
java.time
Avoid the troublesome old date-time classes such as Date & Calendar now supplanted by the java.time classes.
Convert your count-from-epoch into a point on the timeline in UTC.
Instant instant = Instant.ofEpochSecond( yourCount ) ;
Assign your desired time zone.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Asia/Kabul" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
See this code run live at IdeOne.com.
Notice the 4.5 hour difference, changing from 02:40 to 07:10, appropriate for time in Kabul. This is the same moment, the same point on the time zone, but viewed through the lens of a different region’s wall-clock time.
input: 1500000000
instant: 2017-07-14T02:40:00Z
zdt: 2017-07-14T07:10+04:30[Asia/Kabul]
I would like to answer this question based on the definition of each terminology.
What is timestamp?
Timestamp or Unix Timestamp is the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970,minus the number of leap seconds that have taken place since then. Wikipedia
Wath is Time Zone?
A time zone is a region of the earth where the same standard time is used. Each time zone is described by an identifier and usually has the format region/city (Asia/Tokyo) and an offset from Greenwich/UTC time. For example, the offset for Tokyo is +09:00. Time Zone Oracle Doc
Regarding to both definitions there is no way to get a region of the earth based on a number of seconds (time), it is imperative to know from what region of the earth the time comes from.

After Parsing timestamp contains timezone to Date object, does Date object contain timezone information

If we have timestamps that contain the timezone info, like 2017-07-03T17:30:00-04:00, and parse it into java.Date or joda.DateTime.
Does it contains timezone information ?
I am asking this because i want to compare two different date instance. So if it does not contain timezone information, the day difference will be wrong with different timezones
UPDATE:
I run a quick unit test to verify, first convert date instance to milliseconds and convert back to TimeUnit after subtract these two milliseconds. The hours are different for different timezone
Both java.util.Date and Joda-Time have been supplanted by the java.time classes.
Your input string 2017-07-03T17:30:00-04:00 is in standard ISO 8601 format and has an offset-from-UTC at the end. That -04:00 means the string represents a moment four hours behind UTC.
This offset is not a time zone. A time zone is a history of offsets for a particular region. For example, America/Barbados or America/New_York.
Parse your string as an java.time.OffsetDateTime object.
OffsetDateTime odt = OffsetDateTime.parse( "2017-07-03T17:30:00-04:00" );
odt.toString(): 2017-07-03T17:30:00-04:00
You may compare OffsetDateTime instances by calling the methods IsEqual, isBefore, and isAfter.
To see the same simultaneous moment in UTC, extract an Instant.
Instant instant = odt.toInstant() ;
instant.toString(): 2017-07-03T21:30:00Z
The Z on the end is short for Zulu and means UTC.
It is going to depend on what type of DateTime you use, as of Java 8 you have these options:
A LocalDate or LocalDateTime. It is going to discard time zone information, you will wind up with a value that is 'valid' only for the local timezone. This value is ambiguous without some context as to the specific timezone of the server process which generated the value.
A ZonedDate or ZonedDateTime. This one preserves the time zone. Comparison is still going to be ambiguous: you have issues like DST or calendaring changes to contend with (depending on the range of datetime which you need to be compatible with). For sorting/comparison purposes you would probably want to convert it to a reference timescale, which is why:
An Instant represents a particular moment in time, on the absolute timescale of UTC. Any Instant is directly comparable with any other Instant and any ambiguity in values is resolved by the definition of Instant. Input values will be converted to the matching counterparts in UTC, so the original timezone (if any) will be lost even if the absolute time value will be preserved correctly. Instant is therefore not a good choice if you rely on the timezone to make decisions about location or locale, for instance.

Determining the hour in daylight saving time

Probably, this question is asked many times but I might not find the correct keywords to find them.
There was a time change at 30.10. The time was set back to 2 o'clock at 03:00 o'clock (Europe/Berlin). That means, at that day, there were two 02:00 o'clock (before and after time change)
Currently, I have two date (java.util.Date) objects. One of them was created at the first 02:00 o'clock (before the time was set back) and the second one was created at the second 02:00 o'clock.
Is there any way to differentiate these objects based on whether it was created at the first or second 02:00 o'clock?
Count from epoch
Your java.util.Date objects actually are in UTC but their toString method confusingly applies a time zone when generating the string output.
You can differentiate two Date objects by interrogating for their count from epoch. Internally the date-time is tracked as a number of milliseconds since first moment of 1970 in UTC. Call the badly-named method java.util.Date::getTime to get a long.
UTC
Record moments in UTC. Every programmer should learn to think in UTC, work in UTC, do logging in UTC, and keep a second clock on their desk and screen set to UTC.
UTC is the One True Time. All others are mere variations, every time zone being a deviation from UTC.
Let me repeat the acronym one more time to be clear: UTC
Instant
The Instant class is your new best friend in this arena, your go-to class for date-time work. It represents a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.now() ;
You need not worry about Daylight Saving Time (DST) cut-overs, politicians redefining DST ( often with little notice), nor other anomalies particular to any one time zone. Just use UTC.
To generate a String representing this moment, call toString for a string in standard ISO 8601 format. This string is always in UTC, so you don't have the problem of Date::toString applying a time zone while generating the string. The standard format has a Z on the end, short for Zulu, and means UTC.
instant.toString(): 2016-01-23T12:34:56.123456789Z
Converting Date
Convert your java.util.Date objects to Instant. New conversion methods have been added to the old classes.
Instant instant = myUtilDate.toInstant();
Zoned
I do not care about Berlin time. You, as a programmer, do not care about Berlin time. Your network and server admins do not care about Berlin time. We care about UTC.
The only people who care about Berlin time are end-users. For them, you can assign a time zone for presentation of data.
ZoneId z = ZoneId.of( "Europe/Berlin" );
ZonedDateTime zdt = instant.atZone( z );
Call toString to generate a String in standard ISO 8601 format but wisely extended by appending the name of the time zone in square brackets.
2016-07-07T08:00:15.768+02:00[Europe/Berlin]
Use DateTimeFormatter class to generate Strings representing the date-time value in other formats.
DST in effect?
You can interrogate to determine if Daylight Saving Time (DST) is in effect for any particular ZonedDateTime. See this Question.
ZoneRules rules = zdt.getZone().getRules();
Boolean dstInEffect = rules.isDaylightSavings( zdt.toInstant() );

Categories

Resources