How to deal with timstamps with extra milliseconds in a Java Date - java

I have a javax.ws.rs REST API that accepts an object body that has a timestamp field mapped to a util Date with JPA, but something sending to it is sending a timestamp with extra milliseconds (maybe supposed to be nanoseconds?) and that causes the date to be in the future when I use the object within my request handler method.
for example this came in: "TimeStamp": "2020-04-24T16:26:11.9376071Z",
and it resolved to "2020-04-24T19:02:27" in the object.
If I use Postman and send the exact same message just with the TimeStamp reduced to 2 milliseconds it works as expected and the date is correct.
So, assuming I can't change what's being sent but want to be able to handle it, how can I shorten the milliseconds so the Date resolves correctly?

Such strings are parsed according to a pattern. The numbers following the 'dot' are parsed as milliseconds, and parsing dates is evidently configured in lenient mode, which means overflow is adjusted into the higher fields. If you parse 9376071 as milliseconds, that's 9376.071 seconds; that's about 2 hours and change. Add that to '16:26:11' and you get 19:02. So, that's what's going wrong here.
I don't see enough detail in the mechanism you're using to transit this string into a value of type java.util.Date - in various frameworks you can explicitly specify the pattern. However, the 'old' API (the one java.util.Date belongs to cannot parse this input - it has no option to parse that dangling batch of digits properly. Yes, really. The old API (java.text.SimpleDateFormat) cannot actually read ISO8601 - a grievous ommision which strongly suggests you really, really need to stop using this incapable old deprecated stuff. (ISO8601 does indeed allow any number of digits on the fractional part, and it allows a fractional part on the 'lowest' entry in the input, therefore, the timestamp you get, while somewhat exotic, fits the ISO8601 spec).
But, good news!
The newer API does it just fine!
import java.time.Instant;
import java.time.format.DateTimeFormatter;
class Test { public static void main(String[] args) {
Instant parsed =
DateTimeFormatter.ISO_INSTANT.parse("2020-04-24T16:26:11.9376071Z", Instant::from);
System.out.println(parsed);
}}
I'm not entirely sure how you can tell your framework to stop using the bad API, but once you've managed to tell it to stop hitting itself in the face with the old one, all will be well again.
Sidenote: j.u.Date is a really bad type to use; it does not represent a date at all, but an instant in time, and badly at that. In general I wouldn't use API that is so epically badly named! May I suggest java.time.Instant instead? Its name matches what it represents, and should be drop-in ready. Another workable option is ZonedDateTime or LocalDateTime depending on what it represents).

Related

Create calendar from String date without knowing format

I want to create an instance of Calendar with string date coming from server. Now I don't know what format server is sending .
It can be changed for different countries. I know I can ask them to add another key for dateFormat and create Calendar from it. But still I want to know Is there any way to create Calendar Instance without knowing current string date format.
I have gone through this and this. But none fulfill my requirement
This is impossible.
If the server sends the value "1/2/2017", you have no way of knowing if this refers to January 2nd or February 1st.
If the server sends the value "מָחָר", in theory you could realize that this might be a Hebrew translation of the word "tomorrow" (at least, according to Google Translate), but even then, it is not clear whether this is to be taken relative to today or some other date.
If the server sends the value "I want to create an instance of Calendar with string date coming from server", you have no means of creating a date from that, at least using any algorithm that would make sense to people.
And so on.
The only reason a server should return a date in an arbitrary format is if the date would only ever be read by the user who provided the value in the first place and presented as plain text verbatim, without parsing. Otherwise, the server should supply the date in a standardized format, with the UI consuming that date being responsible for formatting it in a user-friendly (and, ideally, locale-aware) fashion.
You're welcome to try to brute-force the problem, iterating over a series of date formats and seeing if any result in a seemingly-valid date. This fails the 1/2/2017 scenario (as there are at least two formats that would return a seemingly-valid date), but perhaps you know enough about the server to narrow down the possible formats to reduce the odds of collisions like this.
The Joda Date & Time API has a date parser which can parse date strings in many formats. Note that some datetime strings can be ambiguous: 10-09-2003 could mean October 9 or September 10.

URL-compatible and easy to parse date-time format?

Using Play 2 I want to create a REST API, which shall include
/resource/<startDateTime>
meaning return all items of resource with a startDateTime greater than the startDateTime given in the URL.
So now I need some DateTime format, that can be passed by an URL in a human-readable format and is still easy to parse into a Java Date object inside my Play 2 controller. Any hints / best practices on that? Thanks for any hint!
Update:
Even better would be if Play would do the parsing for me. For java.util.Date in the routes configuration I am getting the error
No QueryString binder found for type java.util.Date. Try to implement an implicit QueryStringBindable for this type.
Is there anything predefined to parse a Date?
Update:
Expected input:
Could be e.g.
http://site.com/resource/20121231-141557 # 2012/12/31 14:15:57
or sth. else, easy readable - I don't care as long as it can be transfered using an URL and is easy to parse into a Date object.
There is an ISO standard for dates, number 8601.
http://en.wikipedia.org/wiki/ISO_8601
Date and time values are organized from the most to the least significant: year, month (or week), day, hour, minute, second, and fraction of second.
It seems you have two questions here:
How to format and parse dates easily? I think the best library for handling dates in java is Joda Time. It has methods for formatting and parsing dates in different formats.
How to define a route with a custom parser? For that, you need to define your own QueryStringBindable. Look at this answer about Doubles for an example.
You can check native Play2 Path binders here : https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/mvc/Binders.scala#L251
Currently, there is nothing to handle Date in parameters.
But you can write your own PathBinder on top of DateTime (JodaTime), with the ISO 8601 format (use ISODateTimeFormat)
I think it will be a good Pull request ;)

Why was Date.getTimezoneOffset deprecated?

The documentation for Date.getTimezoneOffset says:
Deprecated. As of JDK version 1.1, replaced by
-(Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000).
Why was it deprecated? Is there a shorter way (Apache Commons?) to get the offset from UTC in hours/minutes? I have a Date object ... should I convert it to JodaDate for this?
And before you ask why I want the UTC offset - it's just to log it, nothing more.
There are 2 questions here.
Why was Date.getTimezoneOffset deprecated?
I think it is because they actually deprecated nearly all methods of Date and moved their logic to calendar. We are expected to use generic set and get with the parameter that says which specific field we need. This approach has some advantages: less number of methods and the ability to run setters in a loop passing a different field each time. I personally used this technique a lot: it makes code shorter and easier to maintain.
Shortcut? But what's wrong with call
Calendar.get(Calendar.DST_OFFSET) comparing to
Calendar.getTimeZoneOffset()
As far as I can see the difference is 6 characters.
Joda is a very strong library and if you really have to write a lot of sophisticated date manipulation code switch to it. I personally use the standard java.util.Calendar and don't see any reason to use external libraries: good old calendar is good enough for me.
All of the date manipulation logic was moved out of Date once the Java implementers realized that it might need to be implemented differently for different types of calendars (hence the need to use a GregorianCalendar to retrieve this info now). A Date is now just a wrapper around a UTC time value.
Take care before you paste code from this page.
Perhaps just me but I believe that in order to get the tz offset in minutes you need to do
int tzOffsetMin = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET))/(1000*60);
rather than what the Javadoc says, which is:
int tzOffsetMin = -(cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET))/(1000*60);
Calendar.ZONE_OFFSET gives you the standard offset (in msecs) from UTC. This doesn't change with DST. For example for US East Coast timezone this field will always be -6 hours regardless of DST.
Calendar.DST_OFFSET gives you the current DST offset (in msecs) - if any. For example during summer in a country that uses DST this field is likely to have the value +1 hour (1000*60*60 msecs).

How should date properties be defined in a java class?

I have a simple java object with several date properties and I always seem to change my mind on how to define them. Should the properties be defined as date objects or strings? The object is going to be used in struts 1.3 application with iBatis as the persistence layer and mysql as the database. The database columns are defined as datetime and they can possibly be null and I usually don’t care about the time portion.
public Date getForcastDate();
or
public String getForcastDate();
Most of the existing code base uses strings, but that just doesn’t seem quite right to me.
Keep your dates as Dates. That way you can change formatting depending on locales, check for invalid dates, sort by them etc.
By keeping them as strings you're potentially throwing away data (e.g. milliseconds if your formatter doesn't use them) and definitely behaviour.
Using strong-typing (e.g. keeping them as Dates) will aid in terms of development. Your method signatures become clearer, refactoring using IDE tooling becomes easier etc. Otherwise you end up with APIs that talk in nothing but strings, it's trivial to mix up parameters, and it becomes impossible to work out what's going on.
Tip: Check out Joda-Time as a better alternative to the standard java.util.Date.
I would use Date object because it cleaner to store a Date and convert it to a String when needed. Otherwise you have to hard code a formatted date into a String field.
I would never use Strings in this cas as what would today be 8/3/11 or 3/8/11 or 2011-03-08. This is really a specific case of trying to use the most restrictive type/class possible for a variable. This is so that you can understand its behaviour more fully, both by having a restricted or specialised set of methods and by helping documentation of other classes using it. Using a Date here would allow you to use a Calendar object to add days or months. Conversion to or from a string only needs to be done for input and output.
In practice if they were only dates I would crete my own Date class so could ignore times or use JodaTime which provides easier manipulation than the java Date
In my code I always use the most high level object. In this case I would suggest - Calendar. Here is separate discussion about Date and Calendar. I always think this way - converting Calendar/Date to String is simple - use SimpleDateFormatter. But very often you will need to do something with the date (add several days or hours, subtract a year, handle timezones etc) and then each time you would have to convert it from String to Calendar/Date.
Date if you had to, but java.util.Calendar would probably be more appropriate nowadays. With a String you'd have to worry about format like #jzd mentioned. With Calendar, you can easily switch between formats. Also Calendar lets you get at the date with Calendar.getTime()

How to parse four digit year only (with Joda Time)?

Is there a way to force Joda time to parse dates only when they contain four digit years? For example:
2009-11-11 - should parse
09-11-11 - should not parse
Tried the following code:
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
DateTimeFormatter formatter = builder.appendYear(4, 4).appendLiteral('-').appendMonthOfYear(1).appendLiteral('-').appendDayOfMonth(1).toFormatter();
formatter.parseDateTime("09-11-11");
Parses into 0009-11-11. Apparently minDigits in the method appendYear are only used for formatting when printing out the date.
The result is the same if I use appendYearOfEra(). If I use appendYearOfCentury(), it parses the year into 1909 instead.
We are implementing a general data parser, which will recognize various types of inputs. Also the example is a shortened form of the real deal (for simplicity). Real life scenarios parses dates which can have weekdays, months as words, time, zone and different characters separating month, day and year. Therefore, writing a RegEx or checking the content/length of the string can prove rather difficult.
Some real examples could look like this:
2009-11-11
Wednesday 2009-11-11T15:00:00
2009/11/11 15:00
and many more...
DateTimeFormatterBuilder#appendFixedDecimal() may well do what you need.
Alternatively, you could implement the DateTimeParser interface to create whatever parser you want and pass that into the DateTimeFormatterBuilder.
You can check the length of the date string.
You can build extremely specific parsers and formatters using DateTimeFormatterBuilder. There's generally no need to use this class directly, since most common formats are more easily available elsewhere in the API, but this is the builder class they all use under the covers.
What do you want to get from a user who enters '0001-01-01' as the date (that is, they entered 4 digits for the year, but the first three were zeroes)? What about '0999-12-31'? And '999-12-31'? And what about '10000-01-01' - the infamous Y10K1 problem?
If that is a legitimate value, then you are stuck with discovering the length of what the user typed as the year portion of the date (probably after any other parsing has been done), and making sure it is at least (or is it exactly?) four digits.
If that is not a legitimate value, then you are stuck with checking the year value after the date is parsed.
Or you can take the code and modify it so it includes your preferred definition of valid year.
1 I do not plan to start working on fixing the Y10K problem before 5000-01-02.

Categories

Resources