Reputation: 13
My xmlGregorianCalendar value coming as 2020-10-02T13:07:38-06:00 .. I want to pass thisxmlGregorianCalendar value and get the output like 2020-10-02T07:07:38(which reduces 6 hours from the time) any suggestion on this please?
I have used this below method.
return new Timestamp(xmlGregorianCalendar.toGregorianCalendar(TimeZone.getTimeZone("GMT"),null,null).getTimeInMillis())..
But it removing the off set value but not adjusting the time. the output i am getting is 2020-10-02T13:07:38..But i am expecting the 2020-10-02T07:07:38 like this.
Upvotes: 1
Views: 3293
Reputation: 86139
Arvind Kumar Avinash has already nicely explained how the offset works (what XMLGregorianCalendar
misleadingly calls a time zone). He has also already recommended java.time, the modern Java date and time API. I should like to elaborate on this recommendation. Assuming that you thought you wanted a java.sql.Timestamp
for your SQL database, you should not want that. Since JDBC 4.2 and Hibernate 5 we can seamlessly transfer the types of java.time to our SQL databases. Which I recommend that you do instead.
If the datatype in SQL is timestamp with time zone
(recommended for timestamps), transfer an OffsetDateTime
from Java to it. To most database engines timestamp with time zone
really means timestamp in UTC, so for clarity I prefer to pass an OffsetDateTime
that is in UTC too. There are a couple of ways to convert, each with their pros and cons.
1. Convert via GregorianCalendar.
// For demonstration build an XMLGregorianCalenadr equivalent to yours
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
.newXMLGregorianCalendar("2020-10-02T13:07:38-06:00");
// Convert to OffsetDateTime for your SQL database
OffsetDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.toOffsetDateTime()
.withOffsetSameInstant(ZoneOffset.UTC);
// Show what we’ve got
System.out.println(dateTime);
Output is:
2020-10-02T19:07:38Z
Pro: it’s the official conversion.
Or 2. Convert via String.
OffsetDateTime dateTime = OffsetDateTime.parse(xmlGregorianCalendar.toString())
.withOffsetSameInstant(ZoneOffset.UTC);
Pro: it’s short, and I don’t think it gives any surprises. Con: To me it feels like a waste to format into a string and parse back into a date-time object again.
3. Convert via int. You may convert by taking the individual fields out of the XMLGregorianCalendar
and building an OffsetDateTime
from them. It’s getting long-winded, handling fraction of second is a bit complicated, and there’s a risk of accidentally swapping fields, so I don’t recommend it.
How to pass to SQL. Here’s an example of transferring the OffsetDateTime
to SQL.
String sqlString = "insert into your_table(your_timestamp_column) values (?);";
PreparedStatement ps = yourDatabaseConnection.prepareStatement(sqlString);
ps.setObject(1, dateTime);
int rowsInserted = ps.executeUpdate();
If the datatype in SQL is timestamp
without time zone (not recommended for timestamps, but often seen), you need to pass a LocalDateTime
from Java. Again there are a couple of ways to convert. I am showing just one:
LocalDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
I am using the default time zone of the JVM in agreement with what the old-fashioned Timestamp
class did, so the result is time zone dependent. In my time zone (Europe/Copenhagen) currently at UTC offset +02:00, the result is:
2020-10-02T21:07:38
The three-arg XMLGregorianCalendar.toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
that you were using is designed to introduce bugs like the one you observed. It promises to construct a GregorianCalendar
with the specified time zone (if one is given) and with the field values from the XMLGregorianCalendar
(year, month, day, hour, minute, etc.). So the documentation says it quite clearly: if you specify a time zone that does not agree with the offset of the XMLGregorianCalendar
, it gives you a different point in time than the one specified by the XMLGregorianCalendar
. It might occasionally be useful if the XMLGregorianCalendar
hadn’t got any offset and we knew which time zone was intended. But your XMLGregorianCalendar
has got offset, so this method definitely is not what you want to use.
XMLGregorianCalendar.toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
Upvotes: 2
Reputation: 78965
First of all, I recommend you switch from the outdated and error-prone java.util
date-time API to the modern java.time
date-time API. Learn more about the modern date-time API from Trail: Date Time.
The date-time string, 2020-10-02T13:07:38-06:00
tells us that the given date and time has been adjusted with an offset of -06:00 hours from UTC
i.e. the corresponding date-time at UTC
would be 2020-10-02T19:07:38Z
where Z
specifies a Zone-Offset of 00:00
hours.
It means that if you are expecting a date-time of 2020-10-02T07:07:38
, you need to offset the given date-time further by -06:00
hours i.e. it will be at a total of -12:00
hours offset from UTC
.
The following example illustrates this concept:
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
OffsetDateTime odtGiven = OffsetDateTime.parse("2020-10-02T13:07:38-06:00");
System.out.println(odtGiven);
// Date and time at Zone-Offset of 00:00 hours
OffsetDateTime odtUTC = odtGiven.withOffsetSameInstant(ZoneOffset.UTC);
System.out.println(odtUTC);
// Date and time at Zone-Offset of -12:00 hours
OffsetDateTime odtDerived = odtGiven.withOffsetSameInstant(ZoneOffset.of("-12:00"));
System.out.println(odtDerived);
// Get the date-time string in the format with Zone-Offset dropped
String strDateTimeZoneOffsetDropped = odtDerived.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(strDateTimeZoneOffsetDropped);
}
}
Output:
2020-10-02T13:07:38-06:00
2020-10-02T19:07:38Z
2020-10-02T07:07:38-12:00
2020-10-02T07:07:38
Using the legacy API:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class Main {
public static void main(String[] args) throws DatatypeConfigurationException, ParseException {
String givenDateTimeString = "2020-10-02T13:07:38-06:00";
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(givenDateTimeString);
System.out.println(xmlGregorianCalendar);
// Derive the date-time string at Zone-Offset of UTC-12:00 hours
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT-12:00"));
Date date = xmlGregorianCalendar.toGregorianCalendar().getTime();
String derivedDateTimeString = sdf.format(date);
System.out.println(derivedDateTimeString);
}
}
Output:
2020-10-02T13:07:38-06:00
2020-10-02T07:07:38
Upvotes: 2