to_date returns 错误的年份

to_date returns wrong year

我需要有关日期输出的帮助。我是 oracle 的新手,大部分时间我都在使用 msSQL。

我有一个存储为 varchar 的日期和时间,我需要输出以便我可以将其导入 sql db。

select to_date('210408092508', 'yy/mm/dd HH24:MI:SS') from dual; returns 08.04.2021 9:25:08 AM 可以

select to_date('210408091706', 'yy/mm/dd HH24:MI:SS') from dual; returns 2104 年 8 月 9 日 5:06:00 下午。 输出应该是 08.04.2021 9:17:06 AM

如您所见,第二个输出中的年份和时间是错误的... 如果我设置 HH12,它工作正常,但它必须是 HH24,因为 table.

中的其他数据

我想知道当我明确设置字符串的格式时为什么会发生这种情况,然后我该怎么做才能解决这个问题?

谢谢

正如@Hive7 所说,Oracle 正在'helpfully' 就如何将您的字符串与格式模型进行匹配做出最佳猜测。正如 the documentation 中所说:

Oracle Database converts strings to dates with some flexibility.

您可以通过添加 FX 格式修饰符告诉它不要这样做,这都会使您的转换失败并显示“ORA-01861:文字与格式字符串不匹配”,因为它们不匹配t.

由于灵活性以及 Oracle 尝试计算(或猜测)字符串的哪些位的含义,您会得到不同的结果。第一个字符串被解释为 YYMMDDHH24MISS,而第二个字符串被解释为 YYYYMMDDHH24MI。

210408092508
YYMMDDHHMISS => 2021-04-08 09:25:08
      24

210408091706
YYYYMMDDHHMI => 2021-08-09 17:06:00
        24

这种差异是因为 17 是小时元素的有效数字,但 25 不是。

210408092508
YYYYMMDDHHMI => ORA-01850: hour must be between 0 and 23
        24

它似乎更喜欢假设一个 4 位数的年份,这并非不合理,即使这实际上意味着忽略秒数。但在第一个字符串中,当它看到 25 时,它会丢弃可能的完整格式模型,并且在列表下方的某处是一个具有两位数年份的年份 - 这会改变月、日、小时、分钟和秒的解释方式.

If I set HH12, its working well

对于这些样本值,它的工作更加一致;但还是不太好。如果你这样做,那么 17 现在也不再有效(ORA-01849:小时必须在 1 和 12 之间),因此该格式再次被丢弃,并且它采用与小时相同的两位数年份版本25. 但是如果你尝试使用 1 到 12 之间的小时值,你会再次得到错误的结果。更高的 minute/second 值也会给出不同的结果。

正如@Hive7 所说,处理此问题的正确方法是使用正确的匹配格式模型:

select to_date('210408092508', 'RRMMDDHH24MISS') from dual;

2021-04-08 09:25:08

select to_date('210408091706', 'RRMMDDHH24MISS') from dual;

2021-04-08 09:17:06

我已经从 YY 切换到 RR,因为如果您真的必须处理两位数的年份值,通常更可取,但您可能有理由使用 YY。这里的结果是一样的。

db<>fiddle demo

您的格式掩码与您的数据不匹配。此外,您的数据使用的是两位数年份,这是......呃; Y2K 真的是一回事 - 20 年后没有人应该使用 2 位数年份!

这是一个使用您的格式掩码的演示,以及其他两个 - 更好的 - 格式掩码:

WITH your_data AS (SELECT '210408092508' date_str FROM dual UNION ALL
                   SELECT '210408091706' date_str FROM dual UNION ALL
                   SELECT '510408091706' date_str FROM dual)
SELECT date_str,
       to_date(date_str, 'yy/mm/dd HH24:MI:SS') your_dt,
       to_date(date_str, 'yymmddhh24miss') dt1,
       to_date(date_str, 'rrmmddhh24miss') dt2
FROM   your_data;


DATE_STR     YOUR_DT             DT1                 DT2
------------ ------------------- ------------------- -------------------
210408092508 08/04/2021 09:25:08 08/04/2021 09:25:08 08/04/2021 09:25:08
210408091706 09/08/2104 17:06:00 08/04/2021 09:17:06 08/04/2021 09:17:06
510408091706 09/08/5104 17:06:00 08/04/2051 09:17:06 08/04/1951 09:17:06

在这里您可以看到 Oracle 对如何读取以不同格式传入的字符串的猜测与它被告知期望的格式有很大的不同,这与您给它正确的信息来处理有很大不同!

我提供了两种可能的格式掩码供您使用,具体取决于您希望如何处理日期 - YY 会猜测年份的前两位数字应该是当年的数字,而 RR如果年份是00-49,则猜测年份的前两位为当前年份,否则选择一百年前的年份的前两位。

然而,如果你对数据有任何发言权,让它使用四位数的年份,然后 Oracle 根本不需要猜测,因为你可以在你的格式掩码中使用 YYYY .