¿Hay alguna forma de convertir ZoneId a ZoneOffset en java 8?


85

Tengo un segundo de época y un zoneId, por método1. Se puede convertir a LocalDateTime con el zoneId predeterminado del sistema, pero no encuentro la manera de convertir el segundo de época a LocalDateTime por el método2, porque no hay ZoneOffset.systemDefault. Creo que es oscuro.

import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}

val epochSecond = System.currentTimeMillis() / 1000

LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault())//method1
LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.MAX)//method2

¿Qué idioma / dialecto está utilizando para que import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}funcione la importación masiva ? Es unexpected tokenpara mí en Java 11.
anddero

1
Puede ser Scala, pero eso no es realmente importante aquí
donatas M

1
He discutido la teoría detrás java.timeen detalle aquí: stackoverflow.com/a/56508200/145989
Ondra Žižka

Respuestas:


109

Así es como se puede obtener ZoneOffseta partir de ZoneId:

Instant instant = Instant.now(); //can be LocalDateTime
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);

NB: ZoneIdpuede tener un desplazamiento diferente según el momento y la historia del lugar en particular. Por lo tanto, elegir diferentes instantes daría como resultado diferentes compensaciones.

NB2: ZoneId.of()puede devolver un en ZoneOffsetlugar de ZoneIdsi se pasa UTC+3/ GMT+2/ etc en lugar de una zona horaria como Africa/Cairo. Entonces, si se pasan las compensaciones UTC / GMT, la información histórica / geográfica / de horario de verano Instantno se tendrá en cuenta; simplemente trabajará con la compensación especificada.


Esto da un valor incorrecto si el instante actual está en DST, pero en el momento del cálculo era sin DST
msangel

@msangel, ¿puedes comprobarlo? ¿Quizás está usando ZoneId.systemDefault()en su código y devuelve diferentes zonas horarias en diferentes máquinas?
Stanislav Bashkyrtsev

Lo verifiqué y funciona dependiendo de otra condición. Respuesta actualizada con eso.
msangel

43

No hay mapeo uno a uno. Un ZoneId define una extensión geográfica en la que se usa un conjunto de diferentes ZoneOffsets a lo largo del tiempo. Si la zona horaria usa el horario de verano, su ZoneOffset será diferente entre verano e invierno.

Además, las reglas del horario de verano pueden haber cambiado con el tiempo, por lo que ZoneOffset podría ser diferente, por ejemplo, para el 13/10/2015 en comparación con el 13/10/1980.

Por lo tanto, solo puede encontrar el ZoneOffset para un ZoneId en un Instant en particular.

Véase también https://en.wikipedia.org/wiki/Tz_database


10
Lo sé, pero ¿cómo se hace? Si conozco el LocaDate y tengo un ZoneId, ¿cómo puedo convertirlo en un ZoneOffset? Mi caso de uso es una cadena que contiene una hora y una zona. Quiero analizar la cadena en un OffsetTime, sin soltar la información de la zona.
Kai Moritz

33

tl; dr

ZonedDateTime.now( 
    ZoneId.of( "America/Montreal" ) 
)

... de la zona horaria predeterminada actual ...

ZonedDateTime.now( 
    ZoneId.systemDefault() 
)

Detalles

La respuesta de Stanislav Bshkyrtsev responde correcta y directamente a su pregunta.

Pero hay problemas más importantes involucrados, como se sugiere en la Respuesta de Jon Skeet .

LocalDateTime

No encuentro la manera de convertir epoch second a LocalDateTime

LocalDateTimeintencionalmente no tiene un concepto de zona horaria o desplazamiento de UTC. Probablemente no sea lo que quieras. El Local…significa cualquier localidad, no cualquier localidad en particular. Esta clase no representa un momento, solo momentos potenciales en un rango de aproximadamente 26-27 horas (el rango de zonas horarias alrededor del mundo).

Instant

No es necesario comenzar con los segundos de época si está tratando de obtener la hora actual. Obtenga la corriente Instant. La Instantclase representa un momento en la línea de tiempo en UTC con una resolución de nanosegundos (hasta nueve (9) dígitos de una fracción decimal).

Instant instant = Instant.now();

Dentro de eso Instanthay un recuento de nanosegundos desde la época. Pero realmente no nos importa.

ZonedDateTime

Si desea ver ese momento a través del lente del reloj de pared de una región en particular, aplique a ZoneIdpara obtener a ZonedDateTime.

ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = instant.atZone( z );

Como atajo, puede hacerlo directamente al ZonedDateTime.

ZonedDateTime zdt = ZonedDateTime.now( z );  

A ZonedDateTimetiene un Instantdentro de él. Llame zdt.toInstant()para obtener el mismo momento en el tiempo que un valor básico en UTC . El mismo número de nanosegundos desde la época de cualquier manera, como a ZonedDateTimeo como a Instant.

Segundos desde la época dada

Si se le da un recuento de segundos desde época, y la época es el primer momento de 1970 en UTC ( 1970-01-01T00:00:00Z), ingrese ese número a Instant.

long secondsSinceEpoch = 1_484_063_246L ;
Instant instant = Instant.ofEpochSecond( secondsSinceEpoch ) ;

Tabla de tipos de fecha y hora en Java, tanto moderno como heredado.


Acerca de java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.

Para obtener más información, consulte el tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

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

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No se necesitan cuerdas, no se necesitan java.sql.*clases.

¿Dónde obtener las clases java.time?

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


10

Como dice la documentación , "Esto está pensado principalmente para conversiones de bajo nivel en lugar de uso general de aplicaciones".

Ir a través Instanttiene mucho sentido para mí: su segundo de época es efectivamente una representación diferente de un Instant, así que conviértalo en un Instanty luego conviértalo en una zona horaria en particular.


9

Espero que las dos primeras líneas de mi solución a continuación sean útiles. Mi problema era que tenía una LocalDateTimey el nombre de una zona horaria, y necesitaba una instantpara poder construir una java.util.Date, porque eso es lo que quería MongoDB. Mi código es Scala, pero está tan cerca de Java aquí que creo que no debería haber problemas para entenderlo:

val zid = ZoneId.of(tzName)                                // "America/Los_Angeles"
val zo: ZoneOffset = zid.getRules.getOffset(localDateTime) // ⇒ -07:00
                                         // 2017-03-16T18:03

val odt = OffsetDateTime.of(localDateTime, zo) // ⇒ 2017-03-16T18:03:00-07:00
val instant = odt.toInstant                    // ⇒ 2017-03-17T01:03:00Z
val issued = Date.from(instant)

1
Publiqué esta respuesta aquí porque esta fue la página que apareció cuando estaba buscando una manera de hacer lo que finalmente descubrí.
gknauth

1

Lo siguiente devuelve la cantidad de tiempo en milisegundos que se debe agregar a UTC para obtener la hora estándar en esta zona horaria:

TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")).getRawOffset()

0

Tengo un segundo de época y un zoneId. ¿Hay alguna forma de convertir ZoneId a ZoneOffset en java 8?

  1. Obtener ZonedDateTimedesde el segundo de época y el ID de zona
  2. Obtener ZoneOffsetdeZonedDateTime

Manifestación:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        // Get ZonedDateTime from epoch second and Zone Id
        ZonedDateTime zdt = Instant.ofEpochSecond(1597615462L).atZone(ZoneId.of("Europe/London"));

        // Get ZoneOffset from ZonedDateTime
        ZoneOffset offset = zdt.getOffset();

        System.out.println(offset);
    }
}

Salida:

+01:00
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.