TL;DR
DateFormatterを使い isLenient = false
に設定する。
func makeDate(year: Int, month: Int, day: Int) -> Date? {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.isLenient = false
formatter.dateFormat = "yyyy-MM-dd"
return formatter.date(from: String(format: "%d-%02d-%02d", year, month, day))
}
let date = makeDate(year: 2021, month: 2, day: 29)
// -> nil
やりたいこと
例えば年と月と日を別々に入力するUIがあってそこからDate型を生成したいとき、ユーザーが2021年2月29日のような存在しない日付を入力したらnilを返したい。
❌ DateComponentsを使う方法
func makeDate(year: Int, month: Int, day: Int) -> Date? {
var components = DateComponents()
components.calendar = Calendar(identifier: .gregorian)
components.timeZone = TimeZone(secondsFromGMT: 0)
components.year = year
components.month = month
components.day = day
return components.date
}
let date = makeDate(year: 2021, month: 2, day: 29)
// -> 2021年3月1日
自動で日が進んで別の日付を返してしまう。
❌ DateFormatterを使ってisLenientを指定しない方法
func makeDate(year: Int, month: Int, day: Int) -> Date? {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd"
return formatter.date(from: String(format: "%d-%02d-%02d", year, month, day))
}
let date = makeDate(year: 2021, month: 2, day: 29)
// -> 2021年3月1日
自動で日が進んで別の日付を返してしまう。
ただし、32日を指定するとnilが返ってくる。
この挙動はDateComponentsとは異なる。
let date = makeDate(year: 2021, month: 2, day: 32)
// -> nil
環境
Swift 5.1
謝辞
@kishikawakatsumi さんに教えていただきました。ありがとうございました。