¿Cómo manejar las zonas horarias del calendario usando Java?
Tengo un valor de marca de tiempo que proviene de mi aplicación. El usuario puede estar en cualquier zona horaria local determinada.
Dado que esta fecha se utiliza para un servicio web que supone que la hora proporcionada siempre está en GMT, necesito convertir el parámetro del usuario de, por ejemplo, (EST) a (GMT). Aquí está el truco: el usuario no se da cuenta de su TZ. Introduce la fecha de creación que quiere enviar al WS, entonces lo que necesito es:
El usuario ingresa: 1/5/2008 6:12 p.m. (EST)
El parámetro para el WS debe ser : 1/5/2008 6:12 p.m. (GMT)
Sé que se supone que los TimeStamps siempre están en GMT de forma predeterminada, pero al enviar el parámetro, aunque creé mi Calendario desde TS (que se supone que está en GMT), las horas siempre están apagadas a menos que el usuario esté en GMT. ¿Qué me estoy perdiendo?
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;
}
Con el Código anterior, esto es lo que obtengo como resultado (Formato corto para facilitar la lectura):
[1 de mayo de 2008 23:12]
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;
}
Aquí está el resultado si paso la hora actual ("12:09:05 EDT" de Calendar.getInstance()
) en:
DEBUG: el calendario de entrada tiene fecha [jueves 23 de octubre a las 12:09:05 EDT de 2008]
DEBUG: el desplazamiento es -14400000
DEBUG: creación de calibración GMT con fecha [jueves 23 de octubre a las 08:09:05 EDT de 2008]
Las 12:09:05 GMT son las 8:09:05 EDT.
La parte confusa aquí es que Calendar.getTime()
le devuelve un Date
en su zona horaria actual, y también que no existe ningún método para modificar la zona horaria de un calendario y cambiar también la fecha subyacente. Dependiendo del tipo de parámetro que tome su servicio web, es posible que desee tener el acuerdo WS en términos de milisegundos desde la época.
Gracias a todos por responder. Después de una mayor investigación llegué a la respuesta correcta. Como mencionó Skip Head, la marca de tiempo que recibía de mi aplicación se estaba ajustando a la zona horaria del usuario. Entonces, si el usuario ingresó las 6:12 p. m. (EST), obtendría las 2:12 p. m. (GMT). Lo que necesitaba era una forma de deshacer la conversión para que la hora ingresada por el usuario sea la hora que envié a la solicitud del servidor web. Así es como logré esto:
// 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()));
La salida del código es: (El usuario ingresó el 1/5/2008 6:12 p.m. (EST)
Zona horaria del usuario actual: EST
Desviación actual de GMT (en horas): -4 (normalmente -5, excepto si se ajusta el horario de verano)
TS de ACP: 2008-05-01 14:12:00.0
Fecha del calendario convertida de TS usando GMT y configuración regional US_EN : 01/05/08 6:12 p. m. (GMT)
Usted dice que la fecha se usa en relación con los servicios web, por lo que supongo que se serializa en una cadena en algún momento.
Si este es el caso, deberías echar un vistazo al método setTimeZone de la clase DateFormat. Esto dicta qué zona horaria se utilizará al imprimir la marca de tiempo.
Un ejemplo sencillo:
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());