MS Access 获取 ISO 标准周数

MS Access get ISO standard week number

我很惊讶我在网上找不到任何现有的解决方案,但我只需要一个 SQL 函数,它 return 是 ISO 标准周数(即一周的开始1 始终是一年中的第一个星期一)。

None 的 DatePart 函数选项始终 return 正确的结果。我曾考虑过 "vbFirstFourDays - Start with the first week that has at least four days in the new year." 选项,但在今天(1 月 12 日)return 的第 3 周而不是第 2 周对其进行了测试(我的表达方式是 DatePart("ww",Now(),2)

今年 ISO 第 1 周从 1 月 4 日开始,明年是 1 月 2 日,去年是 1 月 5 日。

非常感谢

firstdayofweek 使用vbMonday 时,DatePart 函数确实几乎* 正确计算了ISO-8601 周数 参数和 vbFirstFourDays 用于 firstweekofyear 参数,例如

DatePart("ww", Date(), vbMonday, vbFirstFourDays)

或者,直接在 Access 查询中使用时

DatePart("ww", Date(), 2, 2)

* 请注意,记录在 here 中的错误显然从未被修复,因此接下来的星期一在 21st世纪被报告为第 53 周,而根据 ISO-8601,它们应该在下一年的第 1 周:

2003-12-29
2007-12-31
2019-12-30
2031-12-29
2035-12-31
2047-12-30
2059-12-29
2063-12-31
2075-12-30
2087-12-29
2091-12-31

继 Gord Thompson 之后,Microsoft 提供了一种解决方法,可以在所有情况下 returns 正确的 ISO 周。它只是将第 53 周更改为第 1 周。只需将其放在 VBA 模块中,然后您就可以使用 Excel/Access.

中的函数
Public Function ISOWeek(MyDate As Date) As Integer

      ISOWeek = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
      If ISOWeek > 52 Then
         If Format(MyDate + 7, "ww", vbMonday, vbFirstFourDays) = 2 Then ISOWeek = 1
      End If

End Function

ISO 周数比 DatePart 返回的 2 周数字存在更多问题。

1 月 1 日的某个星期五应该是上一年的第 53 周

12 月 31 日的星期一应该是下一年的第 1 周

欧洲的许多企业使用四位数字同时显示年份和星期。在这些情况下:

星期五 #01/01/2021# 应显示为周“2153”

星期一 #12/31/2018# 应显示为周“1901”

我围绕 DatePart 函数创建了 2 个包装器,以添加正确的年份并显示正确的周数,以防 DatePart 出错。

Public Function ISO_YYWW(dat As Date) As String ' ISO 8601 / NEN 2772
    Dim ww As Integer
    ww = CInt(ISO_WW(dat))
    If ww >= 52 And Val(Format(dat, "ww")) <= 2 Then
       ISO_YYWW = Format(((Year(dat) - 1) Mod 100), "00") & Format(ww, "00")
    ElseIf ww = 1 And Month(dat) = 12 Then
       ISO_YYWW = Format(((Year(dat) + 1) Mod 100), "00") & Format(ww, "00")
    Else
       ISO_YYWW = Format(dat, "YY") & Format(ww, "00")
    End If
End Function
Public Function ISO_WW(dat As Date) As String ' ISO 8601 / NEN 2772
    If Format(dat, "DD-MM") = "31-12" And DatePart("W", dat, vbMonday, vbFirstFourDays) = 1 Then ' 31-dec on a monday
       ISO_WW = "01"
    ElseIf Format(dat, "DD-MM") = "30-12" And DatePart("W", dat, vbMonday, vbFirstFourDays) = 1 Then ' 30-dec on a monday
       ISO_WW = "01"
    ElseIf Format(dat, "DD-MM") = "29-12" And DatePart("W", dat, vbMonday, vbFirstFourDays) = 1 Then ' 29-dec on a monday
       ISO_WW = "01"
    Else
       ISO_WW = Format(DatePart("ww", dat, 2, 2), "00")
    End If
End Function

我从 1970 年测试到 2021 年,使用这段代码没有发现问题

Sub test()
    Dim dat As Date, yy As Integer, f As Integer
    f = FreeFile
    Open CodeDb.Name & ".txt" For Output As #f
    Print #f, "date", "day", "ISO_WW / DOW", "ISO_YYWW"
    For yy = 1970 To 2021
        For dat = CDate("31/12/" & yy) - 7 To CDate("31/12/" & yy) + 7
            If ISO_WW(dat) >= 52 Or ISO_WW(dat) = "53" Or Val(ISO_WW(dat)) = 1 Then
               Print #f, Format(dat, "yyyy-mm-dd"), Format(dat, "DDD"), ISO_WW(dat) & " / " & DatePart("W", dat, vbMonday, vbFirstFourDays), ISO_YYWW(dat)
            End If
        Next dat
        Print #f, ""
    Next yy
    Close #f
End Sub