2010-05-20 36 views
11

Recibo una fecha y hora de un servicio web SOAP sin información de timzone. Por lo tanto, el deserializador Axis asume UTC. Sin embargo, el horario actual es en la hora de Sydney. He resuelto el problema restando el desplazamiento de zona horaria:Cambiar la zona horaria sin cambiar el tiempo en Java

Calendar trade_date = trade.getTradeDateTime(); 
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney"); 
long millis = trade_date.getTimeInMillis() - est_tz.getRawOffset(); 
trade_date.setTimeZone(est_tz); 
trade_date.setTimeInMillis(millis); 

Sin embargo, no estoy seguro de si esta solución también toma en cuenta el horario de verano. Creo que debería, porque todas las operaciones están en horario UTC. ¿Alguna experiencia con manipular el tiempo en Java? Mejores ideas sobre cómo resolver este problema?

+1

Siempre, siempre, y solo manipule la fecha (en cualquier idioma) usando la única unidad que está arreglada. Esa unidad es la segunda (o milisegundo). Hay minutos con 59 o 61 segundos y hay días con 23 o 25 horas. Hay horas con 60 * 60 +/- 1 segundos, etc. Todas mis fechas se almacenan como (milli) segundos desde la época. Así es como están en la base de datos, así es como están ordenados. El único momento en el que hago una zona horaria/aaaa-mm-dd sea cual sea la conversión es cuando se debe mostrar al usuario (o cuando se utilizan API rotas que no entienden que una fecha se puede expresar en segundos). – SyntaxT3rr0r

+0

@WizardOfOdds. Ese es el consejo sensato, pero OP publicó claramente que recibe la marca de tiempo de un servicio web (posiblemente de terceros) y utilizó una biblioteca de terceros para procesar esa solicitud. Claramente hay algunas cosas fuera de su control directo, y la conversión de la zona horaria a la zona horaria definitivamente no es directa con java.util.Calendar. –

+0

Gracias por sus comentarios. Estoy generando los apéndices del servicio web del archivo WSDL utilizando Axis. Estoy usando las clases generadas para conectarme a un servicio web de terceros. De hecho, creo que deberían transmitir campos de fecha y hora con información de zona horaria o en UTC, que es lo que espera Axis. Pero esa no es mi elección. –

Respuesta

-1

He decidido reparse cadena de fecha y hora recibida con la zona horaria correcta establecida. Este ahorro de energía solar también debe considerar:

public class DateTest { 

    private static SimpleDateFormat soapdatetime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     TimeZone oztz = TimeZone.getTimeZone("Australia/Sydney"); 
     TimeZone gmtz = TimeZone.getTimeZone("GMT"); 
     Calendar datetime = Calendar.getInstance(gmtz); 

     soapdatetime.setTimeZone(gmtz); 
     String soap_datetime = soapdatetime.format(datetime.getTime()); 
     System.out.println(soap_datetime); 

     soapdatetime.setTimeZone(oztz); 
     datetime.setTimeZone(oztz); 
     try { 
      datetime.setTime(
        soapdatetime.parse(soap_datetime) 
      ); 
     } catch (ParseException e) { 
      e.printStackTrace(); 
     } 

     soapdatetime.setTimeZone(gmtz); 
     soap_datetime = soapdatetime.format(datetime.getTime()); 
     System.out.println(soap_datetime); 
    } 
} 
16

Me compadezco del tonto que tiene que hacer las fechas en Java.

Lo que ha hecho casi seguramente saldrá mal en las transiciones de ahorro de luz diurna. La mejor manera de hacerlo es crear un nuevo objeto de calendario, establecer la zona horaria en él y luego establecer todos los campos individualmente, por lo que año, mes, día, hora, minuto, segundo, obtener los valores del objeto de fecha .

Editar:
Mantener el mundo contento, probablemente debería hacer esto:

Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 
Calendar sydneyTime = Calendar.getInstance(TimeZone.getTimeZone("Australia/Sydney"); 
utcTime.setTime(trade_date); 
for (int i = 0; i < Calendar.FIELD_COUNT; i++) { 
    sydneyTime.set(i, utcTime.get(i)); 
} 

Entonces no va a utilizar cualquiera de los métodos obsoletos.

+0

-1: por sugerir el uso de métodos obsoletos de la clase java.util.Date –

+0

probablemente: 'newCalendar.set (oldCalendar.get (Calender.YEAR), oldCalendar.get (Calendar.MONTH), ..., oldCalendar.get (Calendar.SECOND)); ' – wds

+1

@Alexander Pogrebnyak: He actualizado la respuesta para que los usuarios no sean llevados al lado oscuro ;-) –

3

Sin embargo, no estoy seguro de si esta solución también tiene horario de verano en cuenta. Creo que debería, porque todas las operaciones están en hora UTC.

Sí, debe tener en cuenta el horario de verano, ya que afecta el desplazamiento a UTC.

¿Alguna experiencia con la manipulación del tiempo en Java? Mejores ideas sobre cómo resolver este problema?

Joda-Time es una API de mejor tiempo. Tal vez el siguiente fragmento podría ser de ayuda:

DateTimeZone zone; // TODO : get zone 
DateTime fixedTimestamp = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, zone); 

tipos JodaTime son inmutables, que es también un beneficio.

+0

Gracias por el enlace a JodaTime. Sin embargo, no presentaré una nueva biblioteca de terceros para ese proyecto. Pero consideraré a JodaTime como la próxima vez. –

+0

Joda-Time bien vale la pena el problema de agregar una biblioteca de terceros. Bien usado y probado. Las clases java.util.Date y Calendar son notoriamente problemáticas. –

2

normalmente lo hago de esta manera

Calendar trade_date_utc = trade.getTradeDateTime(); 
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney"); 
Calendar trade_date = Calendar.GetInstance(est_tz); 
trade_date.setTimeInMillis(millis); 
4

Quiero dar las gracias a la persona para registrar el funcionamiento 6. Este fue un gran comienzo para mí y un enfoque que no lo hice considerar. Se requieren algunos pasos adicionales para llevarlo al nivel de código de producción. En particular, observe los pasos requeridos para DST_OFFSET y ZONE_OFFSET. Quiero compartir la solución que se me ocurrió.

Esto toma el tiempo del objeto de calendario de entrada, lo copia al tiempo de salida, establece la nueva zona horaria a la salida. Esto se usa cuando se toma tiempo literalmente de la base de datos y se configura la zona horaria sin cambiar la hora.

public static Calendar setNewTimeZoneCopyOldTime(Calendar inputTime, 
     TimeZone timeZone) { 
    if((inputTime == null) || (timeZone == null)) { return(null); } 

    Calendar outputTime = Calendar.getInstance(timeZone); 
    for(int i = 0; i < Calendar.FIELD_COUNT; i++) { 
     if((i != Calendar.ZONE_OFFSET) && (i != Calendar.DST_OFFSET)) { 
      outputTime.set(i, inputTime.get(i)); 
     } 
    } 

    return((Calendar) outputTime.clone()); 
} 
0

¿Está obteniendo una cadena de estilo ISO 8601 de ese Servicio Web desordenado? Si es así, la biblioteca de Joda-Time 2.3 lo hace muy fácil.

Si obtiene una cadena ISO 8601 sin ningún desplazamiento de zona horaria, pasa un objeto de zona horaria al constructor DateTime.

DateTimeZone timeZone = DateTimeZone.forID("Australia/Sydney"); 
String input = "2014-01-02T03:00:00"; // Note the lack of time zone offset at end. 
DateTime dateTime = new DateTime(input, timeZone); 

volcado a la consola ...

System.out.println("dateTime: " + dateTime); 

Cuando ejecute ...

dateTime: 2014-01-02T03:00:00.000+11:00 
0
@Test 
public void tzTest() { 
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z"); 
    TimeZone tz1 = TimeZone.getTimeZone("Europe/Moscow"); 
    Calendar cal1 = Calendar.getInstance(tz1); 
    long l1 = cal1.getTimeInMillis(); 
    df.setTimeZone(tz1); 
    System.out.println(df.format(cal1.getTime())); 
    System.out.println(l1); 
    TimeZone tz2 = TimeZone.getTimeZone("Africa/Douala"); 
    Calendar cal2 = Calendar.getInstance(tz2); 
    long l2 = l1 + tz1.getRawOffset() - tz2.getRawOffset(); 
    cal2.setTimeInMillis(l2); 
    df.setTimeZone(tz2); 
    System.out.println(df.format(cal2.getTime())); 
    System.out.println(l2); 
    assertNotEquals(l2, l1); 
} 


Correr CalendarTest
30/06/2016 19: 09: 16.522 0.300
30/06/2016 19: 09: 16,522 +0100
Pruebas realizadas: 1, Fallos: 0 Errores: 0, pasadas por alto: 0, Tiempo transcurrido: 0,137 seg

Cuestiones relacionadas