SHIMSOFT-Holidays.psm1

$SupportedDate ="2019 年 4月"

function Get-NextWeekDay {
    <#
    .SYNOPSIS
    指定日を基準に次ぎの指定曜日を算出します。
 
    .DESCRIPTION
    指定日を基準に次ぎの指定曜日を算出します。
 
    .EXAMPLE
    Get-NextWeekDay -From (Get-Date -Format "yyyy/MM/dd") -NextWeekDay Monday
     
    .EXAMPLE
    Get-NextWeekDay -From "2018/2/17" -BeforeWeekDay Monday
     
    .EXAMPLE
    Get-NextWeekDay -From "2018/2/17" -NextWeekDay Monday -IntervalWeeks 2
     
    .EXAMPLE
    Get-NextWeekDay -From "2018/2/17" -NextWeekDay Monday -ListCount 10
 
    .EXAMPLE
    Get-NextWeekDay -From "2018/2/17" -NextWeekDay Saturday -AllowSameDate
 
    .EXAMPLE
    Get-NextWeekDay -From "2018/2/17" -NextWeekDay Saturday -AllowSameDate -DateTime
     
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    param(
        [Parameter(Mandatory=$false,Position=1)]
        [String]$From=[string](Get-Date -Format "yyyy/MM/dd"),

        [Parameter(Mandatory=$false,Position=2)]
        [String]$To=$From,
        
        [Parameter(Mandatory=$false,Position=3)]
            [ValidateSet("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")]
        [String]$NextWeekDay,
        
        [Parameter(Mandatory=$false,Position=3)]
            [ValidateSet("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")]
        [String]$BeforeWeekDay,
        
        [Parameter(Mandatory=$false,Position=3)]
        [int]$IntervalWeeks=1,
        
        [Parameter(Mandatory=$false,Position=4)]
        [int]$ListCount=1,

        [Parameter(Mandatory=$false,Position=5)]
        [Switch]$AllowSameDate,

        [Parameter(Mandatory=$false,Position=6)]
        [Switch]$DateTime
        
    )

    $WeekDays =@("sunday","monday","tuesday","wednesday","thursday","friday","saturday")

    Write-Debug ("From : {0} NextWeekDay : {1} BefreWeekDay : {2} IntervalWeeks : {3} ListCount : {4}" -f $From, $NextWeekDay, $BeforeWeekDay, $IntervalWeeks, $ListCount)

    $FromDate = Get-Date -Date $From
    $ToDate = Get-Date -Date $To
    
    Write-Debug ("FromDate {0}" -f $FromDate)

    
    $FromWeekDay = $WeekDays.IndexOf(([string]$FromDate.DayOfWeek).ToLower())

    Write-Debug ("FromWeekDay {0}" -f $FromWeekDay)
    $DiffDays =0
    $IntervalWeeks = $IntervalWeeks * 7
    
    if ($BeforeWeekDay -ne "") {
        $BeforeTargetWeekDay = $WeekDays.IndexOf($BeforeWeekDay.ToLower())
        $DiffDays = $BeforeTargetWeekDay - $FromWeekDay - [int]($BeforeTargetWeekDay -gt $FromWeekDay) * 7 - [int]($BeforeTargetWeekDay -eq $FromWeekDay) * 7 * [int](-not $AllowSameDate.IsPresent)
        Write-Debug ("BeforeTargetWeekDay : {0} DiffDays : {1}" -f $BeforeTargetWeekDay, $DiffDays)
        
        $IntervalWeeks = -$IntervalWeeks

    }

    if ($NextWeekDay -ne "") {
        $NextTargetWeekDay = $WeekDays.IndexOf($NextWeekDay.ToLower())
        $DiffDays = $NextTargetWeekDay - $FromWeekDay + [int]($NextTargetWeekDay -lt $FromWeekDay) * 7 + [int]($NextTargetWeekDay -eq $FromWeekDay) * 7 * [int](-not $AllowSameDate.IsPresent)

        Write-Debug ("NextTargetWeekDay : {0} DiffDays : {1}" -f $NextTargetWeekDay, $DiffDays)

    }

    $DateList = @()
    
    if ($From -eq $To) {
        for ($i=0; $i -lt $ListCount ; $i++) {
            if ($DateTime -eq $False) {
                $DateList += Get-Date $FromDate.AddDays($DiffDays + $IntervalWeeks * $i) -Format "yyyy/MM/dd"
            } else {
                $DateList += Get-Date $FromDate.AddDays($DiffDays + $IntervalWeeks * $i)
            }
        }
    } else {
        if ($FromDate -lt $ToDate) {
            $StartDate = $FromDate; $EndDate= $ToDate
        } else {
            $StartDate = $ToDate; $EndDate= $FromDate
        }
        
        $i=0;
        
        Do {
            if ($DateTime -eq $False) {
                $DateList += Get-Date $FromDate.AddDays($DiffDays + $IntervalWeeks * $i) -Format "yyyy/MM/dd"
            } else {
                $DateList += Get-Date $FromDate.AddDays($DiffDays + $IntervalWeeks * $i)
            }
                $i++
        } until ($StartDate -gt $DateList[($i-1)] -or $EndDate -lt $DateList[($i-1)])
        
        $DateList = $DateList[0..($DateList.Count -2)]
    }
    
    $DateList
}




function Reset-HolidaysTemplate {
    <#
    .SYNOPSIS
    祝日テンプレートを初期化します。
 
    .DESCRIPTION
    祝日テンプレートを Default に初期化します。
    祝日テンプレートに追加設定していた情報は失われます。
     
    -Default オプションを指定し、ハードコードされている祝日テンプレートに初期化する場合、
    2019 年 4月現在制定されている祝日情報に対応します。対応している祝日は以下の通りです。
    元日
    成人の日
    建国記念の日
    春分の日
    昭和の日
    憲法記念日
    みどりの日
    こどもの日
    海の日
    山の日
    敬老の日
    秋分の日
    体育の日/スポーツの日
    文化の日
    勤労感謝の日
    天皇誕生日
     
    ※ 2019年 5月 1日  新天皇即位
    ※ 2019年 10月 22日  即位礼正殿の儀
 
    .EXAMPLE
    Reset-HolidaysTemplate
    祝日テンプレートを Default テンプレートに初期化します。
 
    .EXAMPLE
    Reset-HolidaysTemplate -Default
    祝日 Default テンプレートをハードコードされている祝日テンプレートに初期化します。
     
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Justification="I like write-host.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingInvokeExpression", "", Justification="Requirement this function.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Affected this module using XML file only.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "Year", Justification="Year variable used only object displayed.")]
    param(
        [Parameter(Mandatory=$false,Position=1)]
        [Switch]$Default,

        [Parameter(Mandatory=$false,Position=1)]
        [Switch]$PublicHoliday,
        
        [Parameter(Mandatory=$false,Position=1)]
        [Switch]$Custom
        
    )

    if ($Default -eq $True) {
        $HolidayNames = @(
            "元日",
            "成人の日",
            "建国記念の日",
            "春分の日",
            "昭和の日",
            "憲法記念日",
            "みどりの日",
            "こどもの日",
            "海の日",
            "山の日",
            "敬老の日",
            "秋分の日",
            "体育の日",
            "文化の日",
            "勤労感謝の日",
            "天皇誕生日(平成)",

            "スポーツの日",
            "スポーツの日",
            "海の日",
            "山の日",
            "天皇誕生日(昭和)",
            "天皇誕生日(令和)",
            "成人の日",
            "海の日",
            "敬老の日",
            "体育の日",
            
            "新天皇即位(令和)",
            "即位礼正殿の儀(令和)"
            
        )

        $StartEndDate = @(
            @("1900/01/01","3000/01/01"),
            @("2000/01/01","3000/01/01"),
            @("1966/01/01","3000/01/01"),
            @("1948/01/01","3000/01/01"),
            @("1989/01/01","3000/01/01"),
            @("1948/01/01","3000/01/01"),
            @("2007/01/01","3000/01/01"),
            @("1948/01/01","3000/01/01"),
            @("2003/01/01","3000/01/01"),
            @("2016/01/01","3000/01/01"),
            @("2003/01/01","3000/01/01"),
            @("1948/01/01","3000/01/01"),
            @("2000/01/01","2019/12/31"),
            @("1946/11/03","3000/01/01"),
            @("1948/01/01","3000/01/01"),
            @("1989/01/01","3000/01/01"),

            @("2020/01/01","2020/12/31"),
            @("2021/01/01","3000/01/01"),
            @("2020/01/01","2020/12/31"),
            @("2020/01/01","2020/12/31"),
            @("1949/01/01","1988/12/31"),
            @("2020/01/01","3000/01/01"),
            @("1948/01/01","1999/12/31"),
            @("1996/01/01","2002/12/31"),
            @("1948/01/01","2002/12/31"),
            @("1966/01/01","1999/12/31"),

            @("2019/01/01","2019/12/31"),
            @("2019/01/01","2019/12/31")
            
        )
        
        

        $HolidayExpressions = @(
            'Get-Date -Date ($Year + "/1/1")',
            'Get-Date -Date(Get-NextWeekDay -From ($Year + "/1/14") -BeforeWeekDay Monday -AllowSameDate)',
            'Get-Date -Date ($Year + "/2/11")',
            ('Get-Date -Date (Get-Date -Date ($Year + "/3/23")).AddDays(-(Invoke-Expression ' + "`'" + '$a=@(@(0,1900,1960,2092),@(0,1901,1933),@(0,1902,2026),@(1903,1927,2059))[($Year%4)];$a.IndexOf(($a -le $Year | Sort-Object -Descending)[0])+1' + "`'))"),
            'Get-Date -Date ($Year + "/4/29")',
            'Get-Date -Date ($Year + "/5/3")',
            'Get-Date -Date ($Year + "/5/4")',
            'Get-Date -Date ($Year + "/5/5")',
            'Get-Date -Date (Get-NextWeekDay -From ($Year + "/7/21") -BeforeWeekDay Monday -AllowSameDate)',
            'Get-Date -Date ($Year + "/8/11")',
            'Get-Date -Date (Get-NextWeekDay -From ($Year + "/9/21") -BeforeWeekDay Monday -AllowSameDate)',
            ('Get-Date -Date (Get-Date -Date ($Year + "/9/25")).AddDays(-(Invoke-Expression ' + "`'" + '$a=@(@(0,1900,2012),@(1901,1921,2045),@(1902,1950,2078),@(1903,1983))[($Year%4)];$a.IndexOf(($a -le $Year | Sort-Object -Descending)[0])+1' + "`'))"),
            'Get-Date -Date (Get-NextWeekDay -From ($Year + "/10/14") -BeforeWeekDay Monday -AllowSameDate)',
            'Get-Date -Date ($Year + "/11/3")',
            'Get-Date -Date ($Year + "/11/23")',
            'Get-Date -Date ($Year + "/12/23")',

            'Get-Date -Date ($Year + "/7/24")',
            'Get-Date -Date (Get-NextWeekDay -From ($Year + "/10/14") -BeforeWeekDay Monday -AllowSameDate)'
            'Get-Date -Date ($Year + "/7/23")',
            'Get-Date -Date ($Year + "/8/10")',
            'Get-Date -Date ($Year + "/4/29")',
            'Get-Date -Date ($Year + "/2/23")',
            'Get-Date -Date ($Year + "/1/15")',
            'Get-Date -Date ($Year + "/7/20")',
            'Get-Date -Date ($Year + "/9/15")',
            'Get-Date -Date ($Year + "/10/10")',

            'Get-Date -Date ($Year + "/5/01")',
            'Get-Date -Date ($Year + "/10/22")'
            
            
        )

        $HolidaysTemplate = @()

        for ($i = 0; $i -lt $HolidayNames.Count;$i++) {
            $Holiday = New-Object PSObject
            $Holiday | Add-Member -MemberType NoteProperty -Name HolidayName -Value ($HolidayNames[$i])
            $Holiday | Add-Member -MemberType NoteProperty -Name HolidayID -Value $i
            $Holiday | Add-Member -MemberType ScriptProperty -Name Date -Value { $Year = [string](Get-Date).Year; Invoke-Expression ($this.Expression) }
            $Holiday | Add-Member -MemberType NoteProperty -Name Expression -Value ($HolidayExpressions[$i])
            $Holiday | Add-Member -MemberType NoteProperty -Name Type -Value "PublicHoliday"
            $Holiday | Add-Member -MemberType NoteProperty -Name SubstituteEnable -Value ($True)
            $Holiday | Add-Member -MemberType NoteProperty -Name IsCustom -Value ($False)
            $Holiday | Add-Member -MemberType NoteProperty -Name Locale -Value ("Japan")

            $Holiday | Add-Member -MemberType NoteProperty -Name StartDate -Value (Get-Date ($StartEndDate[$i][0]))
            $Holiday | Add-Member -MemberType NoteProperty -Name EndDate -Value (Get-Date ($StartEndDate[$i][1]))
    
            $HolidaysTemplate += $Holiday
        }

        $HolidaysTemplate | Export-CliXml -Path $PSScriptRoot\HolidaysTemplate-Default.xml

        Write-Host ("HolidaysTemplate-Default.xml を初期値で再生成しました。{0}以降に制定された祝日には対応していません。" -f $SupportedDate)
    }
    
    if ($PublicHoliday -eq $True) {
        if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate-Default.xml) -eq $False) {
            Reset-HolidaysTemplate -Default
        } else {
            $HolidaysTemplate = Import-CliXml -Path $PSScriptRoot\HolidaysTemplate-Default.xml
            $HolidaysTemplate | Export-CliXml -Path $PSScriptRoot\HolidaysTemplate.xml

            Write-Host "HolidaysTemplate.xml を Default で初期化しました。"
        }
    }
    
    if ($Custom -eq $True) {
        if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate-Custom.xml) -eq $True) {
            Copy-Item -Path $PSScriptRoot\HolidaysTemplate-Custom.xml -Destination $PSScriptRoot\HolidaysTemplate-Custom-Backup.xml
            Remove-Item -Path $PSScriptRoot\HolidaysTemplate-Custom.xml
            Write-Host "HolidaysTemplate-Custom.xml を 削除しました。"
        }
    }
}



function Get-Holidays {
    <#
    .SYNOPSIS
    祝日テンプレートから指定年の祝日一覧を算出します。
 
    .DESCRIPTION
    祝日テンプレートから指定年の祝日一覧を算出します。
    祝日日程が 日曜日 である場合、振り替え休日も算出され ActualDate に出力されます。
    Date = 祝日の日
    ActualDate = 祝日の日、または振り替え休日
     
 
    .EXAMPLE
    Get-Holidays
 
    .EXAMPLE
    Get-Holidays -Year "2018"
 
    .EXAMPLE
    Get-Holidays -Year "2018" -IncludeCustom
     
    .EXAMPLE
    Get-Holidays -Year "2018" -OnlyCustom
     
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingInvokeExpression", "", Justification="Requirement this function.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    param(
        [Parameter(Mandatory=$false,Position=1)]
        [String]$Year=[string]((Get-Date).Year),

        [Parameter(Mandatory=$false,Position=2)]
        [Switch]$IncludeCustom,

        [Parameter(Mandatory=$false,Position=1)]
        [Switch]$OnlyCustom
    )

    if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate.xml) -eq $False) {
        Reset-HolidaysTemplate -PublicHoliday
    }
        
    $HolidaysTemplate = @(Import-CliXml -Path $PSScriptRoot\HolidaysTemplate.xml)
    
    if ($IncludeCustom -and (Test-Path -Path $PSScriptRoot\HolidaysTemplate-Custom.xml) -eq $True) {
        $HolidaysTemplate += @(Import-CliXml -Path $PSScriptRoot\HolidaysTemplate-Custom.xml)
    }
    
    if ($OnlyCustom) {
        if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate-Custom.xml) -eq $True) {
            $HolidaysTemplate = @(Import-CliXml -Path $PSScriptRoot\HolidaysTemplate-Custom.xml)
        } else {
            break
        }
    }
    
    $Work = $HolidaysTemplate | Where-Object {$_.StartDate -le ($Year + "/1/1") -and $_.EndDate -ge ($Year + "/12/31")} | Group-Object -Property HolidayName
    
    $HolidaysTemplate = @()
    
    foreach ($W in $Work) {
        if ($W.Count -eq 1) { $HolidaysTemplate += $W.Group[0] } else {
            $HolidaysTemplate += $W.Group | Where-Object {$_.StartDate -eq ($W.Group | Measure-Object -Property StartDate -Maximum).Maximum }
        }
    }

    $Holidays = @()

    Foreach ($HT in $HolidaysTemplate) {
        $Work = New-Object PSObject

        $Work | Add-Member -MemberType NoteProperty -Name HolidayName -Value $HT.HolidayName
        $Work | Add-Member -MemberType NoteProperty -Name Date -Value (Invoke-Expression ($HT.Expression))
        $Work | Add-Member -MemberType NoteProperty -Name ActualDate -Value (Invoke-Expression ($HT.Expression))
        $Work | Add-Member -MemberType NoteProperty -Name IsSubstituted -Value $False
        $Work | Add-Member -MemberType NoteProperty -Name Type -Value $HT.Type
        $Work | Add-Member -MemberType NoteProperty -Name SubstituteEnable -Value $HT.SubstituteEnable
        $Work | Add-Member -MemberType NoteProperty -Name IsCustom -Value $HT.IsCustom
        $Work | Add-Member -MemberType NoteProperty -Name Locale -Value $HT.Locale
        $Work | Add-Member -MemberType ScriptProperty -Name Holiday -Value { Get-Date $this.Date -Format "yyyy/MM/dd" }
        $Work | Add-Member -MemberType ScriptProperty -Name Substitute -Value { Get-Date $this.ActualDate -Format "yyyy/MM/dd" }


        $defaultProperties = @("HolidayName","Holiday","Substitute")
        $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet("DefaultDisplayPropertySet",[string[]]$defaultProperties)
        $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
        $Work | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers

        $Holidays += $Work

    }

    Foreach ($HD in $Holidays) {
        if ($HD.SubstituteEnable -eq $True -and $HD.ActualDate.DayOfWeek -eq "Sunday") {
            $Substituted = $False
    
            Do {
                $HD.ActualDate = $HD.ActualDate.AddDays(1)
                $Work = @($Holidays | Where-Object {$_.ActualDate -eq $HD.AcrualDate -and $_.IsCustom -eq $False })
                if ($Work.Count -eq 0 -and $HD.ActualDate.DayOfWeek -ne "Sunday") { $Substituted = $True; $HD.IsSubstituted = $True }
            } Until ($Substituted -eq $True)
        }
    }

    $Holidays = $Holidays | Sort-Object -Property ActualDate

    for ($i=1; $i -lt $Holidays.Count; $i++) {
        if ($Holidays[$i].Type -eq "PublicHoliday" -and $Holidays[($i-1)].Type -eq "PublicHoliday") {
            if (($Holidays[$i].ActualDate - $Holidays[($i-1)].ActualDate).Days -eq 2) {
                if ((($Holidays[($i-1)].ActualDate).AddDays(1)).DayOfWeek -ne "Sunday") {

                    $Work = New-Object PSObject

                    $Work | Add-Member -MemberType NoteProperty -Name HolidayName -Value "休日(挟まれた平日)"
                    $Work | Add-Member -MemberType NoteProperty -Name Date -Value (($Holidays[($i-1)].ActualDate).AddDays(1))
                    $Work | Add-Member -MemberType NoteProperty -Name ActualDate -Value (($Holidays[($i-1)].ActualDate).AddDays(1))
                    $Work | Add-Member -MemberType NoteProperty -Name IsSubstituted -Value $False
                    $Work | Add-Member -MemberType NoteProperty -Name Type -Value "CatchingHoliday"
                    $Work | Add-Member -MemberType NoteProperty -Name SubstituteEnable -Value $False
                    $Work | Add-Member -MemberType NoteProperty -Name IsCustom -Value $False
                    $Work | Add-Member -MemberType NoteProperty -Name Locale -Value "Japan"
                    $Work | Add-Member -MemberType ScriptProperty -Name Holiday -Value { Get-Date $this.Date -Format "yyyy/MM/dd" }
                    $Work | Add-Member -MemberType ScriptProperty -Name Substitute -Value { Get-Date $this.ActualDate -Format "yyyy/MM/dd" }


                    $defaultProperties = @("HolidayName","Holiday","Substitute")
                    $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet("DefaultDisplayPropertySet",[string[]]$defaultProperties)
                    $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
                    $Work | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers

                    $Holidays += $Work
                }
            }
        }
    }

    $Holidays | Sort-Object -Property ActualDate
}

function Add-CustomHoliday {
    <#
    .SYNOPSIS
    祝日/休日定義を作成し、カスタムテンプレートに追加します。
 
    .DESCRIPTION
    祝日/休日定義を作成し、カスタムテンプレートに追加します。
    以下に相当する一連の動きを行います。
    $CHT = Get-HolidaysTemplate -TemplateType Custom
    $CHT += New-CustomHoliday -Name "メーデー" -Expression 'Get-Date -Date ($Year + "/5/01")'
    Update-HolidaysTemplate -CustomTemplate $CHT -TemplateType Custom
 
    .EXAMPLE
    Add-CustomHoliday -Name "メーデー" -Date "5/1"
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Justification="I like write-host.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Create variable type of Object of HolidayTemplate style.")]
    param(
        [Parameter(Mandatory=$True,Position=1)]
        [String]$Name,

        [Parameter(Mandatory=$False,Position=2)]
        [String]$Date,

        [Parameter(Mandatory=$False,Position=2)]
        [String]$Expression,

        [Parameter(Mandatory=$false,Position=3)]
        [Switch]$SubstituteEnable,

        [Parameter(Mandatory=$false,Position=4)]
                [ValidateSet('Holiday','Anniversary','Event','PublicHoliday')]
        [String]$Type="Holiday",

        [Parameter(Mandatory=$false,Position=5)]
        [String]$Locale="Japan",

        [Parameter(Mandatory=$false,Position=6)]
        [String]$StartDate=(Get-Date -Format "yyyy/01/01"),

        [Parameter(Mandatory=$false,Position=7)]
        [String]$EndDate=(Get-Date -Format "yyyy/12/31")
        
    )


    $LastDay = @(0,31.28.31,30,31,30,31,31,30,31,30,31)
    $Fail = $False
    

    $CHT = @(Get-HolidaysTemplate -TemplateType Custom)
    if ($null -eq $CHT[0]) { $CHT = @() }
    
    if ($Date -ne "") {
        if ($Date  -match "^[0-9]+/[0-9]+$") {
            $Work = $Date.Split("/")
            if ([int]$Work[0] -ge 1 -and [int]$Work[0] -le 12 -and [int]$Work[1] -ge 1 -and [int]$Work[1] -le $LastDay[($Work[0])]) {
                $CH = New-CustomHoliday -Name $Name -Date $Date -SubstituteEnable:$SubstituteEnable -Type $Type -Locale $Locale -StartDate $StartDate -EndDate $EndDate
            } else {
                Write-Error "日付指定が異常です。 mm/dd で指定してください。"
                $Fail = $True
            }
        } else {
            Write-Error "日付指定が異常です。 mm/dd で指定してください。"
            $Fail = $True
        }
    } else {
        if ($Expression -ne "") {
            $CH = New-CustomHoliday -Name $Name -Expression $Expression -SubstituteEnable:$SubstituteEnable -Type $Type -Locale $Locale -StartDate $StartDate -EndDate $EndDate
        } else {
            Write-Error "-Date または -Expression オプションいずれかは必ず指定してください。"
            $Fail = $True
        }
    }

    


# if ($CHT.HolidayName -contains $CH.HolidayName) {
# Write-Error "既存の定義名と重複しています。別の名称を指定してください。"
# $Fail = $True
# } else {
        $CHT += $CH
        Update-HolidaysTemplate -CustomTemplate $CHT -TemplateType Custom
# }

    
    if ($Fail -eq $False) {
        Write-Host "定義を追加しました。"
        $CH
    }
}


function Remove-CustomHoliday {
    <#
    .SYNOPSIS
    指定された定義を、カスタムテンプレートから削除します。
 
    .DESCRIPTION
    指定された定義を、カスタムテンプレートから削除します。
 
    .EXAMPLE
    Remove-CustomHoliday -Name "メーデー"
     
    .EXAMPLE
    Remove-CustomHoliday -ID "079621da-bcd3-48fc-ad35-d510d5ddecaa"
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Justification="I like write-host.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Create variable type of Object of HolidayTemplate style.")]
    param(
        [Parameter(Mandatory=$False,Position=1)]
        [String]$Name,

        [Parameter(Mandatory=$False,Position=2)]
        [String]$ID        
    )


    $CHT = Get-HolidaysTemplate -TemplateType Custom

    $Removed = ""
    
    if ($Name -ne "" -and $CHT.HolidayName.IndexOf($Name) -ne -1) {
        $Removed = $CHT[($CHT.HolidayName.IndexOf($Name))]
        $CHT = $CHT | Where-Object {$_.HolidayName -ne $Name}
        
        
        if ($null -eq $CHT) {
            Reset-HolidaysTemplate -Custom
        } else {
            Update-HolidaysTemplate -CustomTemplate $CHT -TemplateType Custom
        }

        Write-Host "指定された定義をカスタムテンプレートから削除しました。"
        $Removed
    }

    if ($ID -ne "" -and $CHT.HolidayID.IndexOf($ID) -ne -1) {
        $Removed = $CHT[($CHT.HolidayID.IndexOf($ID))]
        $CHT = $CHT | Where-Object {$_.HolidayID -ne $ID}
        
        if ($null -eq $CHT) {
            Reset-HolidaysTemplate -Custom
        } else {
            Update-HolidaysTemplate -CustomTemplate $CHT -TemplateType Custom
        }
        
        Write-Host "指定された定義をカスタムテンプレートから削除しました。"
        $Removed
    }
    
    if ($Removed -eq "") {
        Write-Error ("指定された定義が見つかりませんでした。 {0}{1}" -f $Name,$ID)
    }

}



function New-CustomHoliday {
    <#
    .SYNOPSIS
    祝日/休日定義を作成します。
 
    .DESCRIPTION
    祝日/休日定義を作成します。
    祝日/休日定義の配列を構成し、Update-HolidaysTemplate のパラメータとしてカスタムな祝日定義テンプレートとして保存することが出来ます。
     
 
    .EXAMPLE
    New-CustomHoliday -Name "メーデー" -Date "5/1"
 
    .EXAMPLE
    $CHT = Get-HolidaysTemplate
    $CHT += New-CustomHoliday -Name "メーデー" -Date "5/1"
    Update-HolidaysTemplate -CustomTemplate $CHT
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>


    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingInvokeExpression", "", Justification="Requirement this function.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Create variable type of Object of HolidayTemplate style.")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "Year", Justification="Year variable used only object displayed.")]
    param(
        [Parameter(Mandatory=$True,Position=1)]
        [String]$Name,

        [Parameter(Mandatory=$False,Position=2)]
        [String]$Date,

        [Parameter(Mandatory=$False,Position=2)]
        [String]$Expression,

        [Parameter(Mandatory=$false,Position=3)]
        [Switch]$SubstituteEnable,

        [Parameter(Mandatory=$false,Position=4)]
                [ValidateSet('Holiday','Anniversary','Event','PublicHoliday')]
        [String]$Type="Holiday",

        [Parameter(Mandatory=$false,Position=5)]
        [String]$Locale="Japan",

        [Parameter(Mandatory=$false,Position=6)]
        [String]$StartDate=(Get-Date -Format "yyyy/01/01"),

        [Parameter(Mandatory=$false,Position=7)]
        [String]$EndDate=(Get-Date -Format "yyyy/12/31")
        
    )

    $LastDay = @(0,31.28.31,30,31,30,31,31,30,31,30,31)
    $Fail = $False
    

    $Holiday = New-Object PSObject
    $Holiday | Add-Member -MemberType NoteProperty -Name HolidayName -Value ($Name)
    $Holiday | Add-Member -MemberType NoteProperty -Name HolidayID -Value ((New-Guid).Guid)
    $Holiday | Add-Member -MemberType ScriptProperty -Name Date -Value { $Year = [string](Get-Date).Year; Invoke-Expression ($this.Expression) }


    if ($Date -ne "") {
        if ($Date  -match "^[0-9]+/[0-9]+$") {
            $Work = $Date.Split("/")
            if ([int]$Work[0] -ge 1 -and [int]$Work[0] -le 12 -and [int]$Work[1] -ge 1 -and [int]$Work[1] -le $LastDay[($Work[0])]) {
                $Holiday | Add-Member -MemberType NoteProperty -Name Expression -Value ('Get-Date -Date ($Year + "/{0}/{1}")' -f $Work[0],$Work[1])
            } else {
                Write-Error "日付指定が異常です。 mm/dd で指定してください。"
                $Fail = $True
            }
        } else {
            Write-Error "日付指定が異常です。 mm/dd で指定してください。"
            $Fail = $True
        }
    } else {
        if ($Expression -ne "") {
            $Holiday | Add-Member -MemberType NoteProperty -Name Expression -Value ($Expression)
        } else {
            Write-Error "-Date または -Expression オプションいずれかは必ず指定してください。"
            $Fail = $True
        }
    }
    $Holiday | Add-Member -MemberType NoteProperty -Name Type -Value ($Type)
    $Holiday | Add-Member -MemberType NoteProperty -Name SubstituteEnable -Value ($SubstituteEnable)
    $Holiday | Add-Member -MemberType NoteProperty -Name IsCustom -Value ($True)
    $Holiday | Add-Member -MemberType NoteProperty -Name Locale -Value ($Locale)
    $Holiday | Add-Member -MemberType NoteProperty -Name StartDate -Value (Get-Date $StartDate)
    $Holiday | Add-Member -MemberType NoteProperty -Name EndDate -Value (Get-Date $EndDate)
    
    if ($Fail -eq $False) {
        $Holiday
    }
}



function Update-HolidaysTemplate {
    <#
    .SYNOPSIS
    現在保存済みの祝日/休日定義を更新します。
 
    .DESCRIPTION
    現在保存済みの祝日/休日定義を更新します。
    カスタムな祝日/休日定義を変更する場合に利用します。
    -TemplateType "Custom" が既定の動作です。
    新設された祝日の追加や、廃止された祝日の削除を反映したい場合は "PublicHolidays" を指定してください。
    "Default" を指定することでデフォルトテンプレートを更新することが出来ます。
    Reset-HolidaysTemplate では、"Default" テンプレートで "PublicHolidays" を上書きしてリセットします。
    Reset-HolidaysTemplate -Default では、"Default" テンプレートを破棄して初期値を再生成し、 "PublicHolidays" を上書きします。
     
    Get-Holidays では "PublicHolidays" テンプレートを使用します。
    Get-Holidays -IncludeCustom では "PublicHolidays" と "Custom" テンプレートの複合結果が得られます。
     
 
    .EXAMPLE
    Get-HolidaysTemplate
     
    .EXAMPLE
    Get-HolidaysTemplate -Default
     
    .EXAMPLE
    $CHT = Get-HolidaysTemplate
    $CHT += (New-CustomHoliday -Name "メーデー" -Expression 'Get-Date -Date ($Year + "/5/01")' )
    Update-HolidaysTemplate -CustomTemplate $CHT
 
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Affected this module using XML file only.")]
    param(
        [Parameter(Mandatory=$True,Position=1)]
        [Array]$CustomTemplate,
        
        [Parameter(Mandatory=$false,Position=2)]
                [ValidateSet('Custom','PublicHolidays','Default')]
        [String]$TemplateType="Custom"

        
    )

    
    switch ($TemplateType) {
        "Custom" {
            $CustomTemplate | Export-Clixml -Path $PSScriptRoot\HolidaysTemplate-Custom.xml
        }
        

        "PublicHolidays" {
            $CustomTemplate | Export-Clixml -Path $PSScriptRoot\HolidaysTemplate.xml
        }

        "Default" {
            $CustomTemplate | Export-Clixml -Path $PSScriptRoot\HolidaysTemplate-Default.xml
        }
    }
    

}

function Get-HolidaysTemplate {
    <#
    .SYNOPSIS
    現在保存済みの祝日/休日定義を参照します。
 
    .DESCRIPTION
    現在保存済みの祝日/休日定義を参照します。
    カスタムな祝日/休日定義を変更する場合に利用します。
    -Default オプションを使うとデフォルトテンプレートを参照できます。
 
    .EXAMPLE
    Get-HolidaysTemplate
     
    .EXAMPLE
    Get-HolidaysTemplate -TemplateType PublicHolidays
     
    .EXAMPLE
    $CHT = Get-HolidaysTemplate
    $CHT += (New-CustomHoliday -Name "メーデー" -Expression 'Get-Date -Date ($Year + "/5/01")' )
    Update-HolidaysTemplate -CustomTemplate $CHT
 
 
    .LINK
 
    .NOTES
 
    .INPUTS
    パイプラインからの入力非対応です。
 
    .OUTPUTS
    文字列
 
    #>

    param(
        [Parameter(Mandatory=$false,Position=2)]
                [ValidateSet('Custom','PublicHolidays','Default')]
        [String]$TemplateType="Custom"
        
    )
    
    switch ($TemplateType) {
        "Custom" {
            if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate-Custom.xml) -eq $true) {
                $template = @(Import-Clixml -Path $PSScriptRoot\HolidaysTemplate-Custom.xml)
            } else {
                $template = $null
            }
        }
        "PublicHolidays" {
            if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate.xml) -eq $true) {
                $template = @(Import-Clixml -Path $PSScriptRoot\HolidaysTemplate.xml)
            } else {
                Reset-HolidaysTemplate -Default
                $template = @(Import-Clixml -Path $PSScriptRoot\HolidaysTemplate.xml)
            }
        }
        "Default" {
            if ((Test-Path -Path $PSScriptRoot\HolidaysTemplate-Default.xml) -eq $true) {
                $template = @(Import-Clixml -Path $PSScriptRoot\HolidaysTemplate-Default.xml)
            } else {
                Reset-HolidaysTemplate -Default
                $template = @(Import-Clixml -Path $PSScriptRoot\HolidaysTemplate-Default.xml)
            }
        }
    }
    
    $template
}

Export-ModuleMember -Function Get-NextWeekDay, Reset-HolidaysTemplate, Get-Holidays, New-CustomHoliday, Get-HolidaysTemplate, Update-HolidaysTemplate, Add-CustomHoliday, Remove-CustomHoliday