在Oracle中,将数字(5,10)转换为日期

时间:2012-02-17 13:13:30

标签: sql oracle date-format

在Oracle中执行以下SQL语法时,总是不成功,请帮忙。

  

40284.3878935185代表'2010-04-16 09:18:34',微秒。

     

1900年1月1日的纪元日期(如Excel)。

create table temp1 (date1 number2(5,10));

insert into temp1(date1) values('40284.3878935185');

select to_date(date1, 'yyyy-mm-dd hh24:mi:ssxff') from temp1
  

错误报告:SQL错误:ORA-01861:文字与格式不匹配   串   01861. 00000 - “文字与格式字符串不匹配”   *原因:输入中的文字必须与文字中的文字长度相同              格式字符串(前导空格除外)。如果              “FX”修饰符已经打开,文字必须完全匹配,              没有额外的空白。   *操作:更正格式字符串以匹配文字。

感谢Mark Ba​​nnister

现在SQL语法是:

select to_char(to_date('1899-12-30','yyyy-mm-dd') + 
date1,'yyyy-mm-dd hh24:mi:ss')  from temp1

但无法获取日期格式,例如'yyyy-mm-dd hh24:mi:ss.ff'。继续寻求帮助。

4 个答案:

答案 0 :(得分:5)

使用1899年12月30日的纪元日期,尝试:

select to_date('1899-12-30','yyyy-mm-dd') + date1

答案 1 :(得分:1)

简单日期添加不适用于时间戳,至少如果您需要保留小数秒。当您执行to_timestamp('1899-12-30','yyyy-mm-dd')+ date1时(在对Mark的回答的评论中){​​{3}}在添加之前隐式转换为TIMESTAMP,整体答案为DATE,因此没有任何小数秒;那么你使用to_char(..., '... .FF')抱怨ORA-01821。

您需要将date1列所占的天数转换为DATE。幸运的是,Oracle提供了一种功能,interval

select to_timestamp('1899-12-30','YYYY-MM-DD')
    + numtodsinterval(date1, 'DAY') from temp3;

16-APR-10 09.18.33.999998400

然后,您可以以所需的格式显示该格式,例如(使用CTE提供date1值):

with temp3 as ( select 40284.3878935185 as date1 from dual)
select to_char(to_timestamp('1899-12-30','YYYY-MM-DD')
    + numtodsinterval(date1, 'DAY'), 'YYYY-MM-DD HH24:MI:SSXFF') from temp3;

2010-04-16 09:18:33.999998400

或限制为千分之一秒:

with temp3 as ( select 40284.3878935185 as date1 from dual)
select to_char(to_timestamp('1899-12-30','YYYY-MM-DD')+
    + numtodsinterval(date1, 'DAY'), 'YYYY-MM-DD HH24:MI:SS.FF3') from temp3;


2010-04-16 09:18:33.999

1899-12-30的时代听起来很奇怪,并且如你所述,并不符合Excel。您的预期结果似乎更可能是错误的,应该是2010-04-18,所以我会检查您的假设。安德鲁也提出了一些好处,你应该将你的价值存储在TIMESTAMP列的表格中。如果你收到这样的数据,你仍然需要沿着这些方面的东西将它转换为存储在某些时候。

答案 2 :(得分:0)

不完全知道纪元日期,但尝试类似:

select to_date('19700101','YYYYMMDD')+ :secs_since_epoch/86400 from dual;

或者,转换为时间戳,如:

select cast(to_date('19700101', 'YYYYMMDD') + :secs_since_epoch/86400 as timestamp with local time zone) from dual;

答案 3 :(得分:0)

我希望这不会过于严厉,但你必须在这里完全重新思考你的方法。

你根本没有保持数据类型。示例的每一行都会误用数据类型。

  • TEMP1.DATE1不是日期或varchar2,而是NUMBER
  • 您不是插入号码40284.3878935185,而是STRING>> '40284.3878935185'<<
  • 您的SELECT TO_DATE(...)使用NUMBER Temp1.Date1值,但使用格式块将其视为VARCHAR2

我大约95%确定您认为Oracle使用简单的块数据副本传输此数据。 “既然每个Oracle日期都存储为数字,为什么不将这个数字插入表中呢?”好吧,因为当您将列定义为NUMBER时,您告诉Oracle“这不是日期”。因此,Oracle不会将其作为日期进行管理。

这些类型转换中的每一个都是由Oracle根据您当前的会话变量计算的。如果你在法国,那里的'。' INSERT是千位分隔符而不是基数,INSERT将完全失败。

所有这些带字符串的转换都会被Oracle认为您正在运行的语言环境修改。检查字典视图V $ NLS_PARAMETERS。

日期/时间值会变得更糟。日期/时间值可以遍布地图 - 主要是因为时区。您的数据库服务器在哪个时区?它认为你在哪个时区跑?如果这不能让你的头脑充分旋转,请查看如果您将Oracle的默认日历从Gregorian更改为Thai Buddha会发生什么。

我强烈建议你彻底摆脱这些数字。

要创建日期或日期时间值,请使用具有完全不变且明确的格式的字符串。然后专门分配,比较和计算日期值,例如:

GOODFMT常数VARCHAR2 ='YYYY-MM-DD HH24:MI:SS.FFF ZZZ'

Good_Time DATE = TO_DATE('2012-02-17 08:07:55.000 EST',GOODFMT);