Search code examples
sqloracle-databasetimestampunix-timestampmilliseconds

Convert milliseconds to Timestamp


I know that to convert a Unix timestamp in milliseconds to an SQL timestamp I can use

SELECT TO_DATE('1970-01-01','YYYY-MM-DD HH24:MI:SS') + 
       (:timestamp / (1000*60*60*24)) FROM DUAL;

But I need a Timestamp, so I tried with

SELECT TO_TIMESTAMP('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SSFF3') + 
       (:timestamp) from DUAL

Which gives me the error:

Error: ORA-01841: (full) year must be between -4713 and +9999, and not be 0

It seems that adding 1 to the timestamp always converts it to a day.

How can I do the same to get a real timestamp?


Solution

  • You will get a timestamp if you add an interval to a timestamp (see date/interval arithmetics).

    As Benoit noticed, you can't specify an interval with seconds when there are more than about 2.1e9 of them:

    SQL> SELECT numtodsinterval(2.2e9, 'SECOND'),
      2         numtodsinterval(2.3e9, 'SECOND')
      3    FROM dual;
    
    NUMTODSINTERVAL(2.2E9,'SECOND'  NUMTODSINTERVAL(2.3E9,'SECOND'
    ------------------------------- -------------------------------
    +000024855 03:14:07.147483647   +000024855 03:14:07.147483647
    

    This is why you should use minutes which do not lose precision. For example, assuming :TS is the unix timestamp (i.e. a number):

    SQL> variable ts number;
    SQL> -- determining unix timestamp with nanosecond precision
    SQL> BEGIN
      2     :ts := (to_date('2099-01-01 01:02:03', 'yyyy-mm-dd hh24:mi:ss')
      3              - date '1970-01-01') * 1000*60*60*24
      4            + 123.456789;
      5  END;
      6  /
    
    ts
    ---------
    4070912523123,456789
    
    SQL> select timestamp '1970-01-01 00:00:00'
      2         + numtodsinterval((:ts)/1000/60, 'MINUTE')
      3    from dual;
    
    TIMESTAMP'1970-01-0100:00:00'+NUMTODSINTERVAL((:TS)/1000/60,'MINUTE')
    ---------------------------------------------------------------------------
    2099-01-01 01:02:03.123456789