为什么星期二有时候会变成星期天?

3

select to_char(sysdate, 'DAY') from dual; 将正确返回 TUESDAY

但是,当我在同一天运行select to_char(to_date(sysdate, 'DD-MON-YYYY'), 'DAY') from dual;时,为什么会返回SUNDAY

由于当我尝试运行select to_date('27-OCT-15', 'D') from dual;时出现了一个ORA-01835: day of week conflicts with Julian date错误,因此我必须将传递到函数的日期转换为DD-MON-YYYY格式,然后再尝试从中获取星期几。 有人知道为什么会出现这种不一致吗?

这里有一个更好的例子(我已经替换了示例中的列和表名)。

SELECT  DATE, 
    to_char(DATE, 'DAY'), 
    to_char(to_date(DATE, 'DD-MON-YYYY'), 'DAY') 
FROM    DATES
ORDER BY DATE;

结果如下:
DATE      TO_CHAR(D TO_CHAR(T
--------- --------- ---------
19-OCT-15 MONDAY    SATURDAY
19-OCT-15 MONDAY    SATURDAY
19-OCT-15 MONDAY    SATURDAY
21-OCT-15 WEDNESDAY MONDAY
21-OCT-15 WEDNESDAY MONDAY
02-NOV-15 MONDAY    SATURDAY
02-NOV-15 MONDAY    SATURDAY

因为 to_date 返回的是一周中的第一天 - 星期日 - Nguyễn Hải Triều
转换以下关于编程的内容,从英文到中文。仅返回翻译好的文本:to_date(sysdate?真的吗?sysdate已经是一个日期,你不需要将日期强制转换为日期。 - Stawros
发布一些你正在传递的数据。 - anwaar_hell
还要使用以下查询检查日期格式: select * from nls_session_parameters where parameter = 'NLS_DATE_FORMAT'; - anwaar_hell
1
@Dr R Dizzle Sysdate或日期,无所谓。TO_DATE将CHAR、VARCHAR2、NCHAR或NVARCHAR2数据类型的字符转换为DATE数据类型的值。如果您放置日期数据类型,则应准备好隐式类型转换和nls设置可能带来的任何问题。 - Stawros
显示剩余5条评论
3个回答

10

to_date(sysdate

不要在 DATE 上使用 TO_DATE。它会强制 Oracle 进行以下操作:

  • 首先将其转换为字符串
  • 然后再将其转换回日期

这是基于特定于区域设置的 NLS 设置。您需要使用 TO_DATE 将文字转换为日期。对于日期算术运算,请保留日期不变。

SQL> SELECT to_char(to_date('27-OCT-2015', 'DD-MON-YYYY'), 'DAY') FROM dual;

TO_CHAR(T
---------
TUESDAY

根本原因

SQL> alter session set nls_date_format='DD-MON-RR';

Session altered.

SQL> select to_char(to_date(sysdate,'DD-MON-YYYY'),'DD-MON-YYYY') from dual;

TO_CHAR(TO_
-----------
27-OCT-0015

所以这一年被改为0015。因为你将已经是DATE类型的值从RR格式转换为YYYY格式。

现在,0015年10月27日是一个星期天

SQL> select to_char(to_date('27-OCT-0015','DD-MON-YYYY'),'DAY') FROM DUAL;

TO_CHAR(T
---------
SUNDAY

为了理解内部行为,让我们看一个小演示:
SQL> SET AUTOT ON EXPLAIN
SQL> SELECT * FROM dual WHERE SYSDATE = TO_DATE(SYSDATE);

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 3752461848

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     2 |     2   (0)| 00:00:01 |
|*  1 |  FILTER            |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL| DUAL |     1 |     2 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(SYSDATE@!=TO_DATE(TO_CHAR(SYSDATE@!)))

你看到了 filter(SYSDATE@!=TO_DATE(TO_CHAR(SYSDATE@!)))。 这个过滤器发生了什么?
SYSDATE上使用TO_DATE会在内部被解释为TO_DATE(TO_CHAR(SYSDATE@!))
所以,首先应用TO_CHAR,它的格式取决于您本地的NLS_DATE_FORMAT,然后将其转换回DATE
更新 基于下面的评论...
使用:
  • TO_DATE 将日期字面值(字符串)转换为DATE。由于TO_DATE是NLS相关的,我更喜欢在每个语句级别明确指定格式和NLS_DATE_LANGUAGE
SQL> alter session set nls_date_format='YYYY-MM-DD';
Session altered.
SQL> SELECT TO_DATE('27-OCT-2015', 'DD-MON-YYYY','NLS_DATE_LANGUAGE=ENGLISH') 2 FROM DUAL;
TO_DATE('2 ---------- 2015-10-27
如果您依赖客户端的NLS设置,那么对于在不同位置使用不同NLS的人可能无法正常工作。
SQL> alter session set NLS_DATE_LANGUAGE='FRENCH';

Session altered.

SQL> SELECT TO_DATE('27-OCT-2015', 'DD-MON-YYYY') from dual;
SELECT TO_DATE('27-OCT-2015', 'DD-MON-YYYY') from dual
               *
ERROR at line 1:
ORA-01843: not a valid month
  • TO_CHAR函数将日期转换为字符串,以所需格式显示,使用适当的格式模板

不要反过来做。


在这种情况下,为什么当我运行select to_char('27-OCT-2015', 'DAY') FROM DUAL;或者select to_char('27-OCT-2015', 'DAY') FROM DUAL;时会出现ORA-01722: invalid number的错误?没有使用to_date函数,它对我来说是无法工作的。 - Dr R Dizzle
1
为什么要使用TO_CHAR将字符串转换回字符串?这样做基本上是错误的。您必须使用TO_DATE将其转换为日期。 - Lalit Kumar B
1
你手头有一个日期类型的变量还是字符串类型的变量? - Lalit Kumar B
他无法理解VARCHAR2和DATE是不同的数据类型。 - Stawros
1
@DrRDizzle 所以,如果你得到一个日期,只需使用 SELECT TO_CHAR(passed_date, 'DAY') into some_variable FROM DUAL; 如果不是日期而是字符串,则使用Boneist上面提供的查询。 - Lalit Kumar B
显示剩余5条评论

5

由于sysdate已经是一个日期,因此该表达式:

to_date(sysdate, 'DD-MON-YYYY')

将日期转换为字符串并使用当前的 NLS_DATE_FORMAT, NLS_TERRITORYNLS_LANGUAGE 值进行解析,这既是多余的,也很脆弱。垃圾进,垃圾出。

一些提示:

  1. 没有充分的理由不要将日期转化为字符串。
  2. 在转换过程中,始终指定格式。

我也不太喜欢使用月份名称作为交换格式;我认为数字更不容易出错。


2
当您的 nls_date formatDD-MON-RR 时,转换如下:
to_date(sysdate,'dd-mon-yyyy')

执行该操作将生成字符串27-10-0015(其中sysdate为2015-10-27)。

公元15年的10月27日是星期天 - 在当时不存在的格里高利历中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接