我自己找到了答案,TO_CHAR(date,'IW') 格式没有用,因为根据这个标准 (ISO) 一年中的第一周可以在新年之后开始,也可以在新年之前开始(看看TO_CHAR(TO_DATE('2014-12-31','YYYY-MM-DD'),'IW')=01 第一个属于下一年的那一周!)
| DAY | WW | IW | MY
===========+=====+====+====+====
2014-12-28 | SUN | 52 | 52 | 52
2014-12-29 | MON | 52 | 01 | 53
2014-12-30 | TUE | 52 | 01 | 53
2014-12-31 | WED | 52 | 01 | 53
2015-01-01 | THU | 53 | 01 | 53
... | ... | .. | .. | ..
2016-12-31 | THU | 53 | 53 | 01
2016-01-01 | FRI | 01 | 53 | 01
2016-01-02 | SAT | 01 | 53 | 01
2016-01-03 | SUN | 01 | 53 | 01
2016-01-04 | MON | 01 | 01 | 02
2016-01-05 | TUE | 01 | 01 | 02
2016-01-06 | WED | 01 | 01 | 02
2016-01-07 | THU | 01 | 01 | 02
2016-01-08 | FRI | 02 | 01 | 02
逻辑很简单,让我们看看一年中的第一天及其与星期一的偏移量。如果当前日期大于第一天偏移量,则周数应增加 1。
第一天的数量(从星期一偏移)计算如下:
TO_CHAR(TO_DATE(TO_CHAR(dt,'YYYY')||'0101','YYYYMMDD'),'D'))
所以最终的SQL语句是
WITH DATES AS
(
SELECT DATE '2014-12-25' + LEVEL -1 dt FROM DUAL CONNECT BY LEVEL <= 500
)
SELECT dt,TO_CHAR(dt,'DY') DAY,TO_CHAR(dt,'WW') WW,TO_CHAR(dt,'IW') IW,
CASE WHEN TO_CHAR(dt,'D')<TO_CHAR(TO_DATE(TO_CHAR(dt,'YYYY')||'0101','YYYYMMDD'),'D') THEN
LPAD(TO_CHAR(dt,'WW')+1,2,'0')
ELSE
TO_CHAR(dt,'WW')
END MY
FROM dates
当然,可以为此目的创建一个函数,例如:
CREATE OR REPLACE FUNCTION WorkingWeek(dt IN DATE) RETURN CHAR
IS
BEGIN
IF(TO_CHAR(dt,'D')<TO_CHAR(TO_DATE('0101'||TO_CHAR(dt,'YYYY'),'DDMMYYYY'),'D')) THEN
RETURN LPAD(TO_CHAR(dt,'WW')+1,2,'0');
ELSE
RETURN TO_CHAR(dt,'WW');
END IF;
END WorkingWeek;
/