XMLGregorianCalendar to java.sql.Timestamp
Asked Answered
A

2

0

I'm working with webservices, inserting records, that return a timestamp value in XMLGregorianCalendar type. I need to transform it in a java.sql.Timestamp value, so I use a function like this.

public static java.sql.Timestamp getSqlTimeStamp(XMLGregorianCalendar xgc) {
    if (xgc == null) {
        return null;
    } else {
        return new Timestamp(xgc.toGregorianCalendar().getTime().getTime());
    }
}
Timestamp timestamp=getSqlTimeStamp(ExitInXMLGregor.getTimestamp());

My problem is that in the server, the timestamp value when I insert a record, looks like this: 2012-10-03T19:23:22.342+02:00

But when I make my type conversion, I obtain the timestamp value like this: 2012-10-03T17:23:22.342

The time in the server (where the webservice is located) is 2h more than my locale, and for some reason, I obtain my insert locale time, after transform it. The problem is that I really need to obtain the server time, cause in the DB, the timestamp value matches with the server one, and I'm having problems in the update operation, due to the different values of timestamp.

Please, I would appreciate any kind of help. thanks!

Edit: I kind of find a solution, but is not exactly what I need. When I convert my timestamp in java.sql.Timestamp format into XMLGregorian I setting the timeZone of the server (setTimeZone(TimeZone.getTimeZone("GMT+02:00"))). This actually works, but is far away from the ideal solution (It could happen that the timezone or even the server change) It would be great to know in this point the timeZone of the server dinamically, but I don't know how...

public static XMLGregorianCalendar getXMLGregorianCalendar(Timestamp timestamp)
        throws BaseException {
    try {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
        gc.setTimeInMillis(timestamp.getTime());
        return DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
    } catch (DatatypeConfigurationException ex) {
        throw new BaseException(ex);
    }
}
Anesthetize answered 3/10, 2012 at 16:20 Comment(3)
Use the other toGregorianCalendar method where you specify the time zone, the locale, and other XMLGregorianCalendar defaults.Conglutinate
The problem is, how do I know the timezone and locale of the server. What about if the server change it timezone or location..?Anesthetize
You can determine the time zone of the server by asking the server for the current time stamp. My guess is that the server is 2 timezones to the west from you, based on the dates in your question. I don't know if this helps you, but time stamps on servers should always be stored using the Greenwich Mean Time (GMT) timezone, for this very reason.Conglutinate
L
1

I suspect the timestamp does specify the correct time, just doesn't display with the right time zone. 2012-10-03T17:23:22.342 is the same as 2012-10-03T19:23:22.342+02:00, assuming the (hidden) time zone of the former is +00:00.

Leaden answered 3/10, 2012 at 19:41 Comment(5)
I'm pretty sure about that as well, but the problem is that this time (2012-10-03T17:23:22.342) value doesn't match with the database timestamp (2012-10-03T19:23:22.342). And the thing is... how do I fix it?Anesthetize
A java.sql.Timestamp represents an instant in time. It effectively doesn't know about time zones at all. The fact that its default String display is in the wrong time zone won't affect many uses of it. You mention "problems in the update operation". What exactly goes wrong? Can you post the code using the timestamp which doesn't behave as desired?Leaden
The problem is easy to understand. When I insert something, the webservice return me the id of the new record and the Timestamp (in XMLGregorianCalendar type). After that, I try to update the same record, so I need to pass the bean with the Id and timestamp, but obviusly the timestamp I have and the one in the BD doesn't match.Anesthetize
But... they do match. The timestamp you have (2012-10-03T17:23:22.342+00:00) is the same instant in time as the timestamp in the record (2012-10-03T19:23:22.342+02:00). Is it not the case that the server treats 2012-10-03T17:23:22.342+00:00 and 2012-10-03T19:23:22.342+02:00 and 2012-10-04T01:23:22.342+07:00 etc etc as identical? If not, and the server can't be fixed, you may want to consider having your client store the "timestamp" as a string instead of as an instant-in-time.Leaden
You were right again. I was having other problems, and thought that the timestamp was the cause, but it wasn't. Thanks a lot, and excuse me for the inconvenience.Anesthetize
F
1

tl;dr

  • Both strings represent the same moment, simply adjusted to a time zone while failing to note the offset-from-UTC.
  • Avoid such confusion by always including the offset and zone info in such strings.
  • Use modern java.time classes that supplant the troublesome legacy classes. (Instant, not Timestamp)

Example code.

myPreparedStatement.setObject( 
    … , 
    myXMLGregorianCalendar  // If forced to work with a `javax.xml.datatype.XMLGregorianCalendar` object rather than a modern java.time class…
    .toGregorianCalendar()  // …convert to a `java.util.GregorianCalendar`, and then…
    .toZonedDateTime()      // …convert to modern `java.time.ZonedDateTime` class.
    .toInstant()            // Adjust to UTC by extracting an `Instant` object.
)

Retrieving from a database, as of JDBC 4.2 and later.

Instant instant = myResultSet.getObject( … , Instant.class ) ;

java.time

As the correct accepted Answer by Robert Tupelo-Schneck says, it seems all is well in that both strings represent the same moment but adjusted into a different time zone with a different offset-from-UTC. The problem is that one of those two strings lacks an indicator of its offset-from-UTC.

Such an omission is a bad practice as it creates this confusion. Always include the offset-from-UTC unless absolutely certain the context makes the offset/zone clear.

Work in UTC

Working in UTC avoids this kind of confusion. Generally best to work, store, and exchange date-time values in UTC. Adjust from UTC to a time zone only for presentation to the user or where required by business logic.

Also, you are using terribly troublesome old classes that are now supplanted by the java.time classes. Convert your XMLGregorianCalendar to java.time.ZonedDateTime.

ZonedDateTime zdt = myXMLGregorianCalendar.toGregorianCalendar().toZonedDateTime() ;

Adjust to UTC by extracting an Instant object.

Instant instant = zdt.toInstant() ;

As of JDBC 4.2 and later, you can directly exchange java.time objects with the database. No need to ever use the legacy java.sql.Timestamp class again.

myPreparedStatement.setObject( … , instant ) ;

Retrieval:

Instant instant = myResultSet.getObject( … , Instant.class ) ;

Adjust from UTC to some particular time zone if you want to view the same moment using the wall-clock time used by the people of a particular region.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Convert

If you must interface with some old code requiring a java.sql.Timestamp, you can convert back-and-forth with java.time.Instant. Call new conversion methods added to the old classes.

java.sql.Timestamp ts = java.sql.Timestamp.from( instant ) ;

Going the other direction.

Instant instant = ts.toInstant() ;

See also my Answer to a similar Question.


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?

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.

Foremost answered 7/7, 2018 at 23:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.