当我知道ISO 8601中的周数时,如何获得一周的第一天

时间:2015-12-14 12:47:00

标签: sql-server-2008 datetime iso8601

我知道年份和周数,例如

year = 2016
week = 1

根据 ISO 8601 ,如何从中获取一周的第一天

我在表datetime2[0]日期类型中生成默认值2015-12-28,但一周的第一天应为2016-01-04

任何想法?

3 个答案:

答案 0 :(得分:2)

给定任何日期,此SQL将返回ISO周:SELECT DATEPART(ISO_WEEK, @date)。下面是计算该ISO周的第一个日期的丑陋(但非常有效)的方法:

DECLARE @date DATETIME = '2012-09-16'
SELECT (
    CASE DATEPART(ISO_WEEK, @date) 
        WHEN DATEPART(ISO_WEEK, DATEADD(DAY,-6,@date)) THEN DATEADD(DAY,-6,@date)
        WHEN DATEPART(ISO_WEEK, DATEADD(DAY,-5,@date)) THEN DATEADD(DAY,-5,@date)
        WHEN DATEPART(ISO_WEEK, DATEADD(DAY,-4,@date)) THEN DATEADD(DAY,-4,@date)
        WHEN DATEPART(ISO_WEEK, DATEADD(DAY,-3,@date)) THEN DATEADD(DAY,-3,@date)
        WHEN DATEPART(ISO_WEEK, DATEADD(DAY,-2,@date)) THEN DATEADD(DAY,-2,@date)
        WHEN DATEPART(ISO_WEEK, DATEADD(DAY,-1,@date)) THEN DATEADD(DAY,-1,@date)
        ELSE DATEADD(DAY,0,@date)
    END
) FirstDayOfISOWeek

现在,这不是我最漂亮的作品,但它是通过反复试验而起作用的-测试多少周前的ISO周仍然是相同的,然后返回这一天。

答案 1 :(得分:1)

我认为处理业务数据最好的办法是将其存储在表格中。 ISO周是业务数据。如果你有一个看起来像这样的表

calendar_date   iso_year    iso_week
--
2016-01-01      2015        53
2016-01-02      2015        53
2016-01-03      2015        53
2016-01-04      2016        1
2016-01-05      2016        1
2016-01-06      2016        1
2016-01-07      2016        1

然后回答问题"什么是2016年ISO第1周的第一个日期?"简化为此。

select min(calendar_date) 
from calendar
where iso_year = 2016;
  and iso_week = 1;

以下是如何创建这样的表格。 (未经过严格测试。)

create table calendar (
  calendar_date date primary key,
  iso_year integer not null,
  iso_week integer not null,
  check (iso_week = datepart(iso_week, calendar_date)),
  check (iso_year = year(dateadd(wk, datediff(d, 0, calendar_date) / 7, 3)))
);

此代码将填充它。

declare @start_date date;
select @start_date = '2016-01-01';
declare @end_date date;
select @end_date = '2050-12-31';

declare @next_date date;
select @next_date = (select coalesce(max(calendar_date), @start_date) from calendar);

while @next_date <= @end_date 
begin
    begin transaction;
    insert into calendar values (@next_date , year(dateadd(wk, datediff(d, 0, @next_date) / 7, 3)), datepart(iso_week, @next_date));
    commit;
    select @next_date = dateadd(d, 1, @next_date);
end

小心控制权限。很少有人应该拥有插入,更新或删除权限。

答案 2 :(得分:0)

CLR功能也是一种可能性

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Globalization;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static DateTime fnFirstDateOfWeekISO8601(int year, int weekOfYear)
    {
        DateTime jan1 = new DateTime(year, 1, 1);
        int daysOffset = DayOfWeek.Thursday - jan1.DayOfWeek;

        DateTime firstThursday = jan1.AddDays(daysOffset);
        var cal = CultureInfo.CurrentCulture.Calendar;
        int firstWeek = cal.GetWeekOfYear(firstThursday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);

        var weekNum = weekOfYear;
        if (firstWeek <= 1)
        {
            weekNum -= 1;
        }
        var result = firstThursday.AddDays(weekNum * 7);
        return result.AddDays(-3);
    }
}