获取Powershell中给定日期的ISO 8601年份周

时间:2017-10-11 14:41:19

标签: powershell date datetime iso8601

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

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

我离开了这个“不被接受的”'如果有人想出一个更好的解决方案,有一段时间。

6 个答案:

答案 0 :(得分:3)

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 numbernumber of Thursdays中的year from step 2(包括找到的星期四本身)
  4. 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
        '============================================================='
        }
    #>
    

    &#39;棘手&#39;日期引用@ 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
        ...........
    #>
    

答案 1 :(得分:2)

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

get-date -UFormat %V

OR

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

取决于是否要使用ISO8601。

答案 2 :(得分:0)

$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

答案 3 :(得分:0)

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

(Get-Culture).DateTimeFormat.DayNames[(Get-Culture).DateTimeFormat.FirstDayOfWeek.value__]

然后是周数不符合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 *来测试错误的结果。

答案 4 :(得分:0)

荷兰文化。

我通过跳过星期天(因为它是 [int]0)并将其他天数添加/减去到星期四来解决它:

    function Get-ISO8601Week([datetime]$DT = (Get-Date)) {
        (Get-Culture).Calendar.GetWeekOfYear((Get-Date($DT)).AddDays(
    <#
    First create an integer(0/1) from the boolean,
    "Is the integer DayOfWeek value greater than zero?".
    Then Multiply it with 4 minus the integer DayOfWeek value.
    This turns every day (except Sunday) into Thursday.
    Then return the ISO8601 WeekNumber.
    #>
        [int](([int](Get-Date($DT)).DayOfWeek) -gt 0)*(4 - ([int](Get-Date($DT)).DayOfWeek) )
        ), ((Get-Culture).DateTimeFormat.CalendarWeekRule), ((Get-Culture).DateTimeFormat.FirstDayOfWeek))
    }

    Get-ISO8601Week('2013-01-01')
    Get-ISO8601Week('2012-12-31')
    Get-ISO8601Week('2012-12-30')
    Get-ISO8601Week('2012-12-29')

这是一个错误修正!

答案 5 :(得分:0)

我想我找到了一个非常健壮的实现。

 #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 周。