1

I have a scheduled task that runs a powershell script, which runs an executable with some parameters. One of the parameters is the current week number.

I am using the following code in powershell to retrieve the weeknumber:

get-date -uformat %V

This seems to return an incorrect weeknumber

  • 2014-05-04 (sunday), returns week nr 18 (as expected)
  • 2014-05-05 (monday), returns week nr 18 (expected 19)
  • 2014-05-06 (tuesday), returns week nr 19 (as expected)

In my registry the first day of week is set to Monday (HKEY_CURRENT_USER->Control Panel->International->iFirstDayOfWeek = 0)

Does anyone know where powershell retrieves it's firstDayOfWeek setting from? Should (/can) I otherwise set it in the script?

I know a workaround is to use .AddDays(1), but I'm looking for a neater solution.

Edit: Ok this is really weird, I'm receiving different 'first day of week's for different years..

2012 - On saturday:

PS C:\Users\A730978F> get-date -date "2012-01-08" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-08 Sun Week: 2
PS C:\Users\A730978F> get-date -date "2012-01-09" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-09 Mon Week: 2
PS C:\Users\A730978F> get-date -date "2012-01-10" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-10 Tue Week: 2
PS C:\Users\A730978F> get-date -date "2012-01-11" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-11 Wed Week: 2
PS C:\Users\A730978F> get-date -date "2012-01-12" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-12 Thu Week: 2
PS C:\Users\A730978F> get-date -date "2012-01-13" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-13 Fri Week: 2
PS C:\Users\A730978F> get-date -date "2012-01-14" -uFormat "%Y-%m-%d %a Week: %V"
2012-01-14 Sat Week: 3

2013 - On monday:

PS C:\Users\A730978F> get-date -date "2013-01-06" -uFormat "%Y-%m-%d %a Week: %V"
2013-01-06 Sun Week: 1
PS C:\Users\A730978F> get-date -date "2013-01-07" -uFormat "%Y-%m-%d %a Week: %V"
2013-01-07 Mon Week: 2
PS C:\Users\A730978F> get-date -date "2013-01-08" -uFormat "%Y-%m-%d %a Week: %V"
2013-01-08 Tue Week: 2

2014 - On Tuesday:

PS C:\Users\A730978F> get-date -date "2014-01-05" -uFormat "%Y-%m-%d %a Week: %V"
2014-01-05 Sun Week: 1
PS C:\Users\A730978F> get-date -date "2014-01-06" -uFormat "%Y-%m-%d %a Week: %V"
2014-01-06 Mon Week: 1
PS C:\Users\A730978F> get-date -date "2014-01-07" -uFormat "%Y-%m-%d %a Week: %V"
2014-01-07 Tue Week: 2

2015 - On wednesday:

PS C:\Users\A730978F> get-date -date "2015-01-04" -uFormat "%Y-%m-%d %a Week: %V"
2015-01-04 Sun Week: 1
PS C:\Users\A730978F> get-date -date "2015-01-05" -uFormat "%Y-%m-%d %a Week: %V"
2015-01-05 Mon Week: 1
PS C:\Users\A730978F> get-date -date "2015-01-06" -uFormat "%Y-%m-%d %a Week: %V"
2015-01-06 Tue Week: 1
PS C:\Users\A730978F> get-date -date "2015-01-07" -uFormat "%Y-%m-%d %a Week: %V"
2015-01-07 Wed Week: 2

(Note that I haven't tried all the weeks for every year)

Edit2:

After looking over the examples again, it seems the week number is based on the amount of '7 days' that have passed. I guess it doesn't actually use any 'first day of week' setting at all. I guess I'll have to make some calculation accordingly.

Thanks for the help anyway!

Deruijter
  • 2,077
  • 16
  • 27

3 Answers3

2

Apparently powershell parameter -uformat %V doesn't actually use any kind of 'first day of week' rule. It actually just shows the amount of '7 days' that have passed since the beginning of the year (starting at 1).

Using this information I made the following function that retrieves the same correct week number for each day in the week (sunday to saturday):

(Thanks to both @DavidBrabant and @vonPryz for the additional info I required)

Function GetWeekOfYear($date)
{
    # Note: first day of week is Sunday
    $intDayOfWeek = (get-date -date $date).DayOfWeek.value__
    $daysToWednesday = (3 - $intDayOfWeek)
    $wednesdayCurrentWeek = ((get-date -date $date)).AddDays($daysToWednesday)

    # %V basically gets the amount of '7 days' that have passed this year (starting at 1)
    $weekNumber = get-date -date $wednesdayCurrentWeek -uFormat %V

    return $weekNumber
}

$weekNumber = GetWeekOfYear (get-date)

(I used a parameter so the function is more flexible/testable)

This function basically gets the week number based on the nth Wednesday of the year and uses Sunday as the first day of the week (I haven't put time into changing that since this is actually the convention my company uses).

You could say it retrieves the week number based on the first week having at least 4 days in this year, and the first day of the week being Sunday.

Deruijter
  • 2,077
  • 16
  • 27
0

The problem is caused by different globalization rules. US week numbers are counted in different a way from, say, Germany.

In order to get expected week numbering, you got to pull some strings with globalization settings. Another a SO question has detailed a discussion about the topic.

Community
  • 1
  • 1
vonPryz
  • 22,996
  • 7
  • 54
  • 65
  • 1
    Thanks for your answer, the week 53 was indeed a different issue I was having. However, this question was actually about a problem with getting the right week number based on the day of the week (the week change wasn't alwas between sunday-monday) – Deruijter May 05 '14 at 13:06
0

In PowerShell 7.x.y:

[System.Globalization.ISOWeek]::GetWeekOfYear((get-date))

In PowerShell 5.x.y:

function Get-ISOWeek {
  Param(
        [Parameter(
            # Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = 'Valid Date'
        )]
        [Alias('DT', 'DateTime')]
            [datetime[]]$Dates = (Get-Date),
        [Validateset('Culture', 'UICulture', 'InvariantCulture')]
        [string]$Culture
    )
    <#
        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.
    #>
    BEGIN {
        $Cult = Switch ($Culture) {
            'UICulture' {
                Get-UICulture
                Break
            }
            'InvariantCulture' {
                [CultureInfo]::InvariantCulture
                Break
            }
            default{
                Get-Culture
            }
        }
        $WeekRule = $Cult.DateTimeFormat.CalendarWeekRule.value__
        $FirstDayOfWeek = $Cult.DateTimeFormat.FirstDayOfWeek.value__
    }
    PROCESS{
        ForEach ($DateElement in $Dates) {
            Try {
                Write-Verbose "------------------------"
                Write-Verbose "Try to turn $DateElement into ISO8601Week"
                $WeekRuleDay = [int]($DateElement.DayOfWeek.Value__ -ge $FirstDayOfWeek ) * ( (6 - $WeekRule) - $DateElement.DayOfWeek.Value__ )
                $WeekNumber = $Cult.Calendar.GetWeekOfYear(($DateElement).AddDays($WeekRuleDay), $WeekRule, $FirstDayOfWeek)
<#
I need to check the DayNumber formula. The FirstDayOfTheWeek must become 1
In the Netherlands we start with Monday
#>
                $DayNumber = (7, 1, 2, 3, 4, 5, 6)[$($DateElement.DayOfWeek.value__ + 1)]
                $Properties = @{
                    Date = Get-Date($DateElement)
                    LongDate = Get-Date($DateElement).ToLongDateString()
                    WeekRuleDay = $WeekRuleDay
                    WeekNumber = $WeekNumber
          ISOWeek    = "$($DateElement.Year)-W$WeekNumber"
          ISOWeekDay = "$($DateElement.Year)-W$WeekNumber-$DayNumber"
          ISO8601Week    = "$($DateElement.Year)-W$WeekNumber"
                    ISO8601WeekDay = "$($DateElement.Year)-W$WeekNumber-$DayNumber"
                }
            } Catch {
                Write-Verbose "Could not turn: $DateElement into ISO8601Week"
                $Properties = @{
                    Date = $DateElement
                    LongDate = ''
                    WeekRuleDay = ''
                    WeekNumber = ''
          ISOWeek    = ''
          ISOWeekDay = ''
          ISO8601Week    = ''
                    ISO8601WeekDay = ''
                }
            } Finally{
                Write-Verbose "Final"
                $Obj = New-Object -Typename PSCustomObject -Property $Properties
                Write-Output $Obj
            }
            
        }
    }
    END{}
}

For Output:

(20..31).ForEach( { (Get-ISOWeek("2012-12-$($_)")).ISOWeekDay }) + ((1..7).ForEach( { (Get-ISOWeek("2013-1-$($_)")).ISOWeekDay })) -join ','
B-Art
  • 53
  • 4