Swift 3 の Calendar で月の最終曜日を取得しようとした時に、一瞬「ん?」となったので覚え書きです。
ついでに、指定した週の曜日や、第何曜日を取得する方法も載せておきましたので、どなたかのお役に立ちましたら。
読み込み中です。少々お待ち下さい
第 n 日曜日~土曜日の取得
まずは、前段として 2017 年 8 月の第 1 日曜日~第 1 土曜日を取得してみましょう。
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
ちなみに、2017 年 8 月を採用した理由は、1 日も最終日も週中で説明に都合が良かったからです。それ以上の意味は、特にありません。
共通して、以下のコードが先頭に記述されているものとします
let cal = Calendar.current
guard let august2017 = cal.date(from: DateComponents(year: 2017, month: 8)) else { return }
print("カレンダーはグレゴリオ暦、週の最初は日曜日であるとします")
print(" Calendar Identifier = \(cal.identifier)")
print(" First Weekday = \(cal.firstWeekday)")
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.calendar = cal
let yearMonthFomatter = DateFormatter()
yearMonthFomatter.dateFormat = "yyyy 年 M 月"
yearMonthFomatter.calendar = cal
目的の年、月を指定した上で、DateComponents.weekdayOrdinal で第何曜日か指定し、更に DateComponents.weekday で曜日を指定します。
var components = cal.dateComponents([.year, .month], from: august2017)
components.weekdayOrdinal = 1 // 第1
print("\n\(yearMonthFomatter.string(from: august2017)) の第 \(components.weekdayOrdinal!) 日曜日 〜 土曜日")
for weekday in 1...7 {
components.weekday = weekday // 曜日
if let date = cal.date(from: components) {
print(" \(cal.weekdaySymbols[weekday - 1]): \(dateFormatter.string(from: date))")
}
}
出力結果
カレンダーはグレゴリオ暦、週の最初は日曜日であるとします
Calendar Identifier = gregorian
First Weekday = 1
2017 年 8 月 の第 1 日曜日 〜 土曜日
日曜日: 2017/08/06
月曜日: 2017/08/07
火曜日: 2017/08/01
水曜日: 2017/08/02
木曜日: 2017/08/03
金曜日: 2017/08/04
土曜日: 2017/08/05
もちろん、weekdayOrdinal に 2 を指定すれば第 2 曜日、4 を指定すれば第 4 曜日が取得できます。
第 n 週目の日曜日~土曜日
続いて、第 1 週目の日曜日~土曜日を取得してみます。
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
目的の年、月を指定した上で、DateComponents.weekOfMonth で第何週目かを指定し、更に DateComponents.weekday で曜日を指定します。
// この上に共通コードが記述されているものとします
var components = cal.dateComponents([.year, .month], from: august2017)
components.weekOfMonth = 1 // 第 1 週目
print("\n\(yearMonthFomatter.string(from: august2017)) の第 \(components.weekOfMonth!) 週目の日曜日 〜 土曜日")
for weekday in 1...7 {
components.weekday = weekday // 曜日
if let date = cal.date(from: components) {
print(" \(cal.weekdaySymbols[weekday - 1]): \(dateFormatter.string(from: date))")
}
}
出力結果
カレンダーはグレゴリオ暦、週の最初は日曜日であるとします
Calendar Identifier = gregorian
First Weekday = 1
2017 年 8 月 の第 1 週目の日曜日 〜 土曜日
日曜日: 2017/07/30
月曜日: 2017/07/31
火曜日: 2017/08/01
水曜日: 2017/08/02
木曜日: 2017/08/03
金曜日: 2017/08/04
土曜日: 2017/08/05
当たり前ですが、weekOfMonth に 2 を指定すれば第 2 週目、4 を指定すれば第 4 週目になります。
最終週の日曜日 〜 土曜日
さらに、最終週の日曜日~土曜日を取得してみましょう。
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
第 n 週を指定する方法は、既に上で見ているので、最終週が何週目かが分かれば良い道理です。これは、Calendar.range で取得できます。
// この上に共通コードが記述されているものとします
if let lastWeekOfMonth = cal.range(of: .weekOfMonth, in: .month, for: august2017) {
var components = cal.dateComponents([.year, .month], from: august2017)
components.weekOfMonth = lastWeekOfMonth.count
print("\n\(yearMonthFomatter.string(from: august2017)) の最終週(第 \(components.weekOfMonth!) 週目)の日曜日 〜 土曜日")
for weekday in 1...7 {
components.weekday = weekday
if let date = cal.date(from: components) {
print(" \(cal.weekdaySymbols[weekday - 1]): \(dateFormatter.string(from: date))")
}
}
}
出力結果
カレンダーはグレゴリオ暦、週の最初は日曜日であるとします
Calendar Identifier = gregorian
First Weekday = 1
2017 年 8 月 の最終週(第 5 週目)の日曜日 〜 土曜日
日曜日: 2017/08/27
月曜日: 2017/08/28
火曜日: 2017/08/29
水曜日: 2017/08/30
木曜日: 2017/08/31
金曜日: 2017/09/01
土曜日: 2017/09/02
最終日曜日 〜 土曜日
さて、いよいよ本題の最終日曜日 〜 土曜日です。
ここまで見てきたように、2017 年の 8 月は 火~木は第 5 までありますが、それ以外の曜日は第 4 までしか存在しませんので、単純に weekdayOrdinal = 5 としても上手く取得できません。
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
当初は、月が変わったかどうかで判断するしかないかと考えていたのですが、どうやら weekdayOrdinal に -1 を指定すれば最終曜日を取得できるようです。
// この上に共通コードが記述されているものとします
var components = cal.dateComponents([.year, .month], from: august2017)
components.weekdayOrdinal = -1
print("\n\(yearMonthFomatter.string(from: august2017)) の最終日曜日 〜 土曜日")
for weekday in 1...7 {
components.weekday = weekday
if let date = cal.date(from: components) {
print(" \(cal.weekdaySymbols[weekday - 1]): \(dateFormatter.string(from: date))")
}
}
出力結果
カレンダーはグレゴリオ暦、週の最初は日曜日であるとします
Calendar Identifier = gregorian
First Weekday = 1
2017 年 8 月 の最終日曜日 〜 土曜日
日曜日: 2017/08/27
月曜日: 2017/08/28
火曜日: 2017/08/29
水曜日: 2017/08/30
木曜日: 2017/08/31
金曜日: 2017/08/25
土曜日: 2017/08/26
ただし、申し訳なくも weekdayOrdinal にマイナスを指定した際の挙動が明記されている正規のドキュメントを発見できなかったので(日などと同様だとは思いますが)、もし心配な場合は月が同一かどうかで判断した方がよいかも知れません。
おわりに
また、大した内容でもないのに、無駄に長くなってしまった......他にもっと美しい方法がありましたら、申し訳ありません。
次回は、31 日が存在する月を取得する方法について書く予定です。