views:

862

answers:

2

This sounds like a simple task.
Get UTC timestamp value from DB and pass it as UTC date via Web Service.

We have timestamp column DATE_COLUMN and store there time in UTC time zone.

With JPA we get this time with

@Column(name = "DATE_COLUMN")
private java.sql.Timestamp dateValue;

And as we have to pass this time via Web Service in UTC (Jax-ws 2.0) we have getDate and setDate methods.
We are interested in getDate.

public Calendar getDate()
{
   Calendar calendar = Calendar.getInstance(utcTimeZone);
   calendar.setTimeInMillis(dateValue.getTime());

   return calendar;
}

This doesn't work as you may think it should.
And this is because application's default time zone is not 'UTC'.

Here is an example for clarification.
Value in the DATE_COLUMN equals to "30.11.09 16:34:48,833045000", when I translate it to UTC I get "2009-11-30T14:34:48.833Z".
The difference is 2 hours. And this is because my default time zone is "Europe/Helsinki".

Same problem if you just want to map 'DATE_COLUMN' to Calendar

@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Calendar dateValue;

public Calendar getDate()
{
   calendar.setTimeZone(utcTimeZone);
   return calendar;
}

I don't want to change application's time zone because it doesn't look like the solution.

By now we have only two options.

First. Calculate offset between application's time zone and UTC and add it manually after automatic subtraction in the calendar.setTimeZone.

public Calendar getDate()
{
   Calendar calendar = Calendar.getInstance(utcTimeZone);
   calendar.setTimeInMillis(dateValue.getTime());

   int offset = TimeZone.getDefault().getOffset(dateValue.getTime());

   calendar.add(Calendar.MILLISECOND, offset);

   return calendar;
}

Second. Pass dateValue as Long via Web Service. Which is not bad except that we lose real type of the field in wsdl.

My imaginary solution is

@Column(name = "DATE_COLUMN")
@Temporal(type = TemporalType.TIMESTAMP, timezone = 'UTC')
private Calendar dateValue;

But I tend to think that there is the real one somewhere. And I hope you can point it out.

A: 

If you need the java process to run at the UTC timezone, the easiest way to do so is by adding the following JVM parameter:

-Duser.timezone=UTC
David Rabinowitz
But I don't need. And all things are going on Application Server. I don't want to change its timezone.
Mykola Golubyev
As far as I understand, you want your times in UTC. In this case, why not run the application (and the database) in UTC? I am doing it very successfully for the last 5 years.
David Rabinowitz
Because on the Application server there are a lot of applications. Not only mine.
Mykola Golubyev
And it really looks like hack and not a solution.
Mykola Golubyev
I agree that if you are hosting many applications it is not a solution. However why this is a hack? It is a system setting same as setting the time zone at the OS level.
David Rabinowitz
A: 

We decided to use following solution.
Use Date for retrieving date from database. It is because Date is timezoneless type.

@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Date dateValue;

public Date getDate()
{
   return dateValue;
}

And to send it via WebService in UTC (jax-ws) we created UtcTimestampAdapter to change zone from application's default to UTC in the marshaling phase.

public class UtcTimestampAdapter extends XmlAdapter<XMLGregorianCalendar, Date>
{
   @Override
   public XMLGregorianCalendar marshal(Date date) throws Exception
   {
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTime(date);

      DatatypeFactory dataTypeFactory = DatatypeFactory.newInstance();
      XMLGregorianCalendar xmlCalendar = 
         dataTypeFactory.newXMLGregorianCalendar(calendar);

      //Reset time zone to UTC
      xmlCalendar.setTimezone(0);

      return xmlCalendar;
   }

   @Override
   public Date unmarshal(XMLGregorianCalendar calendar) throws Exception
   {
      return calendar.toGregorianCalendar().getTime();
   }
}

Then to enable this rule to all Datas fields in the module we added package specific setting like so.

@XmlJavaTypeAdapter(value = UtcTimestampAdapter.class, type = Date.class)
@XmlSchemaType(name = "dateTime", type = XMLGregorianCalendar.class)
package com.companyname.modulename;

That's it. Now we have generic solution which encapsulate all logic in one place. And if we want to send timezoneless dates as UTC via web service in some other module we will just annotate certain package.

Mykola Golubyev