Convertir fecha/hora para una zona horaria determinada - java
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:
- Establecer la hora en un objeto (Calendario/Fecha/Marca de hora)
- (Posiblemente) Establezca la zona horaria de la marca de tiempo inicial (calendar.setTimeZone(...))
- Formatee la marca de tiempo con una nueva zona horaria (formatter.setTimeZone(...)))
- Devuelve una cadena con la nueva zona horaria. (formateador.formato(calendario.getTime()))
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()));
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!
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 LocalDateTime
porque la entrada carece de información sobre el desplazamiento desde UTC o la zona horaria. A LocalDateTime
no 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 OffsetDateTime
objeto. 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 ZoneId
en ZonedDateTime
lugar de un simple desplazamiento.
Especifique un nombre de zona horaria adecuado . Nunca utilice abreviaturas de 3 o 4 letras como EST
o IST
porque 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 Instant
a 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_000L
es:
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 ZonedDateTime
amplí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 FormatStyle
y 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/Paris
mostrar una fecha y hora en idioma japonés y normas culturales, o una Asia/Kolkata
fecha 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.YearWeek
YearQuarter