Convertir fecha/hora para una zona horaria determinada - java

Resuelto travega asked hace 13 años • 15 respuestas

Quiero convertir esta marca de tiempo GMT a GMT+13:

2011-10-06 03:35:05

He probado alrededor de 100 combinaciones diferentes de DateFormat, TimeZone, Date, GregorianCalendar, etc. para intentar realizar esta tarea MUY básica.

Este código hace lo que quiero para el TIEMPO ACTUAL:

Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));

DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");    
formatter.setTimeZone(TimeZone.getTimeZone("GMT+13"));  

String newZealandTime = formatter.format(calendar.getTime());

Pero lo que quiero es configurar la hora en lugar de usar la hora actual.

Descubrí que cada vez que intento configurar la hora de esta manera:

calendar.setTime(new Date(1317816735000L));

Se utiliza la zona horaria de la máquina local. ¿Porqué es eso? Sé que cuando "nueva fecha ()" devuelve la hora UTC+0, entonces, ¿por qué cuando configura la hora en milisegundos ya no supone que la hora está en UTC?

Es posible que:

  1. Establecer la hora en un objeto (Calendario/Fecha/Marca de hora)
  2. (Posiblemente) Establezca la zona horaria de la marca de tiempo inicial (calendar.setTimeZone(...))
  3. Formatee la marca de tiempo con una nueva zona horaria (formatter.setTimeZone(...)))
  4. Devuelve una cadena con la nueva zona horaria. (formateador.formato(calendario.getTime()))
travega avatar Oct 06 '11 11:10 travega
Aceptado

Para mí, la forma más sencilla de hacerlo es:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

//Here you say to java the initial timezone. This is the secret
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
//Will print in UTC
System.out.println(sdf.format(calendar.getTime()));    

//Here you set to your timezone
sdf.setTimeZone(TimeZone.getDefault());
//Will print on your default Timezone
System.out.println(sdf.format(calendar.getTime()));
Charleston avatar Mar 12 '2014 21:03 Charleston

Comprender cómo funciona el tiempo en la computadora es muy importante. Dicho esto, estoy de acuerdo en que si se crea una API para ayudarlo a procesar el tiempo de la computadora como si fuera tiempo real, entonces debería funcionar de tal manera que le permita tratarlo como si fuera tiempo real. En su mayor parte, este es el caso, pero hay algunos descuidos importantes que requieren atención.

De todos modos estoy divagando!! Si tiene su compensación UTC (es mejor trabajar en UTC que en GMT), puede calcular el tiempo en milisegundos y agregarlo a su marca de tiempo. Tenga en cuenta que una marca de tiempo SQL puede variar de una marca de tiempo Java, ya que la forma en que se calcula el tiempo transcurrido desde la época no siempre es la misma, dependiendo de las tecnologías de bases de datos y también de los sistemas operativos.

Le recomendaría que utilice System.currentTimeMillis() como marcas de tiempo, ya que se pueden procesar de manera más consistente en Java sin preocuparse por convertir marcas de tiempo SQL en objetos de fecha de Java, etc.

Para calcular su compensación, puede intentar algo como esto:

Long gmtTime =1317951113613L; // 2.32pm NZDT
Long timezoneAlteredTime = 0L;

if (offset != 0L) {
    int multiplier = (offset*60)*(60*1000);
    timezoneAlteredTime = gmtTime + multiplier;
} else {
    timezoneAlteredTime = gmtTime;
}

Calendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(timezoneAlteredTime);

DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");

formatter.setCalendar(calendar);
formatter.setTimeZone(TimeZone.getTimeZone(timeZone));

String newZealandTime = formatter.format(calendar.getTime());

¡Espero que esto sea útil!

 avatar Oct 09 '2011 20:10

tl; dr

Instant.ofEpochMilli( 1_317_816_735_000L )
    .atZone( ZoneId.of( "Pacific/Auckland" ) )
    .format( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( new Locale( "en" , "NZ" ) ) )

…también…

LocalDateTime.parse( "2011-10-06 03:35:05".replace( " " , "T" ) )
    .atZone( ZoneId.of( "Pacific/Auckland" ) )

java.tiempo

La pregunta y la mayoría de las respuestas utilizan clases de fecha y hora heredadas y obsoletas de las primeras versiones de Java. Estas viejas clases han demostrado ser problemáticas y confusas. Evítales. En su lugar, utilice las clases java.time.

ISO 8601

Su cadena de entrada está casi en formato estándar ISO 8601. Simplemente reemplace el ESPACIO en el medio con un T.

String input = "2011-10-06 03:35:05".replace( " " , "T" );

LocalDateTime

Ahora analice como LocalDateTimeporque la entrada carece de información sobre el desplazamiento desde UTC o la zona horaria. A LocalDateTimeno tiene concepto de desplazamiento ni zona horaria, por lo que no representa un momento real en la línea de tiempo.

LocalDateTime ldt = LocalDateTime.parse( input );

ZoneOffset

Parece estar diciendo que, desde el contexto empresarial, sabe que la intención de esta cadena es representar un momento que está 13 horas por delante de UTC. Entonces creamos una instancia de ZoneOffset.

ZoneOffset offset = ZoneOffset.ofHours( 13 ); // 13 hours ahead of UTC, in the far east of the globe.

OffsetDateTime

Aplícalo para obtener un OffsetDateTimeobjeto. Este se convierte en un momento real en la línea de tiempo.

OffsetDateTime odt = ldt.atOffset( offset);

ZoneId

Pero luego mencionas Nueva Zelanda. Entonces tenías una zona horaria específica en mente. Una zona horaria es una diferencia con respecto a UTC más un conjunto de reglas para manejar anomalías como el horario de verano (DST). Entonces podemos especificar a ZoneIden ZonedDateTimelugar de un simple desplazamiento.

Especifique un nombre de zona horaria adecuado . Nunca utilice abreviaturas de 3 o 4 letras como ESTo ISTporque no son zonas horarias reales, no están estandarizadas y ni siquiera son únicas (!). Por ejemplo, Pacific/Auckland.

ZoneId z = ZoneId.of( "Pacific/Auckland" );

ZonedDateTime

Aplica el ZoneId.

ZonedDateTime zdt = ldt.atZone( z );

Puedes adaptarte fácilmente a otra zona para el mismo momento en la línea de tiempo.

ZoneId zParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdt.withZoneSameInstant( zParis );  // Same moment in time, but seen through lens of Paris wall-clock time.

Contar desde la época

Recomiendo encarecidamente no manejar los valores de fecha y hora como un recuento de la época, como milisegundos desde el inicio de 1970 UTC. Pero si es necesario, cree uno Instanta partir de ese número.

Instant instant = Instant.ofEpochMilli( 1_317_816_735_000L );

Luego, asigne una zona horaria como se ve arriba, si lo desea, para alejarse de UTC.

ZoneId z = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt = instant.atZone( z );

Su valor de 1_317_816_735_000Les:

  • 2011-10-05T12:12:15Z(Miércoles 5 de octubre de 2011, 12:12:15 GMT)
  • 2011-10-06T01:12:15+13:00[Pacific/Auckland](Jueves 06 de octubre de 2011 01:12:15 en Auckland Nueva Zelanda).

Generar cadenas

Para generar una cadena en formato estándar ISO 8601 , simplemente llame toString. Tenga en cuenta que ZonedDateTimeamplía sabiamente el formato estándar añadiendo el nombre de la zona horaria entre corchetes.

String output = zdt.toString();

Para otros formatos, busque clase en Stack Overflow DateTimeFormatter. Ya cubierto muchas veces.

Especifique a FormatStyley a Locale.

Locale l = new Locale( "en" , "NZ" );
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( l );
String output = zdt.format( f );

Tenga en cuenta que la zona horaria no tiene nada que ver con la configuración regional. Puede Europe/Parismostrar una fecha y hora en idioma japonés y normas culturales, o una Asia/Kolkatafecha y hora en idioma portugués y normas culturales de Brasil.

Acerca de java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases reemplazan a las antiguas y problemáticas clases de fecha y hora como java.util.Date, .Calendar, & java.text.SimpleDateFormat.

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a java.time.

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones.

Gran parte de la funcionalidad java.time está adaptada a Java 6 y 7 en ThreeTen-Backport y se adapta aún más a Android en ThreeTenABP (consulte Cómo utilizar... ).

El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como , Interval, y más.YearWeekYearQuarter

Basil Bourque avatar Sep 02 '2016 20:09 Basil Bourque