When parsing dates and times from XML documents into JodaTime objects, I use a common set of conversion utilities, generally using the parsers and formatters supplied by org.joda.time.format.ISODateTimeFormat.
This works fine in the majority of cases, but this time I'm seeing documents with the xs:date value of the format 2010-08-19Z. This is a perfectly valid xs:date value, but none of the standard ISODateTimeFormat-generated LocalDate parsers I've tried will accept it. The closest I can find is ISODateTimeFormat.dateParser(), which will accept the rather odd-looking 2010-08-19TZ, but not 2010-08-19Z (note the T).
This is all quite irritating, since both Joda and XML Schema are supposed to strictly adhere to the ISO date/time formatting rules, but either one of them isn't, or the spec is fuzzy.
Before I admit defeat and hand-roll a custom format, can anyone point me at a ISODateTimeFormat-sourced parser that will read that value?
I believe that according to ISO-8601, 'Z' is part of the time value. The 'T' separator is used only to create combined date/time values. A strict reading of this implies that 'Z' cannot appear after a date without a time value, which can be empty, thus the "odd-looking" 2010-08-18TZ.
This appears to be a slight impedance mismatch between the definitions of xs:date and ISO-8601.
EDIT: I found a copy of ISO 8601. It does not define the concept of a "time-zoned date" such as is defined in the XML Schema Datatypes spec.
Rather than writing one from scratch, how about a simple wrapper that converts xs:date instances with timezones into corresponding ISO-8601 values (i.e. just insert the 'T') and then use the existing ISODateTimeFormat.dateParser()?
Related
I'm having trouble deciding what data type to use to store dates. I think using the Date type will make it easier for the code to be processed by the backend because there are many functions that can be used. But I'm having trouble when it's used for the response API. Each property of data type Date will return a value in the form of a timestamp. Of course this will require effort for the frontend developer to convert it to the actual date.
I've tried several Rest APIs from well-known vendors out there and then I found they use the String data type to process their Rest API requests/responses. Is using the String data type proper to use on date and time?
REST doesn't have a recommended date format, you should select what works best for your end-user and your system.
Generally, people prefer to use ISO 8601 standard for the date-time values in text. The java.time classes use ISO 8601 formats by default when parsing/generating text.
RFC 3339 Date and Time on the Internet is the document to look at that says:
date and time format for use in Internet protocols that is a profile
of the ISO 8601 standard for representation of dates and times using
the Gregorian calendar.
In your case, you are using epoch for a date which is totally fine and easy to convert to an actual human-readable date using java libraries.
A simple solution would be:
LocalDate ld = Instant.ofEpochMilli(epoch)
.atZone(ZoneId.systemDefault()).toLocalDate();
It all boils down to arbitrary personal preference. I would personally use string since it is easier to read before sending, in transit, and upon receipt.
I have a scenario in a current java program where if the user enters a certain date (01/01/1900) in an excel Date field I'm supposed to flag that and clear the value out in the database. In this case since the field is a Date type, I need to pass it to an xml as "" i.e and empty String since I can't just pass null. I'm getting an error because it is expecting a format of "yyyy-MM-dd" to pass to the xml and not "". Of course when setting the Date field to a certain value, I can't just pass an empty String in the setVesselDate() method because it will complain saying it must be in so so format. I have a method that validates the date (handleValidateDate) that returns the sql Date value.
Any recommendation to actually set the date field as "" would be helpful. Below is the code I have:
if (vesselAssignmentObj.getVesselDate().length() > 0)
{
String clearOutDateFlag = "1900-01-01";
String clearField = "";
//below returns a dateformat of yyyy-MM-dd at i
vesselDate = handleValidateDate(vesselAssignmentObj.getVesselDate(), VESSEL_ASSIGNMENTS_VESSEL, i);
if (vesselDate.toString().equals(clearOutDateFlag))
{
vesselAssignmentObj.setVesselDate(String.valueOf(vesselDate.toString().equals(clearField)));
}
else
{
vesselAssignmentObj.setVesselDate(String.valueOf(vesselDate).toString());
}
}
No easy answer.
… I'm supposed to flag that and clear the value out in the database…
Whoever instructed you should have defined what "clear the value out" means.
NULL
Usually in a database "clear the value out" might mean using a NULL value. But nulls bring a bag of hurt, raising ambiguity as to the meaning, affecting sorting and queries in various ways, and carrying ramifications in your app(s) and libraries.
While I generally avoid NULL like the plague (taking Dr. Chris Date’s advice), this may in fact be the best fit for your needs.
Arbitrary date as flag
You could choose an arbitrary date to use as flag that means 'empty' or 'not specified' or 'unknown'. But what date? One commenter suggested 0000-00-00 but this value may exceed the limits of some database’s date data-types. Furthermore, dates around year zero can be problematic because of how different calendaring systems handle the meaning of that period. Furthermore, any date before the adoption of the Gregorian calendar in the West is problematic as we lost some days in transitioning from Julian calendar to Gregorian.
1901-01-01
In a comment you said that a business rules requires dates to be over the year 1900. In that case I would choose 1901-01-01 as my flag date. The trick is making this clear to the entire team and to posterity. Be sure to use well-named constant in your programming, and document well in both your apps and in the database.
1970-01-01
Another common choice of arbitrary date flag is the first of 1970, 1970-01-01. That date is a commonly used epoch reference date used in Unix time and in java.time. This makes that particular date recognizable to many folks in the information trade. But you still need to document thoroughly, as this epoch date is far from the only one: At least a couple dozen other epoch dates have been in widespread usage.
Millenium
Yet another choice could be the date of the new millennium. Unfortunately, that phrase means different dates to different people, in year 2000 or 2001.
The Postgres database uses 2000-01-01 as its epoch. The Apple Cocoa framework, possibly the most widely distributed software in the world, uses 2001-01-01.
Use objects, not strings
You should not be passing strings to/from your database. Use objects instead. The job of your JDBC driver is to mediate between your Java data types and your database types.
Drivers compliant with JDBC 4.2 and later should be able to use java.time types via PreparedStatement::setObject and ResultSet::getObject. If not, fall back to using the java.sql types briefly and immediately convert to/from the java.time types. To convert, look to new methods added to the old classes.
All this JDBC date-time stuff has been covered many times already, so search Stack Overflow for more information. Search for class names Instant, ZonedDateTime, and ZoneId.
Using the joda-time-2.0 version library, I was wondering, which of this functions is better to construct from an ISO date (suposed XML xs:dateTime format): new DateTime(String) versus DateTime.parse(String).
Because both return a different result from same value. Example:
new DateTime("2012-08-16T07:22:05Z")
DateTime.parse("2012-08-16T07:22:05Z")
Resulting different because of the ISOChronology. First says is ISOChronology[Europe/Paris] and second ISOChronology[UTC], although milliseconds are the same.
Also, here recomends to use ISODateTimeFormat.dateTimeNoMillis(), giving the same result as using the first version (new).
The two methods use two different conversion methods: the constructor uses an instance of InstantConverter, which in case of strings is a StringConverter and which doesn't yet support reading the timezone from the passed string, while the parse method uses a DateTimeFormatter which knows how to parse the timezone.
Although both formats in theory accept an ISO datetime format, I consider that the constructor is buggy since it always uses the system timezone instead of the one specified in the string. This is inconsistent with the other possible values accepted by this constructor, which do take into account a chronology with its timezone offset. For example, this constructor will return a DateTime object with the UTC timezone:
new DateTime(DateTime.parse("2012-08-16T07:22:05Z"))
I'm setting the standards for our application.
I've been wondering, what default date format should I choose to use ?
It should be:
Internationalization & timezone aware, the format should be able to represent user local time
Can be efficiently parsed by SimpleDataFormat (or alike, jdk classes only)
Programming Language agnostic (can parse in java, python, god forbid C++ :) and co.)
Preferably ISO based or other accepted standard
Easy to communicate over HTTP (Should such need arises, JSON or YAML or something in this nature)
Can represent time down to seconds resolution (the more precise the better, micro seconds if possible).
Human readable is a plus but not required
Compact is a plus but not required
Thank you,
Maxim.
yyyy-MM-ddThh:mmZ (See ISO 8601) You can add seconds, etc
You can read it easily, it will not be a problem for SimpleDateFormat.
The most canonical and standard form is probably "Unix Time": The number of seconds elapsed since midnight Coordinated Universal Time (UTC) of January 1, 1970.
If you set that as the default time-format you can easily parse it, store it in memory, write it to disk, easily communicate it over HTTP and so on. It is also definitely an accepted standard, and in a sense it is "time-zone aware", since it is well-defined regardless of time-zones.
(This is the format in which I always store all my time stamps; in databases, in memory, on disk, ...)
The "right" default format really depends on what you're doing with it. The formats for parsing, storing, and displaying can all be different.
For storing the date you're (almost) always going to want to use UTC as aioobe says, even when you want to display it in user local time. I say "(almost)" but I really can't think of a case where I would not want UTC for a saved date. You may want to store the TZ information for where the date originated also, so you can report it in that local time, but more often you want to display the local time for the whoever is currently looking at the date. That means having a way to determine the current user's local time regardless of what the original local time was.
For displaying it, the "default format" should usually be determined by the viewers locale. 08/09/10 usually means 2010-Aug-9 in the U.S. ("Middle endian") but normally means 2010-Sep-8 in most of the rest of the world ("Little endian"). The ISO-8601 format "2010-09-10" is safe and unambiguous but often not what people expect to see. You can also look over RFC-3339 for Date and Time on the internet and RFC-2822 for message format (transmitting the date)
For parsing a date, you'll want to parse it and convert it to UTC, but you should be fairly flexible on what you accept. Again, the end users Locale and timezone, if discoverable, can help you determine what format(s) of string to accept as input. This is assuming user-typed strings. If you're generating a date/time stamp you can control the form and parsing will be no problem.
I also second BalusC link which I hadn't seen before and have now favorited.
I am defining a schema for my web service that will be accessed from multiple countries. I am wondering which of the below 2 should be used (both are valid according to xsd dateTime type and ISO 8601) and which one of them is WS-I compliant?
UTC format like 14:15Z or 14:15:00Z. The appended Z letter indicates that the time is represented in UTC.
Alternatively, use a local time with explicit zone designation in one of the formats [+/-]hh:mm. Example: 12:15+02:00
It is somewhat subjective - both are OK. I prefer UTC. You likely need to convert the time to client local anyway (and you should rely on information from the client to do so, since the user may login from different timezones). When storing in UTC, you have to worry less about the details of how storage is taking place, since all times are represented in the same timezone and it is much easier to compare (and thus sort).
It depends on the use case. Sometimes it's useful to know the timezone the client is in. If the user enters a time of 13:00 in their timezone, they probably still want to see 13:00 when the retrieve the date.
Note, I'm not saying you store the time in local (which would be very bad of course), just that you may want to maintain the timezone.
Both forms compy with WS-I Basic Profile, as they are both valid lexical formats for xsd:dateTime.
Normally, a service description would specify xsd:dateTime in the schema and would not typically constrain the lexical format further. In this case the service implementation should be prepared to handle any valid xsd:dateTime value i.e. should be able to cope with either form in data received from clients.
If you really want to, you could restrict the allowed lexical formats in the schema for your service description, by defining a custom type based on xsd:dateTime with an additional pattern facet. This would still be WS-I Basic profile compliant, I believe, but I would avoid doing this unless you have a very compelling reason. In my experience custom types based on XSD types with added pattern facets do not always play nicely with all XML toolsets, so you may create problems for clients by adding additional constraints beyond xsd:dateTime.