tech.guitarrapc.cóm

Technical updates

PowerShellでうるう年の判定をする

恥ずかしながらうるう年の判定をしたことがありませんでした。 これが疑問だったので、色々教えてもらいながら試してみました。

うるう年の例外

例外がなければ、単純に4年に一日発生するので400年なら100日ですが… これが例外なんですね。 つまり、1700年は閏年だけど、1600年や2000年は閏年ではないと。 現在の西暦を、グレゴリオ暦として解釈した場合、Wiki上の閏年の定義は以下のようです。
  • 西暦年が4で割り切れる年は閏年
  • ただし、西暦年が100で割り切れる年は平年
  • ただし、西暦年が400で割り切れる年は閏年
よって、400年周期で考えるべきであり、y00年(yが4の倍数以外) がうるう年でないため-3回かかると。

.NETのIsLeapYear()メソッド

勿論ありました。
MSDN - DateTime.IsLeapYear メソッド
で、はぐれメタル先生からもアドバイスが。 これなら、true/falseで判定を返してくれるのですね。 ちなみに上記のコードは、trueで帰った数をwhere (?がAlias)で拾って、measureでカウントしています。 よって、閏年の件数が表示されます。
Count    : 97
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

自分でも組んでみよう

当然、やってみたくなるわけです。

条件式

閏年の判定条件式は、Cは以下のように組めます。(Wikiより)
( year % 4 ) == 0 && ( year % 100 ) != 0 || ( year % 400 ) == 0
PowerShellに置き換えるとこうです。
($_ % 4 -eq 0) -and !($_ % 100 -eq 0) -or ($_ % 400 -eq 0)
基本的に、PowerShellの -or は、ショートサーキットなので置き換えるだけですね。

年と結果を返したい

どうせ作るなら、IsLeapYearとは違い、年と結果を纏めて返してくれると嬉しいです。 また、開始年と、終了念を渡すだけにできると嬉しいですね。 よって、このような感じで。
function Get-LeapYear{

    [CmdletBinding()]
    param(
        [parameter(
            mandatory=$true,
            ValueFromPipeLine=$true,
            ValueFromPipelineByPropertyName=$true)]
        [int]$YearBegin,
        [parameter(
            mandatory=$true,
            ValueFromPipeLine=$true,
            ValueFromPipelineByPropertyName=$true)]
        [int]$YearEnd
    )

    Begin{
    }

    Process{
        switch($yearBegin..$yearEnd){
            {($_ % 4 -eq 0) -and !($_ % 100 -eq 0) -or ($_ % 400 -eq 0)}{[PSCustomObject]@{Year=$_;Leap=$true};continue}
            default{[PSCustomObject]@{Year=$_;Leap=$false}}
        }
    }

    end{

    }
}
利用する際は、これで。
Get-LeapYear -YearBegin 1600 -YearEnd 1700
1600年から1700年までの結果表示です。
Year  Leap
----  ----
1600  True
1601 False
1602 False
1603 False
1604  True
1605 False
1606 False
1607 False
1608  True
1609 False
1610 False
1611 False
1612  True
1613 False
1614 False
1615 False
1616  True
1617 False
1618 False
1619 False
1620  True
1621 False
1622 False
1623 False
1624  True
1625 False
1626 False
1627 False
1628  True
1629 False
1630 False
1631 False
1632  True
1633 False
1634 False
1635 False
1636  True
1637 False
1638 False
1639 False
1640  True
1641 False
1642 False
1643 False
1644  True
1645 False
1646 False
1647 False
1648  True
1649 False
1650 False
1651 False
1652  True
1653 False
1654 False
1655 False
1656  True
1657 False
1658 False
1659 False
1660  True
1661 False
1662 False
1663 False
1664  True
1665 False
1666 False
1667 False
1668  True
1669 False
1670 False
1671 False
1672  True
1673 False
1674 False
1675 False
1676  True
1677 False
1678 False
1679 False
1680  True
1681 False
1682 False
1683 False
1684  True
1685 False
1686 False
1687 False
1688  True
1689 False
1690 False
1691 False
1692  True
1693 False
1694 False
1695 False
1696  True
1697 False
1698 False
1699 False
1700 False
勿論件数だけも出せます。400年の件数が97件か見てみましょう。 IsLeapYearと同じようにするだけですね。
Get-LeapYear -YearBegin 1500 -YearEnd 1900 | ?{$_.Leap} | measure
PowerShell 3.0ならこうも書けます。
Get-LeapYear -YearBegin 1500 -YearEnd 1900 | ? Leap | measure
結果です。
Count    : 97
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

結果のみ返したい

IsLeapYearと同じく、bool値でのみ返すならこうですね。
function Get-IsLeapYear{

    [CmdletBinding()]
    param(
        [parameter(
            mandatory=$true,
            ValueFromPipeLine=$true,
            ValueFromPipelineByPropertyName=$true)]
        [int]$YearBegin,
        [parameter(
            mandatory=$true,
            ValueFromPipeLine=$true,
            ValueFromPipelineByPropertyName=$true)]
        [int]$YearEnd
    )

    Begin{
    }

    Process{
        switch($yearBegin..$yearEnd){
            {($_ % 4 -eq 0) -and !($_ % 100 -eq 0) -or ($_ % 400 -eq 0)}{$true;continue}
            default{$false}
        }
    }

    end{

    }
}
利用時はこうです。
Get-IsLeapYear -YearBegin 1900 -YearEnd 1920
true/falseが返ってきます。
False
False
False
False
True
False
False
False
True
False
False
False
True
False
False
False
True
False
False
False
True
これは、.NETのIsLeapYearと同様です。
1900..1920 | %{[Datetime]::IsLeapYear($_)}
true/falseが返ってきます。
False
False
False
False
True
False
False
False
True
False
False
False
True
False
False
False
True
False
False
False
True
勉強になりました。