2012-07-06 128 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