PHPerだったので、これで済んでいた計算をGoに置き換えようとして、ぱっとライブラリが無さそうだったので真面目に実装した。
<?php
echo date_create("fourth saturday of this month")->format("c");
使う素材は公式のtimeパッケージのみとする。
考えたアルゴリズム
- 今月の1日を取得する
- 1日の曜日を調べて、土曜日までの日数を出す
- 最初の土曜日を計算
- 7日 * 3 = 21日うしろが第4土曜日のはず
package main
import (
"errors"
"fmt"
"time"
)
func main() {
jst := time.FixedZone("Asia/Tokyo", 9*60*60)
fmt.Println(NthSaturday(4, jst))
}
func NthSaturday(n int, loc *time.Location) (time.Time, error) {
now := time.Now().In(loc)
if n <= 0 || 4 < n {
return now, errors.New("there is no 5th saturday.")
}
year, month, _ := now.Date()
firstDay := time.Date(year, month, 1, 0, 0, 0, 0, loc)
firstSaturday := int(time.Saturday-firstDay.Weekday()) + 1
nthSaturday := firstSaturday + (n-1)*7
return time.Date(year, month, nthSaturday, 0, 0, 0, 0, loc), nil
}
汎用化して、任意の曜日で計算できるようにすると、こんな感じかな。
package main
import (
"errors"
"fmt"
"time"
)
func main() {
jst := time.FixedZone("Asia/Tokyo", 9*60*60)
fmt.Println(NthWeekday(4, time.Monday, jst))
}
func NthWeekday(n int, wd time.Weekday, loc *time.Location) (time.Time, error) {
now := time.Now().In(loc)
if n <= 0 || 4 < n {
return now, errors.New("there is no 5th weekday.")
}
year, month, _ := now.Date()
firstDay := time.Date(year, month, 1, 0, 0, 0, 0, loc)
first := int(wd-firstDay.Weekday()) + 1
if first <= 0 {
first += 7
}
nth := first + (n-1)*7
return time.Date(year, month, nth, 0, 0, 0, 0, loc), nil
}
第5曜日を計算しようと思ったら、月によっては存在しない可能性があるので、エラーチェックがもう少し必要になる。