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 ;)
Related
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).
I was looking around for something like a convention of how to transfer a date parameter via REST using JSON as body content type. I see some are using long as I was on couple of places where I wrote both client and server side code. I find this approach most convenient.
I want to avoid potential problems when it comes to date formats etc. Is it all up to arrangement between client and server side producers or something can be used as most correct approach?
Depending on your needs, you could use a Unix timestamp since epoch, that is, the number of seconds elapsed since January 1, 1970 (midnight UTC/GMT).
But if you want to use something more readable, consider the ISO 8601 standard, which is endorsed by the RFC 3339 and by the xkcd 1179:
There is a standard for internet date and times: https://www.rfc-editor.org/rfc/rfc3339
ISO 8601 is the canonical format...
I need to get a Date instance from input file. I don't know the date format, but I want to get it from user profile settings.
Te following code does not working:
DateFormat form = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault());
try {
Date t = form.parse("6/6/2015");
}
unparseable date error
I want to know if there is any way to get date from string without knowing the date string pattern.
I need this date to create MySQL query. Maybe there is another way to build this query without parsing date? I am using Entity Beans.
No. Consider the date "1/2/2015": is that February 1st or January 2nd. Depends on your locale.
Instead, you should be more specific: rather than getting a date formatter for your locale, use SimpleDateFormat with an explicit pattern.
I want to know if there is any way to get data from string without knowing the data string pattern.
Without any more information, this is very error prone. For example, consider "7/6/2015" - does that mean June 7th, or July 6th?
If you know the user's locale, you can do a lot better - for example, you could obtain DateFormat instances for long, medium, short and full date patterns for that locale, and try them one at a time. Bear in mind, however, that depending on where this code is executing, the default locale (as you're using at the moment) may not be the user's locale. You mention the user profile settings - hopefully that already contains a locale.
One alternative is to ask the user to tell you what the format is - maybe provide lots of different examples, and let them pick the one that matches.
Finally, if the file has lots of dates in and you're confident they'll all be in the same format, you could try to parse all of them in each of several different formats - that's likely to reduce the chances of error, as "7/6/2015" becomes unambigious if you've also seen "13/1/2015" for example.
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()
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.