2012-07-06 122 views
1

我試圖確定ISO 8601格式中給定的每週給定日期(即YYYY-W ##)的日期。我的最終目標是將ISO 8601星期日期轉換爲ISO 8601日曆日期。我需要在TSQL中執行此操作(我正在使用SQL Server 2005),我不確定是否有任何內置於SQL Server 05的內容允許這樣做,但它有助於查看另一種語言的示例或在一般的僞ISO8601轉換週日期到日曆日期

UPDATE:

很抱歉,如果我的問題的結構混亂。基本上,我有一個ISO8601星期日期,我試圖轉換爲ISO8601日曆日期。

例子(從Wikipedia

ISO 8601週日期:2012-W02(YYYY-W ##)

轉換爲...

ISO 8601日曆日期:2012-01- 09(YYYY-MM-DD)

由於在我的星期日期示例中未提供星期幾組件,因此可以假定星期的第一天。

+0

能否請你展示一些樣品的輸入/輸出(確保包括在今年的前幾個星期爲天以及一些兩位數週)? ISO 8601星期日期如何存儲? – 2012-07-06 20:55:07

+0

每週第一天的價值就足夠了。同樣,由於沒有提供組件,因此要求允許假定第一天。 – kingrichard2005 2012-07-06 21:09:32

+0

「2012-W02」不應該產生「2012-01-08」嗎?你能證明哪一條規則使它成爲第9名? – 2012-07-06 21:23:04

回答

2

請嘗試以下解決方案:

declare 
    @date varchar(10) 
    ,@convertedDate datetime 
    ,@wk int 
    ,@yr int 

set @date = '2012-W02'; 

set @yr = parsename(replace(@date, '-W', '.'), 2) 
set @wk = parsename(replace(@date, '-W', '.'), 1) 

set @convertedDate = convert(varchar(10), dateadd(week, @wk, dateadd (year, @yr-1900, 0)) - 5 - datepart(dw, dateadd (week, @wk, dateadd (year, @yr-1900, 0))), 121) 

select 
    'Year' = @yr 
    ,'Week' = @wk 
    ,'Date' = @convertedDate 

輸出:

----------------------------------------- 
| Year | Week |   Date   | 
----------------------------------------- 
| 2012 | 2 | 2012-01-09 00:00:00.000 | 
----------------------------------------- 
+0

@AaronBertrand - 嗯,奇怪。我展示'2012年12月24日',而不是'2012年12月18日'。 – RobB 2012-07-06 21:42:54

+0

是的,我的壞,我有錯誤的DATEFIRST設置。 – 2012-07-06 21:43:44

+0

......這個問題提出了一個很好的觀點,你應該從備用的'DATEFIRST'設置中隔離這個查詢。 – 2012-07-06 21:51:59

0

鑑於從CREATE FUNCTION documentation適當的ISO一週功能(有一些調整,對所有的壞習慣都在那裏,恕我直言):

CREATE FUNCTION dbo.ISOWeek 
( 
    @dt SMALLDATETIME 
) 
RETURNS TINYINT 
AS 
BEGIN 
    DECLARE @ISOweek TINYINT; 

    SET @ISOweek = DATEPART(WEEK, @dt) + 1 
     -DATEPART(WEEK, RTRIM(YEAR(@dt)) + '0104'); 

    IF @ISOweek = 0 
    BEGIN 
     SET @ISOweek = dbo.ISOweek 
     ( 
      RTRIM(YEAR(@dt)-1)+'12'+RTRIM(24 + DAY(@dt)) 
     ) + 1; 
    END 

    IF MONTH(@dt) = 12 AND DAY(@dt) - DATEPART(DAYOFWEEK, @dt) >= 28 
    BEGIN 
     SET @ISOweek = 1; 
    END 

    RETURN(@ISOweek); 
END 
GO 

我們可以創建一個表是這樣的:

CREATE TABLE dbo.ISOWeekCalendar 
(
    [Date] SMALLDATETIME PRIMARY KEY, 
    ISOWeekNumber TINYINT, 
    [Year] INT, 
    ISOWeek CHAR(8) 
); 
CREATE UNIQUE INDEX iw ON dbo.ISOWeekCalendar(ISOWeek); 

我們可以從年中的任何範圍的數據來填充它,這裏採用ISO周1-52爲2000至29年:

DECLARE @StartDate SMALLDATETIME, 
     @EndDate SMALLDATETIME; 

SELECT @StartDate = '20000102', 
     @EndDate = '20291229'; 

INSERT dbo.ISOWeekCalendar([Date]) 
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate)+1) n 
= DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY s1.[object_id])-1, @StartDate) 
FROM sys.all_objects AS s1 
CROSS JOIN sys.all_objects AS s2 
ORDER BY s1.[object_id]; 

現在我們可以更新數據。

-- delete all non-Mondays: 
SET DATEFIRST 1; 
DELETE dbo.ISOWeekCalendar WHERE DATEPART(WEEKDAY, [Date]) <> 1; 

-- put the proper ISO week number: 
UPDATE dbo.ISOWeekCalendar SET ISOWeekNumber = dbo.ISOWeek([Date]); 

-- put the year: 
UPDATE dbo.ISOWeekCalendar SET [Year] = DATEPART(YEAR, [Date]); 

-- update to the correct year for fringe days: 
UPDATE dbo.ISOWeekCalendar SET [Year] = [Year] + 1 
    WHERE ISOWeekNumber = 1 AND MONTH([Date]) = 12; 

-- finally, build the calculated value for YYYY-W##: 
UPDATE dbo.ISOWeekCalendar 
    SET ISOWeek = RTRIM([Year]) + '-W' + RIGHT('0' + RTRIM(ISOWeekNumber), 2); 

請注意,以上只需要做一次。現在,我們可以運行一個非常簡單的查詢給予我們的輸入:

SELECT [Date] FROM dbo.ISOWeekCalendar WHERE ISOWeek = '2012-W02'; 

結果:

Date 
------------------- 
2012-01-09 00:00:00 

我們甚至可以創建一個函數,這是否:

CREATE FUNCTION dbo.ISOWeekDate(@ISOWeek CHAR(8)) 
RETURNS SMALLDATETIME 
WITH SCHEMABINDING 
AS 
BEGIN 
    RETURN (SELECT [Date] FROM dbo.ISOWeekCalendar 
    WHERE ISOWeek = @ISOWeek); 
END 
GO 

而且一個函數去另一種方式:

CREATE FUNCTION dbo.ISOWeekFromDate(@Date SMALLDATETIME) 
RETURNS CHAR(8) 
WITH SCHEMABINDING 
AS 
BEGIN 
    RETURN (SELECT TOP (1) ISOWeek FROM dbo.ISOWeekCalendar 
    WHERE [Date] <= @Date 
    ORDER BY [Date] DESC); 
END 
GO 

闕RY:

SELECT dbo.ISOWeekDate('2012-W02'), dbo.ISOWeekFromDate('20120110'); 

結果:

------------------- -------- 
2012-01-09 00:00:00 2012-W02 

是的,它是一個更小的前期工作比複雜的查詢,但我更喜歡易用性和更清晰的查詢語義。

+1

我不認爲他正在試圖從'datetime'中找到週數。他正試圖將「ISO 8601星期日期」轉換爲ISO 8601日曆日期。 – 2012-07-06 20:53:39