# POWERSHELL CALCULATION OF ISO 8601 WEEK-NUMBERING DATE # http://www.merlyn.demon.co.uk/weekpshl.ps1 # Page (c) J R Stockton >= 2012-11-09 # This script is for Microsoft PowerShell v1.0 and higher. It tests its # PowerShell functions DTobjToYWDarray and YWDarrayToDTobj, which # converts between a Datetime object and an array {Y, M, D}. and # related functions. # To run a script, the command 'set-executionpolicy remotesigned' # must have been used in PowerShell. # Type 'help set-executionpolicy -full' in PowerShell for details. # Undo with 'set-executionpolicy Default' - I think (Restricted?). # So far, tested only in my Windows XP sp3 and Windows 7 XP mode; # something additional is needed for (64-bit) windows 7 - # must use the Administrator account to change settings # (or "RunAs Administrator"?). # Save this page in a suitable directory, and, at a Command Prompt # with that directory current, enter the command ##### powershell .\weekpshl.ps1 Arg1 Arg2 ##### # NOTE : the .\ is necessary # Arg1 (default 146097) controls the number of days finally tested : # 146097 days is a full cycle of 400 Gregorian years # Arg2 (default $null) tests the error route at that day-count # Testing an earlier version for every day in 400 years took, # using PowerShell 2.0 : # 9.2 minutes in my P4/3G PC, using Win XP sp3, # 3.8 minutes in my 64-bit Windows 7 laptop, 32-bit XP mode. # 16.4 minutes in my 64-bit Windows 7 laptop, 64-bit native mode. # ROUNDING NOTES by Chris Warwick # PowerShell integer rounding is directly inherited from .Net which # uses so-called 'Rounding half to even' (an implementation of the # recommended default rounding defined in IEEE 754). # PowerShell tries to be helpful by expanding the type of intermediate # results as needed (So [Int]$x = [Int]7 / [Int]2 gives $x = 4 (since # [Int]7/[Int]2 == 3.5, not 3)) which can be suprising to people who # have used other languages previously Param($MaxDays=146097, $BadDay=$null) # FIRST executable statement "`nISO 8601 Week Numbering`n" # $Host.Version # for reference # $PSVersiontable Set-StrictMode -Version 2.0 $Zeit = Get-Date ; $ThisYear = $Zeit.Year Function LZ($N) { $X = "" ; if ($N -le 9) { $X = "0" } ; $X + $N } Function YMDdate($D) { $D.toString("yyyy-MM-dd") } Function DTobjToYWDarray($Zeit) { $DoWk = +$Zeit.DayofWeek if ($DoWk -eq 0) { $DoWk = 7 } # Mon=1..Sun=7 $Tijd = $Zeit.AddDays(4 - $DoWk) # Go to nearest Thursday $Week = 1+[Math]::Floor(($Tijd.DayOfYear-1)/7) # Adjusted seventh $Year = $Tijd.Year # Needed $Year, $Week, $DoWk } # Return an array [0..2] # http://ss64.com/ps/syntax-arrays.html Function YWDarrayToDTobj($YWDarr) { $ItIs = Get-Date "01/04/$($YWDarr[0])" $DoWk = +$ItIs.DayofWeek if ($DoWk -eq 0) { $DoWk = 7 } # Mon=1..Sun=7 $ItIs = $ItIs.AddDays($YWDarr[1]*7+$YWDarr[2]-$DoWk-7) $ItIs } Function LZ($N) { $X = "" ; if ($N -le 9) { $X = "0" } ; $X + $N } Function YWDarrayToStr($WNDa) { "$($WNDa[0])-W$(LZ($WNDa[1]))-$($WNDa[2])" } Function StrToYWDarray($Str) { $X = $Str -Match "\D*(\d+)-W(\d\d)-(\d).*" if ($X) { $X = +$Matches[1], +$Matches[2], +$Matches[3] } $X } $Zeit = Get-Date # "12/31/2012" # Test DTobjToYWDarray once $WNDa = DTobjToYWDarray($Zeit) "Provided : $(YMDdate($Zeit))" "WeekDate : $(YWDarrayToStr($WNDa))" "Reversed : $(YMDdate(YWDarrayToDTobj($WNDa)))" $ZZ = StrToYWDarray(YWDarrayToStr($WNDa)) Write "Unstrung : $(YMDdate(YWDarrayToDTobj($ZZ)))" Write-Host "`n Input Date Weekday`t WkNum Date and back" ForEach ($Yeer in 2010..2013) { ForEach ($Offs in -3..+3) { $ItIs = Get-Date "01/01/$Yeer" $ItIs = $ItIs.AddDays($Offs) $WNDa = DTobjToYWDarray($ItIs) $Back = YWDarrayToDTobj($WNDa) Write-Host -NoNewline " $(YMDdate($ItIs)) $($ItIs.DayOfWeek)" Write-Host "`t $(YWDarrayToStr($WNDa)) $(YMDdate($Back))" } Write-Host "" } $Zeit = Get-Date # $MaxDays = 146097 Write-Host "Full test from Monday 2001-01-01, 0 to $MaxDays days :" Write-Host " Input Date -> WkNum Date -> Date After" -NoNewline Write-Host " Y W D <- by counting" $Y = 2001 ; $W = 1 ; $D = 0 ForEach ($Offs in 0..$MaxDays) { $ItIs = Get-Date "01/01/2001" $ItIs = $ItIs.AddDays($Offs) ; $Day = $ItIs.day # of month # Increment (W53 can only start on 28th or 27th) : $D++ if ($D -gt 7) { $D = 1 ; $W++ if ( ($W -eq 53) -and ( ($Day -ne 28) -and ($Day -ne 27) ) ) { $W = 1 ; $Y++ } if ($W -gt 53) { $W = 1 ; $Y++ } } $This = YMDdate($ItIs) $WNDa = DTobjToYWDarray($ItIs) $Wstr = YWDarrayToStr($WNDa) $WNDa = StrToYWDarray($Wstr) $Twas = YMDdate(YWDarrayToDTobj($WNDa)) Write-Host -NoNewline ` " $This $Wstr $Twas $Y $(LZ($W)) $D`t $Offs`r" If ($This -ne $Twas) { Write-Warning "FAIL, QUIT" ; EXIT } If ($Offs -eq $BadDay ` -or $WNDa[0] -ne $Y -or $WNDa[1] -ne $W -or $WNDa[2] -ne $D ) { Write-Host "" ; Write-Warning "FAIL, QUIT" ; EXIT } } $Zeit = (Get-Date) - $Zeit "`nFinished OK at $(YMDdate($ItIs))," + ` " time taken: {0:F2} minutes." -f ($Zeit.TotalSeconds/60) "`nTest Get-Date formats for some dates, to see if weeks are ISO :" # http://technet.microsoft.com/en-us/library/hh849887.aspx $Str = Get-Date "12/30/2019" -uFormat %V Write "Get-Date ""12/30/2019"" -uFormat %V gives $Str, want 1" $Str = Get-Date "2101-01-02" -uFormat %V Write "Get-Date ""2101-01-02"" -uFormat %V gives $Str, want 52" $Str = Get-Date "01/02/2101" -uFormat %V Write "Get-Date ""01/02/2101"" -uFormat %V gives $Str, want 52" "END." # Also test Get-Date "12/31/2007" -uFormat %V # and Calendar.GetWeekOfYear() for ISO weeks !! # $ErrorActionPreference = stop #######