如何在 Oracle 中将时间戳以毫秒为单位转换为日期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43066665/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 03:24:33  来源:igfitidea点击:

How to convert timestamp with milliseconds to date in Oracle

sqloracle

提问by Damiqib

I have MSSTAMPas "timestamp with milliseconds" in Oracle, format: 1483228800000. How can I cast that milliseconds timestamp into a date format "YYYY-MM", in order to get count of FINISHED rows per month for previous years.

我在 Oracle中将 MSSTAMP作为“带毫秒的时间戳”,格式:1483228800000。如何将该毫秒时间戳转换为日期格式“YYYY-MM”,以便获得前几年每月完成的行数。

I have tried different variations of TO_DATE, CAST, TO_CHAR - but I'm unable to get this working.

我尝试了 TO_DATE、CAST、TO_CHAR 的不同变体 - 但我无法使其正常工作。

select 
  count(*) "EVENTS",
  TO_DATE(MSSTAMP, 'YYYY-MM') "FINISHED_MONTH"
from 
  DB_TABLE
where 
  MSSTAMP < '1483228800000' 
and 
  STATUS in ('FINISHED') 
group by
  FINISHED_MONTH ASC

回答by MT0

If you just need to convert from milliseconds since epoch to a date, then:

如果您只需要从纪元以来的毫秒数转换为日期,则:

SELECT TIMESTAMP '1970-01-01 00:00:00.000'
         + NUMTODSINTERVAL( 1483228800000 / 1000, 'SECOND' )
         AS TIME
FROM   DUAL

Which outputs:

哪些输出:

TIME
-----------------------
2017-01-01 00:00:00.000

It you just want the year-month then use TRUNC( timestamp, 'MM' )or TO_CHAR( timestamp, 'YYYY-MM' ).

如果您只想要年月然后使用TRUNC( timestamp, 'MM' )TO_CHAR( timestamp, 'YYYY-MM' )

If you need to handle leap secondsthen you can create a utility package that will adjust the epoch time to account for this:

如果您需要处理闰秒,那么您可以创建一个实用程序包来调整纪元时间以解决此问题:

CREATE OR REPLACE PACKAGE time_utils
IS
  FUNCTION milliseconds_since_epoch(
    in_datetime  IN TIMESTAMP,
    in_epoch     IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
  ) RETURN NUMBER;

  FUNCTION milliseconds_epoch_to_ts (
    in_milliseconds IN NUMBER,
    in_epoch        IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
  ) RETURN TIMESTAMP;
END;
/
SHOW ERRORS;

CREATE OR REPLACE PACKAGE BODY time_utils
IS
  -- List of the seconds immediately following leap seconds:
  leap_seconds CONSTANT SYS.ODCIDATELIST := SYS.ODCIDATELIST(
      DATE '1972-07-01',
      DATE '1973-01-01',
      DATE '1974-01-01',
      DATE '1975-01-01',
      DATE '1976-01-01',
      DATE '1977-01-01',
      DATE '1978-01-01',
      DATE '1979-01-01',
      DATE '1980-01-01',
      DATE '1981-07-01',
      DATE '1982-07-01',
      DATE '1983-07-01',
      DATE '1985-07-01',
      DATE '1988-01-01',
      DATE '1990-01-01',
      DATE '1991-01-01',
      DATE '1992-07-01',
      DATE '1993-07-01',
      DATE '1994-07-01',
      DATE '1996-01-01',
      DATE '1997-07-01',
      DATE '1999-01-01',
      DATE '2006-01-01',
      DATE '2009-01-01',
      DATE '2012-07-01',
      DATE '2015-07-01',
      DATE '2016-01-01'
    );

  HOURS_PER_DAY           CONSTANT BINARY_INTEGER := 24;
  MINUTES_PER_HOUR        CONSTANT BINARY_INTEGER := 60;
  SECONDS_PER_MINUTE      CONSTANT BINARY_INTEGER := 60;
  MILLISECONDS_PER_SECOND CONSTANT BINARY_INTEGER := 1000;

  MINUTES_PER_DAY         CONSTANT BINARY_INTEGER := HOURS_PER_DAY   * MINUTES_PER_HOUR;
  SECONDS_PER_DAY         CONSTANT BINARY_INTEGER := MINUTES_PER_DAY * SECONDS_PER_MINUTE;

  MILLISECONDS_PER_MINUTE CONSTANT BINARY_INTEGER := SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
  MILLISECONDS_PER_HOUR   CONSTANT BINARY_INTEGER := MINUTES_PER_HOUR   * MILLISECONDS_PER_MINUTE;
  MILLISECONDS_PER_DAY    CONSTANT BINARY_INTEGER := HOURS_PER_DAY      * MILLISECONDS_PER_HOUR;

  FUNCTION milliseconds_since_epoch(
    in_datetime  IN TIMESTAMP,
    in_epoch     IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
  ) RETURN NUMBER
  IS
    p_leap_milliseconds BINARY_INTEGER := 0;
    p_diff              INTERVAL DAY(9) TO SECOND(3);
  BEGIN
    IF in_datetime IS NULL OR in_epoch IS NULL THEN
      RETURN NULL;
    END IF;

    p_diff := in_datetime - in_epoch;

    IF in_datetime >= in_epoch THEN
      FOR i IN 1 .. leap_seconds.COUNT LOOP
        EXIT WHEN in_datetime < leap_seconds(i);
        IF in_epoch < leap_seconds(i) THEN
          p_leap_milliseconds := p_leap_milliseconds + MILLISECONDS_PER_SECOND;
        END IF;
      END LOOP;
    ELSE
      FOR i IN REVERSE 1 .. leap_seconds.COUNT LOOP
        EXIT WHEN in_datetime > leap_seconds(i);
        IF in_epoch > leap_seconds(i) THEN
          p_leap_milliseconds := p_leap_milliseconds - MILLISECONDS_PER_SECOND;
        END IF;
      END LOOP;
    END IF;

    RETURN   MILLISECONDS_PER_SECOND * EXTRACT( SECOND FROM p_diff )
           + MILLISECONDS_PER_MINUTE * EXTRACT( MINUTE FROM p_diff )
           + MILLISECONDS_PER_HOUR   * EXTRACT( HOUR   FROM p_diff )
           + MILLISECONDS_PER_DAY    * EXTRACT( DAY    FROM p_diff )
           + p_leap_milliseconds;
  END milliseconds_since_epoch;

  FUNCTION milliseconds_epoch_to_ts(
    in_milliseconds IN NUMBER,
    in_epoch        IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
  ) RETURN TIMESTAMP
  IS
    p_datetime TIMESTAMP;
  BEGIN
    IF in_milliseconds IS NULL OR in_epoch IS NULL THEN
      RETURN NULL;
    END IF;

    p_datetime := in_epoch
        + NUMTODSINTERVAL( in_milliseconds / MILLISECONDS_PER_SECOND, 'SECOND' );

    IF p_datetime >= in_epoch THEN
      FOR i IN 1 .. leap_seconds.COUNT LOOP
        EXIT WHEN p_datetime < leap_seconds(i);
        IF in_epoch < leap_seconds(i) THEN
          p_datetime := p_datetime - INTERVAL '1' SECOND;
        END IF;
      END LOOP;
    ELSE
      FOR i IN REVERSE 1 .. leap_seconds.COUNT LOOP
        EXIT WHEN p_datetime > leap_seconds(i);
        IF in_epoch > leap_seconds(i) THEN
          p_datetime := p_datetime + INTERVAL '1' SECOND;
        END IF;
      END LOOP;
    END IF;

    RETURN p_datetime;
  END milliseconds_epoch_to_ts;
END;
/
SHOW ERRORS;

Then you can do:

然后你可以这样做:

SELECT TIME_UTILS.milliseconds_epoch_to_ts(
         in_milliseconds => 1483228800000,
         in_epoch        => TIMESTAMP '1970-00-00 00:00:00.000'
       ) AS time
FROM DUAL;

And get the output:

并得到输出:

TIME
-----------------------
2016-12-31 23:59:33.000

Note: you will need to keep the package up-to-date when new leap-seconds are proposed.

注意:当提出新的闰秒时,您需要使包保持最新。

Update:

更新

SELECT COUNT(*) "EVENTS",
       TRUNC(
         TIMESTAMP '1970-01-01 00:00:00.000'
           + NUMTODSINTERVAL( MSSTAMP / 1000, 'SECOND' ),
         'MM'
       ) "FINISHED_MONTH"
FROM   DB_TABLE
WHERE  MSSTAMP < 1483228800000
AND    STATUS = 'FINISHED'
GROUP BY
       TRUNC(
         TIMESTAMP '1970-01-01 00:00:00.000'
           + NUMTODSINTERVAL( MSSTAMP / 1000, 'SECOND' ),
         'MM'
       );