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

How to get first day of week when i know week number in ISO 8601

例如我知道年份和周数

year = 2016
week = 1

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

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

有什么想法吗?

我认为处理业务数据的最佳方式是将其存储在 table 中。 ISO 周是业务数据。如果你有一个看起来像这样的 table

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

然后回答问题 "Whats the first date of ISO week 1 in 2016?" 简化为这个。

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

下面是创建这样一个 table 的方法。 (未经严格测试。)

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

谨慎控制权限。很少有人应该有插入、更新或删除权限。

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);
    }
}

#更新 2020-09-13

Microsoft 已经 class 为此 https://docs.microsoft.com/en-us/dotnet/api/system.globalization.isoweek?view=netcore-3.1 所以 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)
        {
           return System.Globalization.ISOWeek.ToDateTime(year, weekOfYear, DayOfWeek.Monday); 
        }
    }

给定任何日期,此 SQL 将 return ISO-week:SELECT DATEPART(ISO_WEEK, @date)。下面是一个丑陋的(但非常有效)one-liner 来计算第一个日期 ISO-week:

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-week 仍然相同,然后 return 那天。