【问题标题】:ISO8601 Convert Week Date to Calendar DateISO8601 将周日期转换为日历日期
【发布时间】:2012-07-07 08:03:25
【问题描述】:

我正在尝试以 ISO 8601 格式确定给定的星期日期(即 YYYY-W##)。我的最终目标是将 ISO 8601 周日期转换为 ISO 8601 日历日期。我需要在 TSQL 中执行此操作(我正在使用 SQL Server 2005),我不确定 SQL Server 05 中是否已经内置了允许此操作的任何内容,但它有助于查看另一种语言的示例或在通用伪代码中

更新:

对不起,如果我的问题的结构令人困惑。基本上,我有一个 ISO8601 周日期,我正在尝试将其转换为 ISO8601 日历日期。

示例(来自Wikipedia

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

转换为...

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

由于在我的星期日期示例中没有给出星期部分,因此可以假定为一周的第一天。

【问题讨论】:

  • 您能否展示一些示例输入/输出(确保包括一年中前几周的天数以及一些两位数的星期)?当前如何存储 ISO 8601 周日期?
  • 一周第一天的值就足够了。同样,由于未提供日期组件,因此要求允许假定为第一天。
  • 不应该2012-W022012-01-08?你能说明哪条规则使它成为第 9 条吗?
  • ISO 8601 标准将一周指定为从星期一开始到星期日结束。因此,如果我的示例是 2012 年的第二周,那么那个星期一就是第 9 周。
  • 知道了,更新了我的答案。

标签: sql sql-server datetime date


【解决方案1】:

鉴于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);

我们可以用任何年份的数据填充它,这使用 2000 - 2029 年的 ISO 第 1-52 周:

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

查询:

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

结果:

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

是的,它比复杂的查询需要更多的前期工作,但我更喜欢易用性和更清晰的查询语义。

【讨论】:

  • 我不认为他试图从datetime 中找到周数。他正在尝试将“ISO 8601 周日期转换为 ISO 8601 日历日期”。
【解决方案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 |
-----------------------------------------

【讨论】:

  • @AaronBertrand - 嗯,奇怪。我显示的是“2012-12-24”,而不是“2012-12-18”。
  • ...提出了一个很好的观点,您应该将此查询与备用 DATEFIRST 设置隔离开来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-08
  • 1970-01-01
  • 2017-10-02
  • 2012-06-15
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
相关资源
最近更新 更多