90 votos

Cómo manejar el calendario, zonas horarias, utilizando Java?

Tengo un valor de marca de hora que viene de mi solicitud. El usuario puede en cualquier zona Horaria.

Desde esta fecha se utiliza para un WebService que asume el momento en que se da siempre es GMT tengo necesidad de convertir el usuario el parámetro de decir (EST) a (GMT). Aquí viene lo bueno: El usuario es totalmente ajeno a su TZ. Entra a la fecha de creación, que él quiere enviar a los WS, así que lo que necesito es:

El usuario ingresa a: 5/1/2008 a las 6:12 PM (EST)
El parámetro para el WS debe ser: 5/1/2008 a las 6:12 PM (GMT)

Sé que hora son siempre deben estar en GMT por defecto, pero cuando se envía el parámetro, aunque he creado mi Calendario a partir de la TS (que se supone es GMT), los horarios son siempre apagado a menos que el usuario es 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 11:12 PM]

60voto

matt b Puntos 73770
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á la salida si me pase de la hora actual ("12:09:05 EDT" desde Calendar.getInstance()):

DEPURACIÓN de entrada de calendario tiene fecha [Jue Oct 23 12:09:05 EDT 2008]
DEPURACIÓN de la compensación es -14400000
DEBUG - Creado GMT cal con fecha [Jue Oct 23 08:09:05 EDT 2008]

12:09:05 GMT es de 8:09:05 EDT.

La parte confusa aquí es que Calendar.getTime() devuelve un Date en su zona horaria actual, y también que no hay ningún método para modificar la zona horaria de un calendario y la fecha subyacente rodó también. Dependiendo de qué tipo de parámetro a su web el servicio de toma, a su sólo puede querer tener el WS oferta en términos de milisegundos de la época.

29voto

Jorge Valois Puntos 501

Gracias a todos por responder. Después de una investigación más a fondo he llegado a la respuesta correcta. Como se ha mencionado por Saltar de la Cabeza, la de sellos de tiempo me estaba poniendo de mi solicitud estaba siendo ajustado a la zona Horaria del usuario. Así que si el Usuario entró en 6:12 PM (EST), me gustaría conseguir 2:12 PM (GMT). Lo que yo necesitaba era una manera de deshacer la conversión para que la hora introducida por el usuario es el momento he enviado a la Web de solicitud. He aquí cómo he logrado 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()));

El código de salida es: (introducidos por el Usuario 5/1/2008 a las 6:12PM (hora del este)

Actual de la zona Horaria del Usuario: EST
Corriente de Desplazamiento de GMT (en hrs):-4 (Normalmente -5, excepto que es horario de verano ajustados)
TS de la ACP: 2008-05-01 14:12:00.0
Fecha de calendario convertidos a partir de TS mediante GMT y US_EN configuración Regional: 5/1/08 6:12 PM (GMT)

20voto

Henrik Puntos 1579

Usted dice que la fecha es usada en conexión con servicios web, así que supongo que se serializa en una cadena en algún momento.

Si este es el caso, usted debe echar un vistazo a la setTimeZone método de la clase DateFormat. Esto nos indica que de la zona horaria que será el utilizado para 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());

12voto

Vitalii Fedorenko Puntos 17469

Se puede resolver con Joda Tiempo:

Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false));
Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));

Java 8:

LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30");
ZonedDateTime fromDateTime = localDateTime.atZone(
    ZoneId.of("America/Toronto"));
ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(
    ZoneId.of("Canada/Newfoundland"));

8voto

Skip Head Puntos 4062

Parece que su marca de tiempo está siendo ajustado a la zona horaria de origen del sistema.

Este es obsoleto, pero debería funcionar:

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

La no-obsoleto es la utilización de

Calendario.get(de Calendario.ZONE_OFFSET) + Calendario.get(de Calendario.DST_OFFSET)) / (60 * 1000)

pero que se tendrían que hacer en el lado del cliente, ya que el sistema sabe qué zona horaria se encuentra.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X