Při konverzi ztrácíte příliš mnoho přesnosti, než abyste se mohli vrátit opačným směrem. Můžete se přiblížit pomocí časových razítek namísto dat.
Za prvé, váš původní dotaz úplně ztratil časovou složku:
select to_char(date '1970-01-01'
+ (1432550197431912935 - power(2, 60))/power(2, 44), 'YYYY-MM-DD HH24:MI:SS')
from dual;
2013-07-09 01:13:19
... ale i s tím převod zpět ztratil příliš mnoho:
select ((to_date('2013-07-09 01:13:19','YYYY-MM-DD HH24:MI:SS')
- date '1970-01-01') * power(2, 44)) + power(2, 60) from dual;
1432550197477589405
Což je blíž než 1432549301782839296, které máte, ale stále je to docela daleko.
Součástí problému je přesnost DATE
, což je teprve do druhého. Pokud používáte TIMESTAMP
místo toho se můžete dostat docela blízko; můžete vidět, že hodnota, kterou chcete mít, je údajně velmi přesná:
select timestamp '1970-01-01 00:00:00'
+ numtodsinterval((1432550197431912935 - power(2, 60))/power(2, 44), 'DAY')
from dual;
2013-07-09 01:13:18.775670462
Převod tohoto zpět je komplikovaný aritmetikou časových razítek poskytujících intervalové výsledky, se kterými pak musíte manipulovat, abyste se dostali zpět na číslo, nejprve jako původní počet dní:
select extract(day from int_val)
+ extract(hour from int_val) / 24
+ extract(minute from int_val) / (24 * 60)
+ extract(second from int_val) / (24 * 60 * 60)
from (
select to_timestamp('2013-07-09 01.13.18.775670462', 'YYYY-MM-DD HH24:MI:SS.FF9')
- timestamp '1970-01-01 00:00:00' as int_val from dual);
15895.0509117554451620370370370370370371
... a pak s vaší manipulací s mocí:
select ((extract(day from int_val)
+ extract(hour from int_val) / 24
+ extract(minute from int_val) / (24 * 60)
+ extract(second from int_val) / (24 * 60 * 60))
* power(2, 44)) + power(2, 60)
as x
from (
select to_timestamp('2013-07-09 01.13.18.775670462', 'YYYY-MM-DD HH24:MI:SS.FF9')
- timestamp '1970-01-01 00:00:00' as int_val from dual);
1432550197431912935.09988554676148148148
Což je zatraceně blízko. Můžete to zkrátit nebo zaokrouhlit na nejbližší celé číslo.
Pouhý pohled na vaše čísla a manipulaci s mocí v každém směru ukazuje, že se zdá být v rámci přesnosti, se kterou se Oracle dokáže vyrovnat:
select (1432550197431912935 - power(2, 60)) / power(2, 44)
from dual;
15895.050911755445156359201064333319664
select (15895.050911755445156359201064333319664 * power(2, 44)) + power(2, 60)
from dual;
1432550197431912935.000...
I s časovým razítkem něco z toho ztratíte, protože první hodnota překračuje limit 9-ti místný zlomek sekund. Část, která představuje zlomkové sekundy – jakmile započítáte 15 895 hodin atd. – je .0000089776673785814232865555418862
dne, což je .77567046150943497195839881896768
sekundy; časové razítko je zaokrouhluje na .775670462
. Takže to nikdy nebude dokonalé.
To také vede k zamyšlení nad tím, jak se generuje původní číslo; zdá se nepravděpodobné, že ve skutečnosti představuje čas až do této extrémní přesnosti, protože je pod yoktosekundy . Není skutečně jasné, zda je „přesnost“ skutečně artefaktem manipulace na základě mocnin 2, ale každopádně to nevypadá příliš užitečně. Je běžnější používat datum epochy ve stylu Unixu, počítající sekundy nebo někdy milisekundy od data epochy, které stejně používáte, pokud vůbec musí být uloženo jako číslo. Tento design je... zajímavý.