Clock.fixed what is the role of zone - java

According to the javadoc for Clock.fixed, zone is:
zone - the time-zone to use to convert the instant to date-time, not null
But my code doesn't seem to be affected by using a different zone.
Clock mockClock = Clock.fixed(Instant.parse("2017-04-10T17:59:00Z"),
ZoneId.of("UTC"));
System.out.println("ZonedDateTime using clock "+ZonedDateTime.now(mockClock));
In the above code, whether I set the timezone as UTC or America/Chicago, the output is the same:
ZonedDateTime using clock 2017-04-10T17:59Z[UTC]
If it doesn't change the timezone, what is the role of zone?

Normally, a Clock instance retrieved using one of the static methods
Clock.system(ZoneId z)
Clock.systemDefaultZone()
Clock.systemUTC()
is a "running" clock that returns the current time. A Clock always tracks UTC, but also knows its timezone, which it uses when returning date/time values.
If you read the Javadoc carefully you'll see that Clock.fixed() returns a special type of clock that is not running, that is, it is always set to a specific instant and does not change with time.
ZonedDateTime.now(Clock clock) queries the given clock and returns the current time adjusted for the Clock's timezone.
So when you do
Clock mockClock = Clock.fixed(Instant.parse("2017-04-10T17:59:00Z"), ZoneId.of("UTC"));
ZonedDateTime zdt = ZonedDateTime.now(mockClock);
the following happens:
A special "not running" clock is created that is set to the date/time you gave, with a timezone specification of UTC.
The ZonedDateTime.now(mockClock) call reads the current value of the clock, which in your case is fixed, and creates a ZonedDateTime with the clock value and a timezone of UTC.

Related

Why does LocalDateTime.ofInstant() requires ZoneId

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…

What is logic of manipulation with timezones in Java/Kotlin?

Let's assume that I have client's time saved in my database as 2020-09-22T10:50:37.276240900
I need to present this date in web-service for client app depending on client timezone, for example I need to add 2 hours to saved date if client lives in UTC+2 timezone.
So what am I doing for ?
Getting date from entity and adding timezone to time taken from database (startDate: LocalDateTime)
entity.startDate.atZone(ZoneId.of("Europe/Vienna"))
what gives me the value of ZonedDateTime 2020-09-22T10:50:37.276240900+02:00[Europe/Vienna]
This value is what I'm expecting for, basically "initial time plus 2 hours". After that I would to format this time to have output with this 2 hours of being added, some kind of this
12:50 22.09.2020
but when I do format like this
entity.startDate
.atZone(ZoneId.of("Europe/Vienna"))
.format(DateTimeFormatter.ofPattern(NotificationListener.EUROPEAN_DATE_FORMAT, Locale.ENGLISH))
where const val EUROPEAN_DATE_FORMAT = "HH:mm dd.MM.yyyy"
I get this output 10:50 22.09.2020 which looks like my format is not applied properly, so I cannot see my 2 hours.
So my questions are:
am I correct to adding timezone of client app in described way ?
how to apply timezone in more precise way and format this date to see timezone zone applied ?
LocalDateTime.atZone does not "move" the point in time. In fact it tries to present the point in time where the local time in the given timezone is exactly what the LocalDateTime shows.
In other words: if your LocalDateTime represented 10:00 at some date, then the ZonedDateTime output of atZone will also represent 10:00 local time at the specified time zone (except in cases where that local time doesn't exist due to DST changes).
So if your stored time is actually in UTC, you need to add one more step:
ZonedDateTime utcTime = entity.startDate.atZone(ZoneOffset.UTC);
ZonedDateTime localTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Vienna"));
Alternatively you can avoid calculating the localTime each time and instead configure the DateTimeFormatter to use a given time zone (which means it'll do the necessary calculations internally) using DateTimeFormatter.withZone. If you do this then you can pass the utcTime to it directly.

Java: Fix incorrect timezone in date object

An external API returns an object with a date.
According to their API specification, all dates are always reported in GMT.
However, the generated client classes (which I can't edit) doesn't set the timezone correctly. Instead, it uses the local timezone without converting the date to that timezone.
So, long story short, I have an object with a date that I know to be GMT but it says CET. How can I adjust for this mistake withouth changing my local timezone on the computer or doing something like this:
LocalDateTime.ofInstant(someObject.getDate().toInstant().plus(1, ChronoUnit.HOURS),
ZoneId.of("CET"));
Thank you.
tl;dr ⇒ use ZonedDateTime for conversion
public static void main(String[] args) {
// use your date here, this is just "now"
Date date = new Date();
// parse it to an object that is aware of the (currently wrong) time zone
ZonedDateTime wrongZoneZdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
// print it to see the result
System.out.println(wrongZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// extract the information that should stay (only date and time, NOT zone or offset)
LocalDateTime ldt = wrongZoneZdt.toLocalDateTime();
// print it, too
System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// then take the object without zone information and simply add a zone
ZonedDateTime correctZoneZdt = ldt.atZone(ZoneId.of("GMT"));
// print the result
System.out.println(correctZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
Output:
2020-01-24T09:21:37.167+01:00[CET]
2020-01-24T09:21:37.167
2020-01-24T09:21:37.167Z[GMT]
Explanation:
The reason why your approach did not just correct the zone but also adjusted the time accordingly (which is good when desired) is your use of a LocalDateTime created from an Instant. An Instant represents a moment in time which could have different representations in different zones but it stays the same moment. If you create a LocalDateTime from it and put another zone, the date and time are getting converted to the target zone's. This is not just replacing the zone while keeping the date and time as they are.
If you use a LocalDateTime from a ZonedDateTime, you extract the date and time representation ignoring the zone, which enables you to add a different zone afterwards and keep the date and time as it was.
Edit: If the code is running in the same JVM as the faulty code, you can use ZoneId.systemDefault() to get the same time zone as the faulty code is using. And depending on taste you may use ZoneOffset.UTC instead of ZoneId.of("GMT").
I am afraid you will not get around some calculations here. I'd strongly suggest to follow an approach based on java.time classes, but alternatively you might use the java.util.Calendar class and myCalendar.get(Calendar.ZONE_OFFSET) for those calculations:
https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#ZONE_OFFSET

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.

Handling TimeZone change with Date class in JAVA

I am trying to print the current system date and time as below,
public class PrintDate {
public void getDate(){
while(true){
System.out.println(new Date());
}
}
public static void main(String[] args) {
new PrintDate().getDate();
}
}
This endless loop prints the current system time stamp as expected and it works fine when i make change in date or time in the OS but not with the timezone change..
Example :
I started the above code , which continuously print the current system time stamp as expected.
When i change the system date or time , it successfully gets reflected in the code.
When i change the system timezone , It is not reflecting in the code. It still show the same timezone since the program started.
May i know the reason behind this?
The time zone is part of the environment of the process. Changing the time zone globally for your system only affects new processes.
See also How to set time zone of a java.util.Date?:
Be aware that java.util.Date objects do not contain any timezone
information by themselves - you cannot set the timezone on a Date
object. The only thing that a Date object contains is a number of
milliseconds since the "epoch" - 1 January 1970, 00:00:00 UTC.
As ZZ Coder shows, you set the timezone on the DateFormat object, to
tell it in which timezone you want to display the date and time.
The answer by neuhaus is correct.
If you meant you changed the time zone of your host operating system while running that code, know that the Java Virtual Machine (JVM) has its own current default time zone.
Usually that default is picked up from that of the host OS. If so in your case, that must mean your Java implementation is detecting the host time zone only at launch and not checking for later changes in the host OS‘ time zone.
The time zone of your JVM can also be set as a configuration parameter upon launch. In that case I should think the JVM would purposely ignore the host OS’ time zone changes.
Any Java code in any thread of any app within that JVM can change the JVM’s current default time zone at any moment during runtime. Again, I should think the JVM would purposely ignore the host OS’ time zone changes.
The class doc for java.util.TimeZone.getDefault() outlines steps taking in determining the current default time zone.
If the cached default TimeZone is available, its clone is returned. Otherwise, the method takes the following steps to determine the default time zone.
• Use the user.timezone property value as the default time zone ID if it's available.
• Detect the platform time zone ID. The source of the platform time zone and ID mapping may vary with implementation.
• Use GMT as the last resort if the given or detected time zone ID is unknown.
The default TimeZone created from the ID is cached, and its clone is returned. The user.timezone property value is set to the ID upon return.
In my reading of that, it says the JVM is not going to detect any changes to the host OS setting. Once launched, and once a default has been determined, it is stored in that user.timezone property (and a value in cache) until changed with a call to setDefault.
java.time
You are using the old java.util.Date class which has been supplanted by the java.time framework in Java 8.
Use the java.time.ZonedDateTime class and specify the desired/expected time zone.
ZoneId zoneId = ZoneId.of( " America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.now( ZoneId );
You should almost never depend on the JVM’s current default time zone ( nor the current default Locale).
import java.util.Date;
public class DateDemo {
public static void main(String args[]) {
//Instantiate a Date object
Date date = new Date();
//display time and date using toString()
System.out.println(date.toString());
}
}

Categories

Resources