Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Converter data e hora local (com fuso horário) em um carimbo de data/hora Unix no Oracle


Você pode converter seu carimbo de data/hora com fuso horário para UTC e, em seguida, subtrair a época disso:
select timestamp '2018-10-19 09:12:47.0 AMERICA/DENVER'
  - timestamp '1970-01-01 00:00:00.0 UTC' as diff
from dual;

que fornece um tipo de dados de intervalo:
DIFF                  
----------------------
+17823 15:12:47.000000

Você pode então extrair os elementos disso e multiplicar cada elemento por um fator apropriado para convertê-lo em milissegundos (ou seja, para dias, 60*60*24*1000); e, em seguida, adicione-os juntos:
select extract(day from diff) * 86400000
  + extract(hour from diff) * 3600000
  + extract(minute from diff) * 60000
  + extract(second from diff) * 1000 as unixtime
from (
  select timestamp '2018-10-19 09:12:47.0 AMERICA/DENVER'
    - timestamp '1970-01-01 00:00:00.0 UTC' as diff
  from dual
);

            UNIXTIME
--------------------
       1539961967000

db<>fiddle

Isso também preserva milissegundos, se o carimbo de data/hora inicial os tiver (isso converte de um tempo 'Unix' enquanto os preserva):
select (timestamp '1970-01-01 00:00:00.0 UTC' + (1539961967567 * interval '0.001' second))
  at time zone 'America/Denver' as denver_time
from dual;

DENVER_TIME                                 
--------------------------------------------
2018-10-19 09:12:47.567000000 AMERICA/DENVER

então para converter de volta:
select extract(day from diff) * 86400000
  + extract(hour from diff) * 3600000
  + extract(minute from diff) * 60000
  + extract(second from diff) * 1000 as unixtime
from (
  select timestamp '2018-10-19 09:12:47.567 AMERICA/DENVER'
    - timestamp '1970-01-01 00:00:00.0 UTC' as diff
  from dual
);

            UNIXTIME
--------------------
       1539961967567

db<>fiddle

Se o seu carimbo de data/hora inicial tiver uma precisão maior do que isso, você precisará truncar (ou round/floor/ceil/cast) para evitar um resultado não inteiro; esta versão apenas trunca a parte de milissegundos extraída:
select diff,
  extract(day from diff) * 86400000
  + extract(hour from diff) * 3600000
  + extract(minute from diff) * 60000
  + trunc(extract(second from diff) * 1000) as unixtime
from (
  select timestamp '2018-10-19 09:12:47.123456789 AMERICA/DENVER'
    - timestamp '1970-01-01 00:00:00.0 UTC' as diff
  from dual
);

DIFF                                  UNIXTIME
------------------------- --------------------
+17823 15:12:47.123456789        1539961967123

Sem esse truncamento (ou equivalente), você terminaria com 1539961967123.456789 .

Eu tinha esquecido a discrepância dos segundos bissextos; se você precisa/quer lidar com isso, veja esta resposta .