【问题标题】:Get the ISO 8601 Week of Year of a given date in Powershell在 Powershell 中获取给定日期的 ISO 8601 周
【发布时间】:2017-10-11 14:41:19
【问题描述】:

在搜索如何在 PowerShell 中获取 ISO 8601 Week of Year 时,我偶然发现了 this question for C#

尽量不要用 PowerShell 代码来解决这个问题,下面是我的 Powershell 端口。 (基于answer by user6887101

如果有人提出更好的解决方案,我将暂时保留这个“不接受”。

【问题讨论】:

    标签: powershell date datetime iso8601


    【解决方案1】:

    user6887101提到并详细解释here,伪算法是:

    ISO 8601 周从 星期一 开始,到 星期日 结束。

    1. 对于任何给定日期,找到与给定日期同一周的星期四 日期。例如。:
      • 如果原始日期是Sunday, January 1st XXXX,则查找Thursday, December 29th XXXX-1
      • 如果原始日期是Monday, December 31st XXXX,请查找Thursday, January 3rd XXXX+1
    2. Year of the ISO 8601 Week 是包含 Thursday found in step 1 的那个(例如:XXXX-1XXXX+1
    3. ISO 8601 Week numberyear from step 2 中的number of Thursdays(直到并包括发现的Thursday 本身)
    function Get-ISO8601Week (){
    # Adapted from https://stackoverflow.com/a/43736741/444172
      [CmdletBinding()]
      param(
        [Parameter(
          ValueFromPipeline                =  $true,
          ValueFromPipelinebyPropertyName  =  $true
        )]                                           [datetime]  $DateTime
      )
      process {
        foreach ($_DateTime in $DateTime) {
          $_ResultObject   =  [pscustomobject]  @{
            Year           =  $null
            WeekNumber     =  $null
            WeekString     =  $null
            DateString     =  $_DateTime.ToString('yyyy-MM-dd   dddd')
          }
          $_DayOfWeek      =  $_DateTime.DayOfWeek.value__
    
          # In the underlying object, Sunday is always 0 (Monday = 1, ..., Saturday = 6) irrespective of the FirstDayOfWeek settings (Sunday/Monday)
          # Since ISO 8601 week date (https://en.wikipedia.org/wiki/ISO_week_date) is Monday-based, flipping Sunday to 7 and switching to one-based numbering.
          if ($_DayOfWeek  -eq  0) {
            $_DayOfWeek =    7
          }
    
          # Find the Thursday from this week:
          #     E.g.: If original date is a Sunday, January 1st     , will find     Thursday, December 29th     from the previous year.
          #     E.g.: If original date is a Monday, December 31st   , will find     Thursday, January 3rd       from the next year.
          $_DateTime                 =  $_DateTime.AddDays((4  -  $_DayOfWeek))
    
          # The above Thursday it's the Nth Thursday from it's own year, wich is also the ISO 8601 Week Number
          $_ResultObject.WeekNumber  =  [math]::Ceiling($_DateTime.DayOfYear    /   7)
          $_ResultObject.Year        =  $_DateTime.Year
    
          # The format requires the ISO week-numbering year and numbers are zero-left-padded (https://en.wikipedia.org/wiki/ISO_8601#General_principles)
          # It's also easier to debug this way :)
          $_ResultObject.WeekString  =  "$($_DateTime.Year)-W$("$($_ResultObject.WeekNumber)".PadLeft(2,  '0'))"
          Write-Output                  $_ResultObject
        }
      }
    }
    

    快速测试:

    PS C:\>  Get-Date  |  Get-ISO8601Week
    
    Year WeekNumber WeekString DateString
    ---- ---------- ---------- ----------
    2017         41 2017-W41   2017-10-11   Wednesday
    

    在广泛的输入范围内测试正确的结果:

    #<# Test Get-ISO8601Week (You can manually check accuracy @ https://planetcalc.com/1252/)
    #   Tested on $PSVersionTable.PSVersion :
    #       5.1.15063.502
    
        "Week starts on:    $([System.Globalization.DateTimeFormatInfo]::CurrentInfo.FirstDayOfWeek)"
    #   Test dates from 2000-01-01 (730119) to 2020-12-31 (737789)
    #   To get the 'serial day number' for a given date, use:
    #   (Get-Date   -Date '2020-12-31').Ticks   /   [timespan]::TicksPerDay
        $WeekOfYearObjectGroupList      =   730119..737789  |   ForEach-Object  -Process {[datetime]::new(($_ * [timespan]::TicksPerDay))}  |   Get-ISO8601Week |   Group-Object    -Property 'Year'
    
        '============================================================='
        foreach ($WeekOfYearObjectGroup in  $WeekOfYearObjectGroupList) {
            $WeekOfYearObjectGroup.Group  |  Where-Object  {$_.WeekNumber  -lt  1       }  |  Format-Table  -AutoSize
            $WeekOfYearObjectGroup.Group  |  Where-Object  {$_.WeekNumber  -in  1..2    }  |  Format-Table  -AutoSize
            '...........'
            $WeekOfYearObjectGroup.Group  |  Where-Object  {$_.WeekNumber  -in  52..53  }  |  Format-Table  -AutoSize
            $WeekOfYearObjectGroup.Group  |  Where-Object  {$_.WeekNumber  -gt  53      }  |  Format-Table  -AutoSize
        '============================================================='
        }
    #>
    

    引用@MSDN的“棘手”日期示例
    可以手动检查准确率@https://planetcalc.com/1252/

    <#  Sample of 'tricky' dates referenced @ https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/
        ...........
        2004         52 2004-W52   2004-12-26   Sunday
        2004         53 2004-W53   2004-12-27   Monday
        2004         53 2004-W53   2004-12-28   Tuesday
        2004         53 2004-W53   2004-12-29   Wednesday
        2004         53 2004-W53   2004-12-30   Thursday
        2004         53 2004-W53   2004-12-31   Friday
        2004         53 2004-W53   2005-01-01   Saturday
        2004         53 2004-W53   2005-01-02   Sunday
        =============================================================
        2005          1 2005-W01   2005-01-03   Monday
        2005          1 2005-W01   2005-01-04   Tuesday
        2005          1 2005-W01   2005-01-05   Wednesday
        2005          1 2005-W01   2005-01-06   Thursday
        2005          1 2005-W01   2005-01-07   Friday
        2005          1 2005-W01   2005-01-08   Saturday
        2005          1 2005-W01   2005-01-09   Sunday
        2005          2 2005-W02   2005-01-10   Monday
        ...........
    #>
    

    【讨论】:

    • 我对漂亮的描述和锻炼投了赞成票。注意事项:将其称为 Get-ISO8601(或 Get-ISO8601Week),因为您实际上返回了一个 object,其中 WeekOfYear 是一个属性((Get-Date | Get-ISO8601WeekOfYear).WeekOfYear 对我来说似乎是多余的)添加一个 WeekNumber 属性返回一个[Int],这使得比较更容易(更快):Where {$_.WeekOfYear -match '^[0-9]+-W0[0-2]$'} --> Where {$_.WeekNumber -le 2} 和操作。
    • @iRon 感谢您的建议。实施了一些更改。 ISO8601 比 Weeks 大,所以我保留了Get-ISO8601Week
    【解决方案2】:

    聚会有点晚了,但是...只是将这个答案留在这里,因为这个问题是我在寻找解决方案时偶然发现的第一件事。有一个更简单的方法:

    get-date -UFormat %V

    "{0:d1}" -f ($(Get-Culture).Calendar.GetWeekOfYear((Get-Date),[System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [DayOfWeek]::Monday))
    

    取决于您是否需要 ISO8601。

    【讨论】:

    • 优秀。是的,第一个不是 ISO8601,但第二个是。 '2004-12-31', '2005-01-01' | Get-Date -UFormat '%V' 产生 53、1。'2004-12-31', '2005-01-01' | % {[System.Globalization.GregorianCalendar]::new().GetWeekOfYear(([datetime] $_),[System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [DayOfWeek]::Monday)} 产生 53、53。ISO8601 的正确答案是 53、53。
    • 这里的第二个示例对于像 2007-12-31 这样的日期失败,其中一年的最后一周从星期一开始,因此实际上应该是下一年的第 1 周。我在下面的回答说明了这一点。
    【解决方案3】:
    $checkdate = Get-Date -date "2007-12-31"
    $dow=[int]($checkdate).dayofweek
    # if the day of week is before Thurs (Mon-Wed) add 3 since Thursday is the critical
    # day for determining when the ISO week starts.
    if ($dow -match "[1-3]") {$checkdate.addDays(3)}
    # Return the ISO week number
    $(Get-Culture).Calendar.GetWeekOfYear(($checkdate),[System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [DayOfWeek]::Monday)
    

    来自https://ss64.com/ps/get-date.html

    【讨论】:

      【解决方案4】:

      对于纯 Powershell: 文化和 UICulture 有问题 以该文化的语言打印一周的第一天:

      (Get-Culture).DateTimeFormat.DayNames[(Get-Culture).DateTimeFormat.FirstDayOfWeek.value__]
      maandag (which is My language AND My starting weekday
      
      (Get-uiCulture).DateTimeFormat.DayNames[(Get-Culture).DateTimeFormat.FirstDayOfWeek.value__]
      Monday (which is NOT My language BUT is My starting weekday
      
      (Get-uiCulture).DateTimeFormat.DayNames[(Get-uiCulture).DateTimeFormat.FirstDayOfWeek.value__]
      Sunday (which is NOT My language AND NOT My starting weekday
      
      (Get-Culture).DateTimeFormat.DayNames[(Get-uiCulture).DateTimeFormat.FirstDayOfWeek.value__]
      zondag (which is My language AND NOT My starting weekday
      

      然后是周数不符合 ISO8601 标准的问题。 日期 2012-12-31 应该是第 1 周,但它给出了 53。 因此,您必须为此实施某种解决方案。

      (Get-Culture).Calendar.GetWeekOfYear((Get-Date).AddDays(3*([int](Get-Date).DayOfWeek -in (1,2,3))), ((Get-Culture).DateTimeFormat.CalendarWeekRule), ((Get-Culture).DateTimeFormat.FirstDayOfWeek))
      

      用于测试:

      (Get-Culture).Calendar.GetWeekOfYear((Get-Date('2013-01-01')).AddDays(3*([int](Get-Date('2013-01-01')).DayOfWeek -in (1,2,3))), ((Get-Culture).DateTimeFormat.CalendarWeekRule), ((Get-Culture).DateTimeFormat.FirstDayOfWeek))
      (Get-Culture).Calendar.GetWeekOfYear((Get-Date('2012-12-31')).AddDays(3*([int](Get-Date('2012-12-31')).DayOfWeek -in (1,2,3))), ((Get-Culture).DateTimeFormat.CalendarWeekRule), ((Get-Culture).DateTimeFormat.FirstDayOfWeek))
      (Get-Culture).Calendar.GetWeekOfYear((Get-Date('2012-12-30')).AddDays(3*([int](Get-Date('2012-12-30')).DayOfWeek -in (1,2,3))), ((Get-Culture).DateTimeFormat.CalendarWeekRule), ((Get-Culture).DateTimeFormat.FirstDayOfWeek))
      

      这些应该给出 1,1,52(正确)而不是 1,53,52(不正确)。 或者(UICulture相关版本)

      (Get-UICulture).Calendar.GetWeekOfYear((Get-Date).AddDays(3*([int](Get-Date).DayOfWeek -in (0,1,2))), ((Get-UICulture).DateTimeFormat.CalendarWeekRule), ((Get-UICulture).DateTimeFormat.FirstDayOfWeek))
      

      用于测试:

      (Get-UICulture).Calendar.GetWeekOfYear((Get-Date('2013-01-01')).AddDays(3*([int](Get-Date('2013-01-01')).DayOfWeek -in (0,1,2))), ((Get-UICulture).DateTimeFormat.CalendarWeekRule), ((Get-UICulture).DateTimeFormat.FirstDayOfWeek))
      (Get-UICulture).Calendar.GetWeekOfYear((Get-Date('2012-12-31')).AddDays(3*([int](Get-Date('2012-12-31')).DayOfWeek -in (0,1,2))), ((Get-UICulture).DateTimeFormat.CalendarWeekRule), ((Get-UICulture).DateTimeFormat.FirstDayOfWeek))
      (Get-UICulture).Calendar.GetWeekOfYear((Get-Date('2012-12-30')).AddDays(3*([int](Get-Date('2012-12-30')).DayOfWeek -in (0,1,2))), ((Get-UICulture).DateTimeFormat.CalendarWeekRule), ((Get-UICulture).DateTimeFormat.FirstDayOfWeek))
      (Get-UICulture).Calendar.GetWeekOfYear((Get-Date('2012-12-29')).AddDays(3*([int](Get-Date('2012-12-29')).DayOfWeek -in (0,1,2))), ((Get-UICulture).DateTimeFormat.CalendarWeekRule), ((Get-UICulture).DateTimeFormat.FirstDayOfWeek))
      

      这些应该给出 1,1,1,52(正确)而不是 1,53,53,52(不正确)。 因为从工作日开始是星期日。

      您可以通过将 3* 替换为 0* 来测试不正确的结果。

      把它做成一个函数,你可以不给它参数一个日期或一个字符串:

      function Get-ISO8601Week {
          Param(
              $getISO8601Week = $(Get-Date)
          )
          If ($getISO8601Week.GetType() -eq [string]){
              $getISO8601Week = (Get-Date($getISO8601Week))
          }
          $getCulture = Get-Culture
          ($getCulture).Calendar.GetWeekOfYear(
              $getISO8601Week.AddDays(
                  3*([int]$getISO8601Week.DayOfWeek -in (0,1,2))
              ), ($getCulture.DateTimeFormat.CalendarWeekRule
              ), ($getCulture.DateTimeFormat.FirstDayOfWeek
              )
          )
      }
      
      Get-ISO8601Week $(Get-Date('2012-12-31'))
      1
      
      Get-ISO8601Week (Get-Date('2012-12-31'))
      1
      
      Get-ISO8601Week '2012-12-31'
      1
      

      当我写这篇文章时:

      Get-ISO8601Week
      4
      

      【讨论】:

      • 还是有故障所以我又做了一个。你可以在这篇文章中找到它。即使失败对我来说也不是问题。但我牢记在心,希望你能从我的错误中吸取教训。 ;-)
      【解决方案5】:

      我想我找到了一个相当健壮的实现。

       #ISO week
      $date = [DateTime] '2014-12-29'
      (Get-Culture).Calendar.GetWeekOfYear(($date).AddDays(-[Int] (($date).AddDays(-1)).DayOfWeek+3), 2, 1)
      
      #ISO year
      (Get-Culture).Calendar.GetYear(($date).AddDays(-[Int] (($date).AddDays(-1)).DayOfWeek+3))
      

      这会计算每周星期四的周数。 其他解决方案会错误地计算给定日期的第 53 周。

      【讨论】:

        【解决方案6】:

        好的(荷兰)文化和 UICulture 的最终抽签。

        我通过创建 WeekRuleDay 解决了这个问题:

        function Get-ISO8601Week {
            Param(
            [datetime]$DT = (Get-Date)
            )
            <#
                First create an integer(0/1) from the boolean,
                "Is the integer DayOfWeek value greater than zero?".
                Then Multiply it with 4 or 6 (weekrule = 0 or 2) minus the integer DayOfWeek value.
                This turns every day (except Sunday) into Thursday.
                Then return the ISO8601 WeekNumber.
            #>
            $Cult = Get-Culture; $DT = Get-Date($DT)
            $WeekRule = $Cult.DateTimeFormat.CalendarWeekRule.value__
            $FirstDayOfWeek = $Cult.DateTimeFormat.FirstDayOfWeek.value__
            $WeekRuleDay = [int]($DT.DayOfWeek.Value__ -ge $FirstDayOfWeek ) * ( (6 - $WeekRule) - $DT.DayOfWeek.Value__ )
            $Cult.Calendar.GetWeekOfYear(($DT).AddDays($WeekRuleDay), $WeekRule, $FirstDayOfWeek)
        }
        
        function Get-UiISO8601Week {
            Param(
            [datetime]$DT = (Get-Date)
            )
            <#
                First create an integer(0/1) from the boolean,
                "Is the integer DayOfWeek value greater than zero?".
                Then Multiply it with 4 or 6 (weekrule = 0 or 2) minus the integer DayOfWeek value.
                This turns every day (except Sunday) into Thursday.
                Then return the ISO8601 WeekNumber.
            #>
            $Cult = Get-UICulture; $DT = Get-Date($DT)
            $WeekRule = $Cult.DateTimeFormat.CalendarWeekRule.value__
            $FirstDayOfWeek = $Cult.DateTimeFormat.FirstDayOfWeek.value__
            $ThursSunDay = [int]($DT.DayOfWeek.Value__ -ge $FirstDayOfWeek ) * ( (6 - $WeekRule) - $DT.DayOfWeek.Value__ )
            $Cult.Calendar.GetWeekOfYear(($DT).AddDays($ThursSunDay), $WeekRule, $FirstDayOfWeek)
        }
        
        Write-Host "UICulture: " -NoNewline
        (20..31).ForEach( { Get-UiISO8601Week("2012-12-$($_)") }) + ((1..7).ForEach( { Get-UiISO8601Week("2013-1-$($_)") })) -join ', ' 
        Write-Host "  Culture: " -NoNewline
        (20..31).ForEach( { Get-ISO8601Week("2012-12-$($_)") }) + ((1..7).ForEach( { Get-ISO8601Week("2013-1-$($_)") })) -join ', ' 
        

        这是一个错误修复!

        【讨论】:

        • (26..31).ForEach({Get-ISO8601Week("2009-12-$($_)")});(1..7).ForEach({Get-ISO8601Week ("2010-1-$($_)")}) 52 52 53 53 53 53 53 53 53 1 1 1 1
        猜你喜欢
        • 2023-03-06
        • 2018-01-05
        • 2012-03-28
        • 2013-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-25
        • 2012-10-28
        相关资源
        最近更新 更多