¿Cómo manejar las zonas horarias del calendario usando Java?

Resuelto Jorge Valois asked hace 16 años • 9 respuestas

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]

Jorge Valois avatar Oct 23 '08 22:10 Jorge Valois
Aceptado
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 Dateen 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.

matt b avatar Oct 23 '2008 16:10 matt b

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)

Jorge Valois avatar Oct 23 '2008 18:10 Jorge Valois

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());
Henrik Aasted Sørensen avatar Feb 02 '2009 12:02 Henrik Aasted Sørensen