views:

16623

answers:

8

I have a Timestamp value that comes from my application. The user can be in any given local TimeZone.

Since this date is used for a WebService that assumes the time given is always in GMT, I have a need to convert the user's parameter from say (EST) to (GMT). Here's the kicker: The user is oblivious to his TZ. He enters the creation date that he wants to send to the WS, so what I need is:

User enters: 5/1/2008 6:12 PM (EST)
The parameter to the WS needs to be: 5/1/2008 6:12 PM (GMT)

I know TimeStamps are always supposed to be in GMT by default, but when sending the parameter, even though I created my Calendar from the TS (which is supposed to be in GMT), the hours are always off unless the user is in GMT. What am I missing?

Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
...
private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) {
  java.util.Calendar cal = java.util.Calendar.getInstance(
      GMT_TIMEZONE, EN_US_LOCALE);
  cal.setTimeInMillis(ts_.getTime());
  return cal;
}

With the previous Code, this is what I get as a result (Short Format for easy reading):

[May 1, 2008 11:12 PM]

+1  A: 

I don't know the specific answer, but I'll bet that Joda Time can help you. It is a fantastic time/date library and is backed by a JSR. I believe it will make it into Java 7 (though not sure).

Michael Easter
+2  A: 

Something that has worked for me in the past was to determine the offset (in milliseconds) between the user's timezone and GMT. Once you have the offset, you can simply add/subtract (depending on which way the conversion is going) to get the appropriate time in either timezone. I would usually accomplish this by setting the milliseconds field of a Calendar object, but I'm sure you could easily apply it to a timestamp object. Here's the code I use to get the offset

int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();

timezoneId is the id of the user's timezone (such as EST).

Adam
Using the raw offset you are ignoring DST.
deepc
+2  A: 

It looks like your TimeStamp is being set to the timezone of the originating system.

This is deprecated, but it should work:

cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset());

The non-deprecated way is to use

Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)

but that would need to be done on the client side, since that system knows what timezone it is in.

Skip Head
+4  A: 
public static Calendar convertToGmt(Calendar cal) {

 Date date = cal.getTime();
 TimeZone tz = cal.getTimeZone();

 log.debug("input calendar has date [" + date + "]");

 //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT 
 long msFromEpochGmt = date.getTime();

 //gives you the current offset in ms from GMT at the current date
 int offsetFromUTC = tz.getOffset(msFromEpochGmt);
 log.debug("offset is " + offsetFromUTC);

 //create a new calendar in GMT timezone, set to this date and add the offset
 Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
 gmtCal.setTime(date);
 gmtCal.add(Calendar.MILLISECOND, offsetFromUTC);

 log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]");

 return gmtCal;
}

Here's the output if I pass the current time ("12:09:05 EDT" from Calendar.getInstance()) in:

DEBUG - input calendar has date [Thu Oct 23 12:09:05 EDT 2008]
DEBUG - offset is -14400000
DEBUG - Created GMT cal with date [Thu Oct 23 08:09:05 EDT 2008]

12:09:05 GMT is 8:09:05 EDT.

The confusing part here is that Calendar.getTime() returns you a Date in your current timezone, and also that there is no method to modify the timezone of a calendar and have the underlying date rolled also. Depending on what type of parameter your web service takes, your may just want to have the WS deal in terms of milliseconds from epoch.

matt b
+3  A: 

Thank you all for responding. After a further investigation I got to the right answer. As mentioned by Skip Head, the TimeStamped I was getting from my application was being adjusted to the user's TimeZone. So if the User entered 6:12 PM (EST) I would get 2:12 PM (GMT). What I needed was a way to undo the conversion so that the time entered by the user is the time I sent to the WebServer request. Here's how I accomplished this:

// Get TimeZone of user
TimeZone currentTimeZone = sc_.getTimeZone();
Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE);
// Get the Offset from GMT taking DST into account
int gmtOffset = currentTimeZone.getOffset(
    currentDt.get(Calendar.ERA), 
    currentDt.get(Calendar.YEAR), 
    currentDt.get(Calendar.MONTH), 
    currentDt.get(Calendar.DAY_OF_MONTH), 
    currentDt.get(Calendar.DAY_OF_WEEK), 
    currentDt.get(Calendar.MILLISECOND));
// convert to hours
gmtOffset = gmtOffset / (60*60*1000);
System.out.println("Current User's TimeZone: " + currentTimeZone.getID());
System.out.println("Current Offset from GMT (in hrs):" + gmtOffset);
// Get TS from User Input
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
System.out.println("TS from ACP: " + issuedDate);
// Set TS into Calendar
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
// Adjust for GMT (note the offset negation)
issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset);
System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: "
    + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
    .format(issueDate.getTime()));

The code's output is: (User entered 5/1/2008 6:12PM (EST)

Current User's TimeZone: EST
Current Offset from GMT (in hrs):-4 (Normally -5, except is DST adjusted)
TS from ACP: 2008-05-01 14:12:00.0
Calendar Date converted from TS using GMT and US_EN Locale: 5/1/08 6:12 PM (GMT)

Jorge Valois
+2  A: 

Jorge,

You may want to change that code a little. I notice you are working with integer hours, and some time zones have offsets that are not a whole number of hours from GMT - Dehli, India for example is GMT+5.5 hours.

Skip Head
My favorite is Nepal: UTC+0545 !!!
johnstok
+1  A: 

You say that the date is used in connection with web services, so I assume that is serialized into a string at some point.

If this is the case, you should take a look at the setTimeZone method of the DateFormat class. This dictates which time zone that will be used when printing the time stamp.

A simple example:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

Calendar cal = Calendar.getInstance();
String timestamp = formatter.format(cal.getTime());
Henrik
+1  A: 

Method for converting from one timeZone to other(probably it works :) ).

/**
 * Adapt calendar to client time zone.
 * @param calendar - adapting calendar
 * @param timeZone - client time zone
 * @return adapt calendar to client time zone
 */
public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) {
    Calendar ret = new GregorianCalendar(timeZone);
    ret.setTimeInMillis(calendar.getTimeInMillis() +
            timeZone.getOffset(calendar.getTimeInMillis()) -
            TimeZone.getDefault().getOffset(calendar.getTimeInMillis()));
    ret.getTime();
    return ret;
}
Helpa