3

I was simply trying to convert the instance of XMLGregorianCalendar (which I got from JAXWS) to GregorianCalendar in specific TimeZone using below code.

The date is coming in EST and I want to convert it into GMT for further saving to DB

//soap response <ns4:TimeStamp>2016-06-18T04:43:54-04:00</ns4:TimeStamp>
//dtime is what i got from JAXB for the above date, so I wrote::
Date date = dTime.toGregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US, null).getTime();
System.out.println(date);

Output: Sat Jun 18 14:13:54 IST 2016

Since above is not working as expected so i tried DateFormat and its giving the expected result.

    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    GregorianCalendar gc =  dTime.toGregorianCalendar();
    System.out.println(df.format(gc.getTime()));

Output: 2016-06-18 08:43:54 +0000

What could be the issue here as toGregorianCalendar(...) is not giving the desired result?

Also I noticed the GregorianCalendar instance obtained above from toGregorianCalendar has fieldSet= false. Not sure if this is causing the issue.

java.util.GregorianCalendar[time=1468382241000,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=6,WEEK_OF_YEAR=29,WEEK_OF_MONTH=3,DAY_OF_MONTH=12,DAY_OF_YEAR=194,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=57,SECOND=21,MILLISECOND=0,ZONE_OFFSET=-14400000,DST_OFFSET=0]

Any help will be appreciated..!!

bbajaj
  • 83
  • 2
  • 8
  • How can the output be `05:27:21` if the input is `04:43:54`? Time zone changes will not affect the seconds. – Andreas Aug 31 '16 at 20:50
  • sorry I mistakenly added the wrong outputs. Corrected now. – bbajaj Sep 01 '16 at 11:21
  • 1
    FYI, the terribly troublesome date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Jul 25 '19 at 04:27

4 Answers4

2

tl;dr

myPreparedStatement
.setObject(                     // Exchange java.time objects with your database in JDBC 4.2 and later.
    … ,                         // Specify which `?` placeholder in your SQL statement.
    myXMLGregorianCalendar      // A legacy class. Better to use *java.time* whenever possible.
    .toGregorianCalendar()      // Convert from the one legacy class to another, as a bridge towards the modern `ZonedDateTime` class.
    .toZonedDateTime()          // Convert to the modern class.
    .toInstant()                // Adjust from a time zone to UTC. Same moment, same point on the timeline, different wall-clock time.
    .atOffset(                  // Adjust from basic `Instant` class to the more flexible `OffsetDateTime` class, if your JDBC driver does not offer the optional support for `Instant`. 
        ZoneOffset.UTC          // Specify UTC using this constant.
    )                           // Returns a `OffsetDateTime` object.
)

java.time

You are using terrible old date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310.

convert the instance of XMLGregorianCalendar (which I got from JAXWS) to GregorianCalendar

First call XMLGregorianCalendar::toGregorianCalendar().

GregorianCalendar gc = myXMLGregorianCalendar.toGregorianCalendar() ;

Convert from the legacy class GregorianCalendar to the modern java.time.ZonedDateTime. Call the new conversion method added to the old class.

ZonedDateTime zdt = gc.toZonedDateTime() ;

date is coming in EST and I want to convert it into GMT

The java.time.Instant class represents a moment in UTC, always in UTC. You can extract an Instant from the ZonedDateTime. Same moment, different wall-clock time.

Instant instant = zdt.toInstant() ;

for further saving to DB

You may be able to pass an Instant to the database if your JDBC driver supports that. The Instant type is optional in JDBC 4.2.

myPreparedStatement.setObject( … , instant ) ;

If not supported, use OffsetDateTime as support is required by JDBC 4.2.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;

Retrieval.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

The date is coming in EST

EST is not a real time zone.

Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

Remember that Java Date objects don't have a time zone. They are internally in UTC. Time zone only manifests when printed (formatted).

This simple code works:

XMLGregorianCalendar xmlCal = XMLGregorianCalendarImpl.parse("2016-06-18T04:43:54-04:00");
GregorianCalendar cal = xmlCal.toGregorianCalendar();
java.util.Date date = cal.getTime();

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
format.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(format.format(date)); // prints: 2016-06-18 08:43:54

Since your goal is to save it to a database, you really don't care about formatting it to text. Assuming you're using JDBC (not some NoSQL), you need a Timestamp.

XMLGregorianCalendar xmlCal = XMLGregorianCalendarImpl.parse("2016-06-18T04:43:54-04:00");
GregorianCalendar cal = xmlCal.toGregorianCalendar();
java.sql.Timestamp date = new java.sql.Timestamp(cal.getTimeInMillis());

Now you can give that to a PreparedStatement using setTimestamp().

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • using this `xmlCal.toGregorianCalendar().getTimeInMillis()` will not solve the purpose as this is using the default server timezone. Whereas I want to have the date saved into GMT. So this is what I used. `xmlCal.toGregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US, null).getTime()` but this is not working. – bbajaj Sep 01 '16 at 10:27
  • @bbajaj `xmlCal.toGregorianCalendar().getTimeInMillis()` does **not** use the default server timezone. The `GregorianCalendar` will inherit the time zone from the `XMLGregorianCalendar`, which uses the time zone in the input string (`-04:00`). The `Timestamp` value doesn't have a time zone. It's internal value is always in UTC. Calling `date.toString()` will *render* the date in the default time zone, but that's for *rendering only*. The *value* of `date` is milliseconds since Epoch UTC (aka 1/1/1970). – Andreas Sep 01 '16 at 15:51
1

I'm using the following code to convert an XMLGregorianCalendar from a given source with a given timezone (e.g. GMT+0) to a GregorianCalendar with the current system timezone :

GregorianCalendar.from(xmlGregorianCalendar
    .toGregorianCalendar()
    .toZonedDateTime()
    .withZoneSameInstant(ZoneId.systemDefault()))

When you use only XMLGregorianCalendar.toGregorianCalendar(), the returned GregorianCalendar has the same timezone as the source system, not the timezone of the current system. Converting the timezone when you receive the data is safer: this way, you reduce the number of potential issues in your code because all dates are based on the system timezone.

For example:

XMLGregorianCalendar xmlGregorianCalendarFromSource = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar("1983-09-30T23:00:00.000Z"); // the source system is on GMT+0
GregorianCalendar gregorianCalendarWithSourceTZ = xmlGregorianCalendarFromSource.toGregorianCalendar();
GregorianCalendar gregorianCalendarWithSystemTZ = GregorianCalendar.from(xmlGregorianCalendarFromSource 
    .toGregorianCalendar()
    .toZonedDateTime()
    .withZoneSameInstant(ZoneId.systemDefault()));
System.out.println(xmlGregorianCalendarFromSource); // displays "1983-09-30T23:00:00.000Z" (i.e. GMT+0)
System.out.println(gregorianCalendarWithSourceTZ.toZonedDateTime()); // displays "1983-09-30T23:00Z[GMT]" (i.e. GMT+0)
System.out.println(gregorianCalendarWithSystemTZ.toZonedDateTime()); // displays "1983-10-01T00:00+01:00[Europe/Berlin]" (my system timezone is GMT+1 or GMT+2 depending on daylight saving)
Julien Kronegg
  • 4,968
  • 1
  • 47
  • 60
0

As another route, you could consider using Date's instead of XMLGregorianCalendars in your code by using jaxb bindings as discussed in this stack overflow question.

Community
  • 1
  • 1
Koos Gadellaa
  • 1,220
  • 7
  • 17