Existe alguma maneira de converter ZoneId em ZoneOffset em java 8?

89

Eu tenho um segundo período e um zoneId, pelo método1. Ele pode ser convertido para LocalDateTime com o zoneId padrão do sistema, mas não encontro uma maneira de converter um segundo período para LocalDateTime pelo método2, porque não há nenhum ZoneOffset.systemDefault. Acho que é obscuro.

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
Renkai
fonte
Que idioma / dialeto você está usando para fazer a importação em massa import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}funcionar? É um unexpected tokenpara mim em Java 11.
anddero
1
Pode ser Scala, mas isso não é muito importante aqui
donatas M
1
Eu discuti a teoria por trás disso java.timeaqui: stackoverflow.com/a/56508200/145989
Ondra Žižka

Respostas:

113

Aqui está como você pode obter ZoneOffsetde ZoneId:

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

NB: ZoneIdpode ter deslocamento diferente dependendo do momento e da história do lugar particular. Portanto, a escolha de diferentes Instants resultaria em diferentes deslocamentos.

NB2: ZoneId.of()pode retornar um ZoneOffset, em vez de ZoneIdse UTC+3/ GMT+2/ etc é passado, em oposição a um fuso horário como Africa/Cairo. Portanto, se os deslocamentos UTC / GMT forem passados, as informações históricas / geográficas / de horário de verão do Instantnão serão levadas em consideração - você simplesmente trabalhará com o deslocamento especificado.

Stanislav Bashkyrtsev
fonte
Isso dá um valor incorreto se o instante atual está em DST, mas no momento do cálculo estava sem DST
msangel
@msangel, você pode verificar isso? Talvez você esteja usando ZoneId.systemDefault()em seu código e ele retorne fusos horários diferentes em máquinas diferentes?
Stanislav Bashkyrtsev
Eu verifiquei isso e funciona dependendo de outra condição. Resposta atualizada com isso.
msangel de
43

Não há mapeamento um para um. Um ZoneId define uma extensão geográfica na qual um conjunto de ZoneOffsets diferentes é usado ao longo do tempo. Se o fuso horário usar o horário de verão, seu ZoneOffset será diferente entre o verão e o inverno.

Além disso, as regras do horário de verão podem ter mudado com o tempo, portanto, o ZoneOffset pode ser diferente para, por exemplo, 13/10/2015 em comparação com 13/10/1980.

Portanto, você só pode encontrar o ZoneOffset para um ZoneId em um determinado Instant.

Veja também https://en.wikipedia.org/wiki/Tz_database

GreyFairer
fonte
10
Eu sei disso, mas como isso é feito? Se eu sei o LocaDate e tenho um ZoneId, como faço para convertê-lo em um ZoneOffset. Meu caso de uso é uma string que contém um horário e uma zona. Quero analisar a string em um OffsetTime, sem eliminar as informações da zona.
Kai Moritz
35

tl; dr

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

... do fuso horário padrão atual ...

ZonedDateTime.now( 
    ZoneId.systemDefault() 
)

Detalhes

A resposta de Stanislav Bshkyrtsev responde correta e diretamente à sua pergunta.

Mas, há questões maiores envolvidas, como sugerido na resposta de Jon Skeet .

LocalDateTime

Não encontro a maneira de converter epoch second em LocalDateTime

LocalDateTimepropositalmente não tem conceito de fuso horário ou deslocamento em relação ao UTC. Provavelmente não é o que você quer. A Local…significa qualquer localidade, não qualquer uma localidade particular. Esta classe não representa um momento, apenas momentos potenciais ao longo de uma faixa de cerca de 26-27 horas (a faixa de fusos horários ao redor do globo).

Instant

Não há necessidade de começar com segundos de época se você estiver tentando obter a hora atual. Obtenha a corrente Instant. A Instantclasse representa um momento na linha do tempo em UTC com uma resolução de nanossegundos (até nove (9) dígitos de uma fração decimal).

Instant instant = Instant.now();

Dentro disso, Instanthá uma contagem de nanossegundos desde a época. Mas nós realmente não nos importamos.

ZonedDateTime

Se você quiser ver aquele momento através das lentes do relógio de parede de uma região em particular, aplique a ZoneIdpara obter a ZonedDateTime.

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

Como um atalho, você pode fazer diretamente para o ZonedDateTime.

ZonedDateTime zdt = ZonedDateTime.now( z );  

A ZonedDateTimetem um Instantdentro dele. Ligue zdt.toInstant()para obter o mesmo momento no tempo que um valor básico em UTC . Mesmo número de nanossegundos desde a época de qualquer maneira, como a ZonedDateTimeou como a Instant.

Segundos desde a época fornecidos

Se você receber uma contagem de segundos desde a época, e a época for o primeiro momento de 1970 em UTC ( 1970-01-01T00:00:00Z), passe esse número para Instant.

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

Tabela de tipos de data e hora em Java, tanto modernos quanto legados.


Sobre java.time

A estrutura java.time é construída em Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

Para saber mais, consulte o Tutorial Oracle . E pesquise no Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de cordas, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

O projeto ThreeTen-Extra estende java.time com classes adicionais. Este projeto é um campo de provas para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

Basil Bourque
fonte
10

Como diz a documentação , "Isso se destina principalmente a conversões de baixo nível, em vez de uso geral de aplicativos."

Seguir via Instantfaz todo o sentido para mim - seu segundo período é efetivamente uma representação diferente de um Instant, portanto, converta em um Instante depois converta isso em um fuso horário específico.

Jon Skeet
fonte
9

Espero que as duas primeiras linhas da minha solução abaixo sejam úteis. Meu problema era que eu tinha um LocalDateTimee o nome de um fuso horário e precisava de um instantpara poder construir um java.util.Date, porque era isso que o MongoDB queria. Meu código é Scala, mas é tão parecido com Java aqui que acho que não deve haver problemas para entendê-lo:

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)
gknauth
fonte
1
Publiquei esta resposta aqui porque esta foi a página que surgiu quando eu estava procurando uma maneira de fazer o que acabei descobrindo.
gknauth
2

O seguinte retorna a quantidade de tempo em milissegundos para adicionar ao UTC para obter a hora padrão neste fuso horário:

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

Eu tenho um segundo epoch e um zoneId. Existe alguma maneira de converter ZoneId em ZoneOffset em java 8?

  1. Obtenha ZonedDateTimeda segunda época e Id da zona
  2. Obtenha ZoneOffsetdeZonedDateTime

Demo:

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);
    }
}

Resultado:

+01:00
Viva e Deixe Viver
fonte