1
\$\begingroup\$

It's me again, talented programming beginner fast-learner, so I have overcomed yet another self-imposed programming challenge, I have developped two algorithms to calculate occurrences of Friday the 13th between two given years(including the two inputed years) as said in title using PowerShell, one with built-in, the other without built-in. I have tested them numerous times on Windows 10 Power 7.1 x64, redirected their outputs to txt and compared the txts using fc and compared the results with outputs of other softwares and confirmed they are both working.

My idea is to loop through all 13th's (loop1 through years loop2 in loop1 through months) and check their Fridayness, the one with builtin, I used [DateTime] and [DateTime].DayOfWeek to do this mainly in one line, the one without builtin I used brute force to convert date to number of days, using this: days=365(y-1)+m+d+leap, where y is the year, m is sum of days before the month, d is day, and leap is: q(y/4)-q(y/100)+q(y/400), where q() means quotient of the division, Now I know of [math]::floor() and I know I can determine number of leap years using this:

$leap=[math]::floor($year/4)-[math]::floor($year/100)+[math]::floor($year/400)

But I decided against using this, then I checked Fridayness by date mod 7, I have found out if the remainder is 5 then it's Friday, now here are the scripts:

With Built-in:

$start=Read-Host "Input start year"
$end=Read-Host "Input end year"
$years=$start..$end
$blackfriday=@()
foreach ($year in $years) {
    $blackfriday+=1..12 | foreach { [datetime]"$year-$_-13" } | where { $_.DayOfWeek -eq 'Friday'} | foreach {$_.tostring("yyyy, MMMM dd, dddd")}
}
$blackfriday

Without Built-in:

$months=@(
[PSCustomObject]@{Month='January';Days=31}
[PSCustomObject]@{Month='February';Days=28}
[PSCustomObject]@{Month='March';Days=31}
[PSCustomObject]@{Month='April';Days=30}
[PSCustomObject]@{Month='May';Days=31}
[PSCustomObject]@{Month='June';Days=30}
[PSCustomObject]@{Month='July';Days=31}
[PSCustomObject]@{Month='August';Days=31}
[PSCustomObject]@{Month='September';Days=30}
[PSCustomObject]@{Month='October';Days=31}
[PSCustomObject]@{Month='November';Days=30}
[PSCustomObject]@{Month='December';Days=31}
)
function BlackFriday {
    param(
    [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0)] [int64] $start,
    [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1)] [int64] $end
    )
    $years=$start..$end
    $blackfriday=@()
    foreach ($year in $years) {
        $array=1..12
        foreach ($arra in $array) {
            $month=0
            for ($i=0;$i -lt $arra-1;$i++) {
                $month+=$months[$i].Days
            }
            [int]$ye=$year
            if ($arra -le 2) { $ye-=1}
            if ($ye % 4 -eq 0) {$leap=$ye/4}
            else {while ($ye % 4 -ne 0) {$ye-=1}
            $leap=$ye/4}
            if ($ye % 100 -eq 0) {$century=$ye/100}
            else {while ($ye % 100 -ne 0) {$ye-=4}
            $century=$ye/100}
            if ($ye % 400 -eq 0) {$cycle=$ye/400}
            else {while ($ye % 400 -ne 0) {$ye-=100}
            $cycle=$ye/400}
            $leap=$leap-$century+$cycle
            $date=[int64](($year-1)*365+$month+13+$leap)
            if ($date % 7 -eq 5) {
                $name=$months[$arra-1].Month
                $blackfriday+=[string]"$year, $name 13, Friday"
            }
        }
    }
    $blackfriday
}
$start=Read-Host "Input start year"
$end=Read-Host "Input end year"
BlackFriday $start $end

But wait, I have also written a script based on Zeller's algorithm from here: https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week Which is not working, I don't know where I did wrong, I don't know whose fault this is, please help me make all three scripts better, I will post it below:

$months=@(
'January'
'February'
'March'
'April'
'May'
'June'
'July'
'August'
'September'
'October'
'November'
'December'
)

function BlackFriday {
    param(
    [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0)] [int64] $start,
    [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1)] [int64] $end
    )
    $years=$start..$end
    $blackfriday=@()
    foreach ($year in $years) {
        $array=1..12
        foreach ($arra in $array) {
            $m=$arra
            $ye=$year
            if ($arra -le 2) {
                $m=$m+12
                $ye-=1
                }
            [int64]$y=([string]$ye).substring(([string]$ye).length-2)
            [int64]$c=([char[]]"$ye"|Select-Object -First 2) -Join ''
            $week=[int](((13*($m+1))/5 + $y/4 + $c/4 + 13 + $y - 2*$c) % 7)
            if ($week -eq 6) {
                $name=$months[$arra-1]
                $blackfriday+=[string]"$year, $name 13, Friday"
            }
        }
    }
    $blackfriday
}
$start=Read-Host "Input start year"
$end=Read-Host "Input end year"
BlackFriday $start $end
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Why reinvent the wheel? Why not use Get-Date cmdlet, and simplify the code?

$StartDate = Get-Date -Day 13 -Month 1 -Year 2019
$EndDate = Get-Date -Day 13 -Month 12 -Year 2020
$IntStartDate = $StartDate.TofileTime()
$IntEndDate = $EndDate.TofileTime()

do {
    if ($StartDate.DayOfWeek -eq 'Friday') {
        Write-Host ($StartDate.tostring("dd-MMMM-yyyy"))
    }
    $StartDate = $StartDate.AddMonths(1)
    $IntStartDate = $StartDate.TofileTime()
}
while ($IntStartDate -le $IntEndDate)

Implement this in your function with parametrization.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.