Days with 25 hours on Java (Daylight saving days) - java

I have to prepare some app that will graph the use of resources over time, but there is one day on the year that has 25 hours (the day with 23 hours is not a big problem).
How can I represent that with a Date? What would be the best way of doing it?
I would like to use Date class, (as it works, is Comparable and so on) as a key, but I'm not sure if this would work... Any ideas?

The Date class itself simply represents an instant in time, from the UTC Unix epoch. It has no concept of time zones, calendars etc.
It's hard to know what exactly you're trying to represent, but in general Joda Time is a much better date/time API than the types in java.util.*.
My main advice on thinking about time-related issues is to be really, really clear about what concept each value is meant to be representing. If you're interested in a local date (a date within a particular calendar, with no reference to a particular time zone), then Joda Time's LocalDate class is probably what you're after. If you need to associate time zone information, then DateTime is probably your best bet - although that does represent an instant within a particular calendar and time zone, rather than a whole day.

It important not to confuse how time is represented and how it is displayed.
In its representation, you have only the number of milli-seconds since 1/1/1970. When you do calculations on this you are just comparing this long value.
When you display this time/date, depending on your timezone, you can have a period of 25 hours or 23 hours with the same day.

Related

Should I use Instant or DateTime or LocalDateTime in Java entities?

In my Java (with Spring Boot and Spring Data JPA) applications, I generally use Instant. On the other hand, I would like to use the most proper data type for time values.
Could you please clarify me about these issues? What data type should I prefer for keeping date and time when:
1. To keep time precisely as timestamp (I am not sure if Instant is the best option)?
2. For normal cases when I just need date and time (as far as I know, the old library was obsolete, but not sure which library should I use).
I also consider the TimeZone, but not sure if using LocalDateTime with UTC solves my problem.
Any help would be appreciated.
Let's assume we need to cover the full span of date and time concerns. If there is a certain concern you don't have, that either collapses various types into 'well then they are interchangible' or simply means you don't need to use a certain part of the API. The point is, you need to understand what these types represent, and once you know that, you know which one to apply. Because even if various different java.time types all technically do what you want, code is more flexible and a lot simpler to read if the types you use represent the things you want them to. For the same reason String[] student = new String[] {"Joe McPringle", "56"}; is perhaps mechanically a way to represent a student's name and age, but things are just a lot simpler if you write a class Student { String name; int age; } and use that instead.
Local alarm clock
Imagine you want to wake up at 07:00 in the morning. Not because you have an appointment, you just like to be a fairly early riser.
So you set your alarm for 07:00 in the morning, go to sleep, and your alarm promptly goes off at 7. So far, so good. However, you then hop in a plane and fly from Amsterdam to New York. (it is 6 hours earlier in new york). You then go to sleep again. Should the alarm go off at 01:00 at night, or at 07:00 in the morning?
Both answers are correct. The question is, how do you 'store' that alarm, and to answer that question, you need to figure out what the alarm is attempting to represent.
If the intent is '07:00, whereever I might be at the time the alarm is supposed to go off', the correct data storage mechanism is java.time.LocalDateTime, which stores time in human terms (years, months, days, hours, minutes, and seconds) and not in computery terms (we'll get there later), and does not include a time zone at all. If the alarm is supposed to go off every day, then you don't want that either, as LDT stores date and time, hence the name, you'd use LocalTime instead.
That's because you wanted to store the concept of 'the alarm should go off at 7 o'clock' and nothing more than that. You had no intention of saying: "The alarm should go off when people in Amsterdam would agree it is currently 07:00", nor did you have the intent of saying: "When the universe arrives at this exact moment in time, sound the alarm". You had the intent of saying: "When it is 07:00 where-ever you are now, sound the alarm", so store that, which is a LocalTime.
The same principle applies to LocalDate: It stores a year/month/day tuple with no notion of where.
This does draw some perhaps wonky conclusions: Given a LocalDateTime object, it is not possible to ask how long it'll take until that LDT arrives. It is also not possible for any given moment in time to be compared to an LDT, because these things are apples and oranges. The notion 'Feb 18th, 2023, 7 in the morning on the dot' isn't a singular time. After all, in New York that 'moment' occurs a full 6 hours earlier than it would in Amsterdam. You can only compare 2 LocalDateTimes.
Instead, you would have to first 'place' your LDT somewhere, by converting it to one of the other types (ZonedDateTime or even Instant) by asking the java.time API: Okay, I want this particular LDT in a certain time zone.
Hence, if you are writing your alarm app, you would have to take the stored alarm (a LocalTime object), convert it to an Instant (which is what the nature of 'what time is it now, i.e. System.currentTimeMillis()' works on), by saying: That LocalTime, on the current day in the current local timezone, as an instant, and THEN comparing those two results.
Human appointments
Imagine that, just before jetting off to New York, you made an appointment at your local (in Amsterdam) barber. Their agenda was kinda busy so the appointment was set for June 20th, 2025, at 11:00.
If you stay in New York for a few years, the correct time for your calendar to remind you that you have an appointment with your barber's in an hour is certainly not at 10:00 on june 20th 2025 in New York. You'd have missed the appointment by then. Instead, your phone should chirp at you that you have an hour left to get to your barber's (a bit tricky, from New York, sure) at 04:00 in the middle of the night.
It sure sounds like we can say that the barber's appointment is a specific instant in time. However, this is not correct. The EU has already adopted legislation, agreed upon by all member states, that all EU countries shall abolish daylight savings time. However, this law does not provide a deadline, and crucially, does not provide a time zone that each EU member state needs to pick. The Netherlands is therefore going to change time zones at some point. They will likely choose to stick either to permanent summer time (in which case they'd be at UTC+2 permanently, vs. their current situation where they are at UTC+2 in summer and UTC+1 in winter, with, notably, different dates when the switch happens vs. New York!), or stay on winter time, i.e. UTC+1 forever.
Let's say they choose to stick to winter time forever.
The day the gavel slams down in the dutch parliament building enshrining into law that the dutch will no longer advance the clocks in march is the day your appointment shifts by one hour. After all, your barber is not going to go into their appointment book and shift all appointments by an hour. No, your appointment will remain on June 20th, 2025, at 11:00. If you have a running clock ticking down the seconds until your barber appointment, when that gavel comes down it should jump by 3600 seconds.
This belies the point: That barber appointment truly is not a singular moment in time. It's a human/political agreement that your appointment is when Amsterdam universally agrees it is currently June 20th, 2025, 11:00 – and who knows when that moment will actually occur; it depends on political choices.
So, you cannot 'solve' this by storing an instant in time, and it shows how the concept 'instant in time' and 'year/month/day hour:minute:second in a certain timezone' are not quite interchangible.
The correct data type for this concept is a ZonedDateTime. This represents a date time in human terms: year/month/day hour:second:minute, and the timezone. It doesn't shortcut by storing a moment in time in epochmillis or some such. If the gavel comes down and your JDK updates its timezone definitions, asking "how many seconds until my appointment" will correctly shift by 3600 seconds, which is what you want.
Because this is for appointments and it doesn't make sense to store just the time of an appointment but not the date, there is no such thing as a ZonedDate or a ZonedTime. Unlike the first thing which comes in 3 flavours (LocalDateTime, LocalDate, and LocalTime), there's only ZonedDateTime.
The universe/log time
Imagine you are writing a computer system that logs that an event occurred.
That event, naturally, has a timestamp associated with it. Turns out that due to severe political upheaval, the laws of the land decide that retrospectively the country has been in a different timezone than what you thought when the event occurred. Applying the same logic as the barber's case (where the actual moment in time jumps by 3600 seconds when the gavel comes down) is incorrect. The timestamp represents a moment in time when a thing happened, not an appointment in a ledger. It should not jump by 3600.
Timezone really has no purpose here. The point of storing 'timestamp' for a log event is so you know when it happened, it doesn't matter where it happened (or if it does, that is fundamentally a separate notion).
The correct data type for this is java.time.Instant. An instant doesn't even know about time zones at all, and isn't a human concept. This is 'computery time' - stored as millis since an agreed upon epoch (midnight, UTC, 1970, new years), no timezone information is necessary or sane here. Naturally there is no time-only or date-only variant, this thing doesn't even really know what 'date' is - some fancypants human concept that computery time is not concerned with in the slightest.
Conversions
You can trivially go from a ZonedDateTime to an Instant. There's a no-args method that does it. But note:
Create a ZonedDateTime.
Store it someplace.
Convert it to an Instant, store that too.
Update your JDK and get new time zone info
Load the ZDT.
Convert it to an Instant a second time.
Compare the 2 ZDTs and the 2 instants.
Results in different results: The 2 instants would not be the same, but the ZDTs are the same. The ZDT represents the appointment line in the barber's book (which never changed - 2025 june 20th, 11:00), the instant represents the moment in time that you are supposed to show up which did change.
If you store your barber's appointment as a java.time.Instant object, you will be an hour late to your barber's appointment. That's why it's important to store things as what they are. A barber's appointment is a ZonedDateTime. storing it as anything else would be wrong.
Conversions are rarely truly simple. There is no one way to convert one thing to another - you need to think of what these things represent, what the conversion implies, and then follow suit.
Example: You are writing a logging system. The backend parts store log events into a database of some sort, and the frontend parts read this database and show the log events to an admin user for review. Because the admin user is a human being, they want to see it in terms they understand, say, the time and date according to UTC (it's a programmer, they tend to like that sort of thing).
The logging system's storage should be storing the Instant concept: Epoch millis, and without timezone because that is irrelevant.
The frontend should read these as Instant (it is always a bad idea to do silent conversions!) - then consider how to render this to the user, figure out that the user wants these as local-to-UTC, and thus you would then on the fly, for each event to be printed to screen, convert the Instant to a ZonedDateTime in the zone the user wants, and from there to a LocalDateTime which you then render (because the user probably does not want to see UTC on every line, their screen estate is limited).
It would be incorrect to store the timestamps as UTC ZonedDateTimes, and even more wrong to store them as LocalDateTimes derived by asking for the current LocalDT in UTC as the event happens and then storing that. Mechanically all these things would work but the data types are all wrong. And that will complicate matters. Imagine the user actually wants to see the log event in Europe/Amsterdam time.
A note about timezones
The world is more complicated than a handful of timezones. For example, almost all of mainland europe is currently 'CET' (Central European Time), but some think that refers to european winter time (UTC+1), some thing that refers to the current state in central europe: UTC+1 in winter, UTC+2 in summer. (There's also CEST, Central European Summer Time, which means UTC+2 and isn't ambiguous). When EU countries start applying the new law to get rid of daylight savings, its likely e.g. The Netherlands on the west edge of the CET zone picks a different time than Poland on the eastern edge. Hence, 'all of central europe' is far too broad. 3-letter acronyms also are by no means unique. Various countries use 'EST' to mean 'eastern standard time', it's not just the eastern USA for example.
Hence, the only proper way to represent timezone names is using strings like Europe/Amsterdam or Asia/Singapore. If you need to render these as 09:00 PST for residents of the west coast of the USA, that's a rendering issue, so, write a rendering method that turns America/Los_Angeles into PST, which is an issue of localization, and has nothing to do with time.
The Answer by rzwitserloot is correct and wise. In addition, here is a summary of the various types. For more info, see my Answer on a similar Question.
To keep time precisely as timestamp (I am not sure if Instant is the best option)?
If you want to track a moment, a specific point on the timeline:
InstantA moment as seen with an offset-from-UTC of zero hours-minutes-seconds. This class is the basic building-block of the java.time framework.
OffsetDateTimeA moment as seen with a particular offset, some number of hours-minutes-seconds ahead of, or behind, the temporal meridian of UTC.
ZonedDateTimeA moment as seen with a particular time zone. A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region, as decided by their politicians.
If you want to track just the date and time-of-day, without the context of an offset or time zone, use LocalDateTime. This class does not represent a moment, is not a point on the timeline.
For normal cases when I just need date and time
If you are absolutely sure that you want only a date with time-of-day, but do not need the context of an offset or time zone, use LocalDateTime.
using LocalDateTime with UTC
That is a contradiction, and makes no sense. A LocalDateTime class has no concept of UTC, nor any concept of offset-from-UTC or time zone.
Spring Data JPA
The JDBC 4.2+ specification maps SQL standard data types to Java classes.
TIMESTAMP WITH TIME ZONE columns map to OffsetDateTime in Java.
TIMESTAMP WITHOUT TIME ZONE columns map to LocalDateTime in Java.
DATE columns map to LocalDate.
TIME WITHOUT TIME ZONE columns map to LocalTime.
The SQL standard also mentions TIME WITH TIME ZONE, but this type is meaningless (just think about it!). The SQL committee has never explained what they had in mind, as far as I know. If you must use this type, Java defines the ZoneOffset class to match.
Note that JDBC does not map any SQL types to Instant nor ZonedDateTime. You can easily convert to/from the mapped type OffsetDateTime.
Instant instant = myOffsetDateTime.toInstant() ;
OffsetDateTime myOffsetDateTime = instant.atOffset( ZoneOffset.UTC ) ;
… and:
ZonedDateTime zdt = myOffsetDateTime.atZoneSameInstant( myZoneId ) ;
OffsetDateTime odt = zdt.toOffsetDateTime() ; // The offset in use at that moment in that zone.
OffsetDateTime odt = zdt.toInstant().atOffset( ZoneOffset.UTC ) ; // Offset of zero hours-minutes-seconds from UTC.
I also consider the TimeZone
The TimeZone class is part of the terrible legacy date-time classes that were years ago supplanted by the modern java.time classes. Replaced by ZoneId and ZoneOffset.
You should take a look at the Java Date and Time API introduced with Java 8. Each class like Instant, LocalDateTime, ZonedDateTime etc. has a documentation as JavaDoc. If you have problems understanding the documentation, please provide a more specific question.

Timestamp and Timespan equivalent in java.time package at Java 8

Hi I read some article about new java.time package.And some of articles say we shouldnt use java.util.Date family.We can use offsetDateTime or LocalDateTime instead of java.util.Date.
And I am wondering about what should we use instead of Timestamp in new java.time package?
As I check Timestamp use
public class Timestamp extends java.util.Date
There are two types of time spans in Java 8:
Period - represents time difference between two points in years, months, days
Duration - represents a time duration - the actual physical time-span, doesn't depend on what is the start time
For example 1 month "period" will be 30 days in April, 31 in may, so the values might be calculated differently in different contexts. Also time zone changes (daylight saving) are taken into account. Duration of certain number of seconds, minutes, hours, days, will not depend on the context. For example 30 days will always be 30 days.
You have a good description in the doc:
https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
Timestamp equivalent
I am assuming that you are referring to the outdated java.sql.Timestamp and/or to the timestamp (without time zone) and timestamp with time zone datatypes of SQL. The first was designed for use with the last two. The answer is different for the two.
For a timestamp in SQL timestamp with time zone is clearly recommended since it actually unambiguously defines a point in time, which is in the definition of what a timestamp is. For a timestamp with time zone you should use the java.time class that you already mentioned, OffsetDateTime. Some JDBC drivers and JPA implementations will accept Instant too.
In most SQL dialects a mere timestamp is a date and time of day with high resolution (for example microseconds) without time zone. Lacking time zone it does not define a point in time, so calling it a timestamp is really a lie. In any case the corresponding java.time type is the other class you mentioned, LocalDateTime.
All of the mentioned java.time classes have resolution of nanoseconds. I know of no SQL dialect that would demand more than that.
You are fully correct. Not only is java.util.Date poorly designed and long outdated. Timestamp is a true hack on top of that class. I recommend you don’t use any of them.
Timespan equivalent
Artur Olszak in another answer has already nicely given the basis of Period and Duration. There is no need for me to repeat that. As a supplement, please be aware that even though Duration has methods for converting to and from a number of days, it isn’t really well suited for days since it assumes that a day is always 24 hours, which is not always the case because of summer time (DST) and other anomalies. As soon as you need to count days, I recommend either Period or a simple number of days.
Links
Wikipedia article: Timestamp.
My answer to a related quetsion about retrieving an SQL timestamp into a java.time type.

Using localDate with UTC

I'm encountering a problem using LocalDate in UTC. My server uses UTC, and my database uses UTC. I used LocalDate to store a billingDate for a subscription based application.
What happens is that we bill at midnight UTC (when doing comparisions like billingDate <= LocalDate.now()). We actually mean to bill sometime after midnight PST.
I really felt like using LocalDate was appropriate here, because we just want to bill at some point during that day. However, it doesn't seem practical when doing comparisons either directly in the code or in the database (billing_date <= CURRENT_DATE()). Did I make a mistake, should this be a ZonedDateTime in PST? Or should we be converting to ZonedDateTime for comparisons? It feels error prone, we need to remember to convert any time we do a comparision, but perhaps this is the correct solution?
Does anyone have experience with this situation and found a nice solution?
I've taken a look at this question, but it doesn't answer my question: Spring REST LocalDate UTC differs of one day
I suggest that this is just a matter of passing the desired time zone to LocalDate.now(ZoneId).
Use LocalDate.now(ZoneId.of("Asia/Manila")) for Philippine Standard Time. At the moment it yeilds 2019-07-09.
Use LocalDate.now(ZoneId.of("Pacific/Pitcairn")) for Pitcairn Standard Time. It just gave 2019-07-08.
I am assuming that you didn’t mean Pacific Standard Time since no time zone uses Pacific Standard Time as we speak (those that do in winter, are on Pacific Daylight Time now). In any case, mind you that three letter time zone abbreviations are often ambiguous.
The java.time classes that have a now method generally have three overloaded variants of it:
One that takes a ZoneId arguments that I recommend for general use.
One that takes a Clock argument that is great for testability. A Clock includes a time zone, so this one too gets you the current date and/or time in that specified time zone.
One that doesn’t take any arguments and uses the JVM’s default time zone. I recommend that you never use it. It’s nice for the reader to know that you have considered time zone and chosen which one you want. And the default time zone can be changed at any time by any program running in the same JVM, so is not stable enough to rely on for real work.
I feel like you should be using Instants.
I really felt like using LocalDate was appropriate here, because we just want to bill at some point during that day.
Well, no. You do care about the time you bill, because your database cares about the time. It stores the billing time as 00:00 UTC. Since that is an instant in time, I think Instant would be the most suitable choice here. You could use a ZonedDateTime as well, but considering that you are probably getting a java.sql.Date from your database, which has a toInstant method already, using Instants is more convenient.
You can get an instant from a year, month, day like this:
LocalDate ld = LocalDate.of(2019, 7, 8);
Instant i = ld.atStartOfDay(ZoneId.of("America/Los_Angeles")).toInstant();
America/Los_Angeles is PST.

Create company specific timezone for 'working day' in java

I work at a company where part of the work for a day is done in the early hours of the next day (i.e. shipping orders). Now for several processes (mainly reporting), we want to let the 'working day' end at 04:00 the next morning so we get more consistent reporting values per day.
We want this to always be at 04:00 the next morning and since we are affected by daylight saving times in our area (Europe - Netherlands) we effectively want a 4 hour shifted variant of our normal timezone 'Europe/Amsterdam' (in our case).
To make this as easy to use for all applications in our company I would like to create a small library that simply contains the code to provide my coworkers to get a modified instance of TimeZone that does this. That way all normal time/date manipulation methods can be used in conjunction with this special time zone.
I did a deep dive into the standard Java 8 code/Javadoc related to the TimeZone/ZoneInfo instances and at this moment I do not understand what the correct field is to change in the returned TimeZone/ZoneInfo instance.
At this point, my best guess is setting the RawOffset to 4 hours, but I'm not sure.
What is the correct way to achieve my goal?
Update:
I had a look at the suggested LocalTime and as I expected: It needs a timezone definition as being what it should use as "Local" when converting an existing timestamp (usually epoch milliseconds) into the "Local" timezone.
Looking at all these classes seems like I'll be using the LocalDate more often than the LocalTime.
Effectively the code I expect to have is something like this:
long epoch = 1525033875230L; // Obtained from some dataset
LocalDate localDate = LocalDateTime
.ofInstant(Instant.ofEpochMilli(epoch),
ZoneId.of("Europe/Amsterdam"))
.toLocalDate();
Where I expect that I need to change that Zone into the 'right one'.
If I have got that correctly, what you really need is a way to convert a milliseconds value since the epoch to a date in a way where days don’t change a 00:00 but not until 04:00.
static ZoneId zone = ZoneId.of("Europe/Amsterdam");
static LocalTime lastShiftEnds = LocalTime.of(4, 0);
public static LocalDate epochMilliToDate(long epoch) {
ZonedDateTime dateTime = Instant.ofEpochMilli(epoch)
.atZone(zone);
if (dateTime.toLocalTime().isAfter(lastShiftEnds)) { // normal date-time
return dateTime.toLocalDate();
} else { // belonging to previous day’s night shift
return dateTime.toLocalDate().minusDays(1);
}
}
Use for example like this:
long epoch = 1_525_050_875_230L;
System.out.println(Instant.ofEpochMilli(epoch));
LocalDate date = epochMilliToDate(epoch);
System.out.println(date);
Output is:
2018-04-30T01:14:35.230Z
2018-04-29
From printing the Instant you can see that the time is after midnight (really 03:14:35.230 in Amsterdam time zone). And the method has correctly deemed that this time belongs to April 29 rather than April 30.
Perhaps I am missing something? On the other hand, if that were me I’d go quite a long way to avoid inventing a time zone that doesn’t exist in real life. Such a time zone would be bound to confuse your coworkers.

Printing milliseconds of Java epoch returns 18000000?

In Joda, if I print
DateTime(GregorianChronology.getInstance())
.withYear(1970)
.withMonthOfYear(1)
.withDayOfMonth(1)
.withHourOfDay(0)
.withMinuteOfHour(0)
.withSecondOfMinute(0)
.withMillisOfSecond(0).getMillis();
I see 18000000 (this also happens to be 1/4th of MILLIS_PER_DAY, FWIW).
What I don't understand is that if the milliseconds represents the offset from the epoch which is defined as Jan-1970-01-01, then shouldn't the milliseconds be 0?
The epoch is Jan-1970-01-01 GMT. The instance you have, obviously has a different DateTimeZone. In fact it lookds like you're at GMT+5. (18000000 millis = 5 hours)
I believe the issue is related to the way Java dates include the time zone as part of there calculations.
For me, this means I'm +10 hours ahead of the epoc.
Try creating a Date/Time value that is set to 0 GMT.
The "epoch" is a specific and universal instant, a point in the universe time (like, say the moment in which the Apollo XI landed on the moon). This reference point can be represented differently in different countries (and a martian could also represent it with his own calendar). For example, for the people in England (GMT), that's the moment in which the hands of their clocks marked "00:00:00" and their (Gregorian) calendars marked "1/1/1970"; but that's just an example.
The line
DateTime(GregorianChronology.getInstance()).withYear(1970).withMonthOfYear(1)
.withDayOfMonth(1).withHourOfDay(0).withMinuteOfHour(0)
.withSecondOfMinute(0).withMillisOfSecond(0)
gives you the instant in which the clocks and the calendars in your country marked "00:00:00 1970-01-01". That's, in general, a different instant.

Categories