XmlBeans XmlDateTime format without timezone info - java

I'm getting an Xml representation of an XmlObject using the xmlText() method. The XmlDateTime objects are coming out with timezone offsets at the end of the string which is valid according to XML Schema: dateTime. Is there any way to force the XmlObject to convert to xml with the Zulu formatting?
Getting this: 2002-10-10T12:00:00-05:00
and need this instead: 2002-10-10T17:00:00Z

I was asking about the instantiation of the XmlDateTime object because I ran into a similar issue a while ago. From what I could figure out, the way the XmlDateTime is printed to xml depends on the value of the internal representation, which in turn depended on the setter which was invoke to provide that value. The issue was with the setDate(...) method.
The default implementation of XmlDateTime keeps the value of the datetime internally as an org.apache.xmlbeans.GDate which is built using a GDateBuilder. When you set the date on the XmlDateTime object it eventually passes the value onto a GDateBuilder.
If you look at the source of the setDate() method, the javadoc states that:
Sets the current time and date based on a java.util.Date instance.
The timezone offset used is based on the default TimeZone. (The default TimeZone is consulted to incorporate daylight savings offsets if applicable for the current date as well as the base timezone offset.)
If you wish to normalize the timezone, e.g., to UTC, follow this with a call to normalizeToTimeZone.
Since the XmlDateTime object has a setGDate(...) method you can test the normalize method like this:
XmlDateTime xmlDateTime = XmlDateTime.Factory.newInstance();
xmlDateTime.setStringValue("2002-10-10T12:00:00-05:00");
System.out.println(xmlDateTime.xmlText());
GDateBuilder gdb = new GDateBuilder(xmlDateTime.getDateValue());
gdb.normalize();
xmlDateTime.setGDateValue(gdb.toGDate());
System.out.println(xmlDateTime.xmlText());
For me this printed:
<xml-fragment>2002-10-10T12:00:00-05:00</xml-fragment>
<xml-fragment>2002-10-10T17:00:00Z</xml-fragment>
That was the only way that I could get it to print in UTC.
I hope there is a better way, although sadly I couldn't find it...

Related

XPages ODA issue with getting a date value from a field in Java

I'm using ODA (which is wonderful) in my Java code but I'm having trouble with getting a date value from a field.
If I use:
DateTime lastApprDt = doc.getItemValue("LastApproved", Date.class);
then the parser objects and suggests "change type of lastApprDt to Date"
If I change the code to:
Date lastApprDt = doc.getItemValue("LastApproved", Date.class);
then the parser is happy but when I run the code I get an error:
[ODA::WARNING] Auto-boxing requested a com.ibm.security.util.calendar.BaseCalendar$Date but is returning a org.openntf.domino.impl.DateTime in item LastApproved for document id 992
I'm confused! If doc.getItemValue("LastApproved", Date.class) returns a Date type then why do I get the error?
Make sure that the lastApprDt Date is of type java.util.Date (and not of type com.ibm.security.util.calendar.BaseCalendar.Date).
The first is failing because it's trying to pass a Date object (the output from getItemValue()) into a DateTime object (lastApprDate).
I'm not quite sure why it's choosing to retrieve it as a com.ibm.security.util.calendar.BaseCalendar.Date, I can't see any reference to that class in the ODA code. It's worth checking the import statements in your code to make sure it's not chosen com.ibm.security.util.calendar.BaseCalendar.Date as the relevant Date class it thinks you want to use. I suspect it may have done. If so, change the import statement to use java.util.Date.
The code for autoboxing Dates looks for specific classes and how to convert them. java.util.Date is the most obvious one it's expecting. I recently added java.sql.Date, I believe for the last base 9.0.1 and first FP8 versions. java.util.Calendar is the other one supported. New Java 8 Date classes like LocalDateTime may seem good candidates because they have better timezone handling, but it's not easy to convert the DateTime timezone to a Java timezone and the timezone in a DateTime is readonly, so it wouldn't work for autoboxing back at the moment.
You shouldn't need to pass the full class name as the second parameter, I've got code running that just passes Date.class. That's what makes me suspect that the parser suggestion has guessed at the wrong class you wanted and imported com.ibm.security.util.calendar.BaseCalendar.Date.
If you are sure that the field contains a date value you should be able to get the java.util.Date with
Date lastApprDt = doc.getItemValue("LastApproved").get(0).toJavaDate();

Java time instant formatting discrepancies

The way Java time handles simple things like timestamps leaves me a little taken aback. Maybe I'm doing it wrong.
I want to generate an ISO 8601 timestamp string. The obvious way would be to create an Instance.now() and format it use DateTimeFormatter.ISO_INSTANT to format it. Except that Instance has no format() method, so I can't format it with the "instance" formatter. Imagine that.
So I have to create a ZonedDateTime from the instance. It shouldn't matter which zone I choose, so I choose UTC because that's what DateTimeFormatter.ISO_INSTANT is going to put it in anyway:
Instant.now().atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT)
Now I get back a string, and obviously I want to get it back to a ZonedDateTime instance, because that's what I used to format it with! So I try:
ZonedDateTime.parse(timestamp, DateTimeFormatter.ISO_INSTANT)
It complains at me that there is no time zone information. But I used a ZonedDateTime to generate it --- it's not my fault that it wouldn't let me use an Instance to format it.
Oddly Instance doesn't have a format() method, but it does have a parse() method, which, again oddly, is parsed using DateTimeFormatter.ISO_INSTANT (even though I can't format an Instance using that formatter).
As pointed out in the comments, I can use DateTimeFormatter.ISO_INSTANT.format(Instance.now()) to format an instance directly, so why isn't there an Instance.format(…)?
So just generating and parsing a timestamp seems convoluted and inconsistent. Or am I doing it wrong?
First, you can just DateTimeFormatter.ISO_INSTANT.format(Instant.now()).
Next, I don't see why you expect to parse back what you've formatted. Formatting is not guaranteed to be a lossless operation. If you format a LocalDateTime with just the year, you wouldn't expect to parse it back to LocalDateTime, would you?
And of course you can parse Instant using DateTimeFormatter.ISO_INSTANT. DateTimeFormatter.ISO_INSTANT.parse(text, Instant::from) - this is what Instant.parse(...) does.
There is no need for a format(DateTimeFormatter) method in Instant, because toString() does the job. The toString() method of Instant is implemented as:
return DateTimeFormatter.ISO_INSTANT.format(this);
Thus, all you need to do is call instant.toString().
To round-trip parse this back in, simply use parse():
Instant instant = Instant.parse(text);
Why do you need to respecify the formatter when parsing back the string?
Can't you just do ZonedDateTime.parse(timestamp);?

Why does getTimestamp on a DateTime field return a different result than selecting unix_timestamp?

My table includes a datetime field:
`RunEndTime` datetime DEFAULT NULL,
It's inserted as a UTC timestamp:
statement.setTimestamp(RUN_END_TIME, runStartTime, UTC_CALENDAR);
Where we have
Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
When I use a SQL query to the effect of
SELECT RunEndTime, unix_timestamp(RunEndTime) ...
I get different results for the following:
rs.getTimestamp(1); // 1445423199000
rs.getTimestamp(1, UTC_CALENDAR); // 1445423199000
rs.getLong(2); // 1445408799
Selecting the unix_timestamp gives a different result, and it's the only accurate one. The other two are EDT, which is the location of the client program. How do I correctly use getTimestamp?
A few things to understand -
Firstly, DateTime objects are strings that do not contain timezones. They are not timestamps. When you call unix_timestamp, the function unix_timestamp implicitly takes a timezone and then calculates the seconds since the epoch using the DateTime and the timezone. The timezone for the server is probably set to UTC, but if you use SET SESSION time_zone = '+04:00' for instance, then call unix_timestamp, you will get a different answer. In other words, unix_timestamp does not output the the canonical timestamp associated with the DateTime because there is no canonical timestamp associated with the DateTime. The DateTime is just a timezone-unaware string. This is tangential to the problem but it something I failed to understand until researching it today, so including.
That being said, the problem actually happens deep within the rs.getTimestamp call. It is a bug within MySQL (or JDBC or something). The fix for this is to add useLegacyDatetimeCode=false when connecting to the database.

joda-time: new DateTime(String) vs DateTime.parse(String)

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"))

How can I set the global default date format in Java?

Is there a way to set the default DateFormat class used for parsing strings into dates?
My background: I get exceptions reading date values from JDBC because the date string is not in the expected format.
(text added on 2011-07-22):
Seems I need to precise my question description: I use a foreign, proprietary database together with a proprietary JDBC driver. There is no possibility to know or even change the column type on database side. When I try to read the ResultSet columns via ResultSet.getDate() or ResultSet.getObject() some exception is triggered inside the JDBC driver like "10 Jul 1999 is not a valid date". What I want to achieve is to avoid this internal exception by setting some appropriate global default date format. Maybe I would need to implement some custom Locale first and the install that Locale globally?
There should be totally no need for this.
Dates should be stored in DB as a DATE, DATETIME or TIMESTAMP field, depending on the DB used and the information you'd like to store (e.g. date only or date and time combined), not as a VARCHAR or something. Such a date-specific field stores the value under the covers basically as an integer/long with the epoch time as value.
Assuming that you're using a date+time field type such as DATETIME or TIMESTAMP, then you should be saving it in the DB using PreparedStatement#setTimestamp(). Here's an example, assuming that the date variable is of a java.util.Date type:
preparedStatement.setTimestamp(1, new Timestamp(date.getTime()));
And you should be retrieving them from the DB using ResultSet#getTimestamp() which returns a Timestamp which in turn is a subclass of java.util.Date, so you could just safely upcast it:
Date date = resultSet.getTimestamp("columnname");
As to parsing/formatting the java.util.Date object from/into a human readable String format, this should technically happen in the view side, not in the persistence layer. How exactly to do this in turn depends on the view/UI technology/framework used, such as Swing, JSP, JSF, Struts2, Spring-MVC, etcetera. As it's not clear from your question which one you're using, it's not possible to give a suitable answer. In general, they all use SimpleDateFormat API under the covers. You could even use it in raw form.
You can set your default Locale:
Locale.setDefault(Locale.US);
Alternatively, you can use one of the DateFormat static methods which accepts a Locale to just get the format for the Locale you're interested in. If your date format doesn't match one of the standard ones, you'll need to create your own SimpleDateFormat (based on a pattern) and make sure you always use that one instead of the default one.

Categories