Oracle 10g 在日期中接受 5 位数年份

Oracle 10g accepting 5 digit year in a Date

我设法将 Oracle 10g 中的日期 21-Feb-12017(我知道这不是正确的日期)输入到日期列中。甲骨文接受日期罚款。当我试图 select 它回到 SQL Developer 时,SQL Developer 将它显示为 NULL。但是,当我尝试通过 java 检索日期时,我得到了与插入方式相同的值。想知道发生了什么,因为我还可以看到 Oracle 将不同的 5 位数年份转换为 4 位数年份。我输入 21-Feb-21019,Oracle 在存储时将年份转换为 4581。我什至可以在 SQL 开发人员中 select 这个值。

我想知道是否可以读取原始日期,例如 21-Feb-21019 更改为 21-Feb-4581,那么如何读取 21-Feb-21019 而不是 21-Feb-4581。

Oracle 使用 7 bytesDATEs 存储在 tables 中,其中前 2 个字节是:

  • 世纪 + 100
  • 世纪年份​​ + 100

所以可以(技术上)存储的最大日期是当这两个字节具有值 255199 时,这将给出 15599 的年份(我'我忽略了理论上您可以将 255 存储在第二个字节中,因为这会打开一大堆单独的问题)。

您可以 convert a raw value to a date 使用 DBMS_STATS.CONVERT_RAW_VALUE 这意味着我们可以绕过创建日期的常规方法并直接生成将要存储的字节值。

这个函数就是一个例子:

CREATE FUNCTION createDate(
  year   int,
  month  int,
  day    int,
  hour   int,
  minute int,
  second int
) RETURN DATE DETERMINISTIC
IS
  hex CHAR(14);
  d DATE;
BEGIN
  hex := TO_CHAR( FLOOR( year / 100 ) + 100, 'fm0X' )
      || TO_CHAR( MOD( year, 100 ) + 100, 'fm0X' )
      || TO_CHAR( month, 'fm0X' )
      || TO_CHAR( day, 'fm0X' )
      || TO_CHAR( hour + 1, 'fm0X' )
      || TO_CHAR( minute + 1, 'fm0X' )
      || TO_CHAR( second + 1, 'fm0X' );
  DBMS_OUTPUT.PUT_LINE( hex );
  DBMS_STATS.CONVERT_RAW_VALUE( HEXTORAW( hex ), d );
  RETURN d;
END;
/

然后,如果您有日期列,则可以插入通常不允许插入的值:

CREATE TABLE table_name ( date_column DATE );

INSERT INTO table_name ( date_column )
VALUES ( DATE '2019-12-31' + INTERVAL '1:02:03' HOUR TO SECOND );

INSERT INTO table_name ( date_column ) VALUES ( createDate( 15599, 12, 31, 1, 2, 3 ) );

INSERT INTO table_name ( date_column ) VALUES ( createDate( 12017, 2, 21, 0, 0, 0 ) );
当年份超出日期的正常范围时,

TO_CHAR 不起作用。要获取存储在 table 中的值,您可以使用 DUMP 获取包含字节值的字符串,或者您可以使用 EXTRACT 获取各个组件。

SELECT DUMP( date_column ),
       TO_CHAR( date_column, 'YYYY-MM-DD' ) AS value,
       TO_CHAR( EXTRACT( YEAR FROM date_column ), 'fm00000' )
         || '-' || TO_CHAR( EXTRACT( MONTH  FROM date_column ), 'fm00' )
         || '-' || TO_CHAR( EXTRACT( DAY    FROM date_column ), 'fm00' )
         || ' ' || TO_CHAR( EXTRACT( HOUR   FROM CAST( date_column AS TIMESTAMP ) ), 'fm00' )
         || ':' || TO_CHAR( EXTRACT( MINUTE FROM CAST( date_column AS TIMESTAMP ) ), 'fm00' )
         || ':' || TO_CHAR( EXTRACT( SECOND FROM CAST( date_column AS TIMESTAMP ) ), 'fm00' )
         AS full_value
FROM table_name;

输出:

DUMP(DATE_COLUMN)                 | VALUE      | FULL_VALUE          
:-------------------------------- | :--------- | :-------------------
Typ=12 Len=7: 120,119,12,31,2,3,4 | 2019-12-31 | 02019-12-31 01:02:03
Typ=12 Len=7: 255,199,12,31,2,3,4 | 0000-00-00 | 15599-12-31 01:02:03
Typ=12 Len=7: 220,117,2,21,1,1,1  | 0000-00-00 | 12017-02-21 00:00:00

db<>fiddle here