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

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.

Related

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.

No. of days between two timestamps

I have two timestamp values. like
Timestamp date1;
Timestamp date2;
Now I want to find no. of days between these two timeStamps like in java
int no_of_days = difference(date2 - date1)
the above codes are just for clarification of the question. just ignore the coding mistakes.
You can use Duration for this (it works for java 8) :
Duration between = Duration.between(date1.toInstant(), date2.toInstant());
int no_of_days = between.get(ChronoUnit.DAYS);
If you are using Java8, it will be much easier. Here's a one way of doing it.
Duration.between(date1.toLocalDateTime(), date2.toLocalDateTime()).toDays();
Other answers given here so far only handle a day unit as fixed amount of 24 hours by using the class java.time.Duration. If you consider your timestamps in the context of daylight saving switching time zones then this is probably not what you want.
During the change from winter to summer time, a calendar day can last only 23 hours (or even 23:30 in some rare cases). And in autumn when switching back to winter time, the calendar days can be more than 24 hours long. So you also need a time zone to handle this situation in order to calculate durations in the correct way.
Suggested solution if your timestamps have been stored in the database coming from instants:
ZonedDateTime zdt1 = date1.toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime zdt2 = date2.toInstant().atZone(ZoneId.systemDefault());
long days = java.time.temporal.ChronoUnit.DAYS.between(zdt1, zdt2);
Of course, you are free to specify any other zone than the system time zone. The result can vary depending on the chosen zone.
Suggested solution if your timestamps have been stored coming from LocalDateTime without any zone context:
LocalDateTime ldt1 = date1.toLocalDateTime();
LocalDateTime ldt2 = date2.toLocalDateTime();
long days = java.time.temporal.ChronoUnit.DAYS.between(ldt1, ldt2);
Here I still prefer the enum ChronoUnit.DAYS because the type java.time.Duration internally stores seconds and nanoseconds only and is hence rather designed for machine-like timestamps. And another remark: The second solution implicitly uses your system time zone for the conversion from java.sql.Timestamp to LocalDateTime. This is only correct if you also have stored your timestamps that way.
By the way, with a JDBC-4.2-compliant driver, you could also retrieve your java.time-type directly, see also a related post here on SO.

Why does JDBC adjust the time to default timezone when it is saving a date?

This question is related to this but this specific question focuses on the why. So no, this isn't a duplicate.
Quoting the answer:
The problem is that Java Date objects don't store a time zone. The
value is always in UTC, and is parsed and formatted in a given time
zone, usually the JVM's default time zone.
Oracle DATE columns are also stored without time zone, but should
represent the date as seen by the user. In 99.99% of cases, that means
the date in the JVM's default time zone.
So, the JDBC driver takes the Timestamp / Date value, which is in UTC,
converts it to the default time zone, and saves that to the database.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
What is it trying to solve by adjusting the value before saving it to the database?
The answers to these questions are the whys.
I couldn't see the benefit of the design and I can only see are the problems associated with it. Case in point is when saving is done in a specific timezone and retrieval is done in another timezone. The amount of questions being thrown at this specific topic just proves my point.
So ultimate question is, why it was designed that way? What are the reasons?
Date-time handling is a surprisingly complicated topic. Our intuitive understanding of time works against us as programmers, making this topic difficult to master. Furthermore, poor date-time handling in old databases and old classes make the job even more confusing.
java.time
Firstly, avoid the wretched old date-time classes bundled with the earliest versions of Java. Never use java.util.Date, java.util.Calendar, java.sql.Timestamp, nor other related classes. Use only the java.time classes. If you must interface with old code not yet updated to java.time, call on new conversions methods added to the old classes.
Date was replaced by Instant.
The problem is that Java Date objects don't store a time zone.
Not true. An Instant (and a Date) is always in UTC. Both the modern and legacy class represent a a count of fractional seconds since the first moment of 1970 in UTC, 1970-01-01T00:00:00Z. Always in UTC, easy-peasy.
Oracle DATE columns are also stored without time zone,
True.
The Oracle DATE data type represents only a date with time zone but lacks any concept of time zone or offset-from-UTC. This is apparently a legacy type, created before the SQL standard defined some basic date-time types. In the standard, the TIMESTAMP WITHOUT TIME ZONE might map close to Oracle DATE.
but should represent the date as seen by the user. In 99.99% of cases, that means the date in the JVM's default time zone.
I have no idea what the author meant by that. I think that is their clumsy way of saying that any type similar to the SQL-standard TIMESTAMP WITHOUT TIME ZONE simply takes any given date or date-with-time-of-day as-is, with no attempt at adjusting between zones or offsets. So if you pass January 21, 2018 at noon, it stores a value equivalent to this string 2018-01-23T12:00 without any regard to whether that was noon in Montréal Québec or noon in Kolkata India (two different moments, hours apart).
So, the JDBC driver takes the Timestamp / Date value, which is in UTC, converts it to the default time zone, and saves that to the database.
While the JDBC driver is unspecified here, I doubt this is its behavior. Such behavior would be a contradiction of the behavior of the Oracle DATE type which is no such zone adjustment. The Oracle DATE type (as I read the documentation; I'm not an Oracle user) is agnostic or unaware of zones/offsets.
In Java, the class mapping to SQL-standard TIMESTAMP WITHOUT TIME ZONE and Oracle DATE is LocalDateTime. You should use these zone-less types only in these three situations:
The zone or offset is unknown.This is bad. This is faulty data. Analogous to having a price/cost without knowing the currency. You should be rejecting such data, not storing it.
The intention is “everywhere”, as in, every time zone.For example, a corporate policy that states “All our factories will break for lunch at 12:30" means the factory in Delhi will break hours before the factory in Düsseldorf which breaks hours before the factory in Detroit.
A specific moment in the future is intended, but we are afraid of politicians redefining the time zone.Governments change the rules of their time zones with surprising frequency and with surprisingly little warning or even no warning at all. So if you want to book an appointment at 3 PM on a certain date, and you really mean 3 PM regardless of any crazy decision a government might make in the interim, then store a LocalDateTime. To print a report or display a calendar, dynamically apply a time zone (ZoneId) to generate a specific moment (ZonedDateTime or Instant). This must be done on-the-fly rather than storing the value.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
The JDBC driver should not be doing any adjustments to UTC for a type of Oracle Date or SQL-standard TIMESTAMP WITHOUT TIME ZONE.
If two users at 2018-06-06T21:53Z, one in Québec and one in India, both simultaneously save the current moment of their own parochial wall-clock time into a column of type SQL-standard TIMESTAMP WITHOUT TIME ZONE or Oracle DATE, then we should see two rows with values:
2018-06-06T17:53 (notice the date is “yesterday”)
2018-06-07T03:23 (notice the date is “tomorrow”)
The values are different because America/Montreal is four hours behind UTC while Asia/Kolkata is five and a half hours ahead of UTC, and no adjustment for time zones was made. To repeat myself yet again, the stored values here represent only a date and a time-of-day, but without any context of time zone or offset-from-UTC, they do not represent a moment.
The confusion may be coming from the fact that some databases such a Postgres do adjust incoming values into UTC for values heading to a column of a different type, the TIMESTAMP WITH TIME ZONE type (note the WITH versus WITHOUT). Postgres and something other databases use any passed zone/offset info to adjust into UTC value, then discard the zone/offset info. So the type name is something of a misnomer, and you can think of it as TIMESTAMP WITH RESPECT FOR TIME ZONE.
If those same two users seen above at 2018-06-06T21:53Z were saving the current moment into a SQL-standard column of type TIMESTAMP WITH TIME ZONE, then the two rows would appear as:
2018-06-06T21:53Z
2018-06-06T21:53Z
The Z at the end is pronounced Zulu and means UTC.
What is it trying to solve by adjusting the value before saving it to the database?
Generally, the best practice in date-time handling is to work in UTC rather than in other zones/offsets.
When working as a programmer or sysadmin, forget about your own parochial time zone. Translating back-and-forth from your own zone to UTC or other zones will drive you batty. Think of UTC as the One True Time; all other zones are but mere variations.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Store.
String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ; // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.
myPreparedStatement.setObject( 1 , instant ) ; // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.
Retrieve.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Present that moment in a particular time zone.
ZoneId z = ZoneId.of( "Asia/Kolkat" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

How can I store and display dates in the timezone as they were originally given?

I have a server that is being fed data from clients in different timezones. The data feed contains people, their date of birth and other dates of events. For our purposes, it would be convenient if we could just store the dates as their given to us.
For example, if the client is in California and it tells us the person's date of birth is May 31st, we'd like to store it in the database as May 31st 1999, pacific time. This way, no matter what timezone you're in, you can see that the person was born on May 31st.
At the same time, we want to be able to query this data to be able to figure out things like, "Is this person a minor" or "did this event happen less than 24 hours ago?
The clients are sending us data over a http based rest API. The server is written in Java (using eclipselink). The database is postgresql. Is it possible to satisfy these requirements?
Typically, people say to store everything as UTC, but I feel like that would be a bad idea because we'd lose the timezone of the original data.
UTC is the way to go. For timestamptz (timestamp with time zone) the time zone of input values only serves as modifier for Postgres to calculate UTC internally. (For timestamp [without time zone] any appended time zone would be ignored!). The time zone is not saved. You need to save it additionally to know where in the world something happened.
If you do that, you might as well store local timestamps. Just don't get confused which is which. Either convert everything to UTC (happens automatically for timestamptz), or convert everything to local time (define "local": your local? local to the db server? local to the user?).
In particular, rather store the exact time zone name (or a reference to it) than just "pacific time". This is more exact for daylight saving time, leap seconds or other events.
Detailed explanation:
Ignoring timezones altogether in Rails and PostgreSQL
About time zone names and abbreviations:
Time zone names with identical properties yield different result when applied to timestamp
About time zone handling:
Accounting for DST in Postgres, when selecting scheduled items
Preserve timezone in PostgreSQL timestamptz type
The answer by Erwin Brandstetter is 100% correct.
Calculating Age
As for matters such as calculating age of a minor, that is a bit tricky because of time of day. Using the Joda-Time library, you can call the method withTimeAtStartOfDay to set a DateTime object to the first moment of the day. Usually that first moment is the time 00:00:00 but not always because of Daylight Saving Time or other anomalies. Ignore the "midnight"-related classes and methods as they have been supplanted by the above-mentioned method.
Furthermore, to be really accurate about age to cover yourself legally, you might want to calculate age as the first moment of the day or two after the birth date-time. Unless you know their time of birth and the time zone of that birth, you cannot know exactly their age.
Avoid j.u.Date/.Calendar
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. Avoid them. Use either Joda-Time and/or the new java.time package bundled in Java 8 (inspired by Joda-Time but re-architected).
Unlike java.util.Date, the date-time objects in both the other libraries know their own assigned time zone. A j.u.Date is particularly confusing because, while it has no time zone assigned, its toString method applies the JVM’s current default time zone thereby creating the illusion of an assigned time zone.
Joda-Time | java.time
With Joda-Time and java.time, things are much clearer. You specify a time zone to each date-time object (otherwise the JVM default is assigned). You can easily convert from one time zone to other.
Both libraries use immutable objects, where a new object based on the original is created rather than changing (mutating) the original.
You can call getZone a Joda-Time DateTime object to obtain its time zone name (ID) and its offset from UTC for your records if you deem that important.
ISO 8601
Learn about the ISO 8601 standard for sensible String formats of date-time values. Consider using those in your text-based API. ISO 8601 is now the norm for all new Internet protocols. Ex: 2014-08-13T16:02:01Z or 2014-12-22T11:54:23+04:00.
And use proper time zone names. Avoid the 3 or 4 letter codes as they are neither standardized nor unique.

Days with 25 hours on Java (Daylight saving days)

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.

Categories