9

I found that DateFormatter date(from:) method can't parse a couple of specific dates. Method returns nil for the 1st april of 1981-1984 years. Is it a bug of Foundation? What can we do to perform parsing of such dates?

Xcode 8.0, iOS SDK 10.0. Here is a screenshot of a short playground example: a screenshot of a short playground example

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
supp-f
  • 1,317
  • 1
  • 9
  • 14
  • Which time zone are you in? Did daylight saving time start at that day? – Martin R Oct 27 '16 at 12:16
  • 2
    It's not a bug of Foundation. I can get the correct value for these dates. This seems an specific configuration that doesn't works (timezone, gregorian calendar...). I'm testing it from London. – Fran Martin Oct 27 '16 at 12:22
  • No, Its not a bug of Foundation. try changing the Timezone and calendar. it should solve your issue. – Wolverine Oct 27 '16 at 12:39
  • @MartinR you are right. This day daylight saving time started those 4 years only. Timezone: Europe/Moscow. How can I parse such dates without changing calendar or timezone? – supp-f Oct 27 '16 at 12:44

1 Answers1

22

This problem occurs if daylight saving time starts exactly on midnight, as it was the case in Moscow in the years 1981–1984 (see for example Clock Changes in Moscow, Russia (Moskva)).

This was also observed in

For example, at midnight of April 1st 1984, the clocks were adjusted one hour forward, which means that the date "1984-04-01 00:00" does not exist in that timezone:

let dFmt = DateFormatter()
dFmt.dateFormat = "yyyy-MM-dd"
dFmt.timeZone = TimeZone(identifier: "Europe/Moscow")
print(dFmt.date(from: "1984-04-01")) // nil

As a solution, you can tell the date formatter to be "lenient":

dFmt.isLenient = true

and then it will return the first valid date on that day:

dFmt.isLenient = true
if let date = dFmt.date(from: "1984-04-01") {
    dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
    print(dFmt.string(from: date)) 
}
// 1984-04-01 01:00:00

A different solution was given by rob mayoff, which is to make the date formatter use noon instead of midnight as the default date. Here is a translation of rob's code from Objective-C to Swift:

let noon = DateComponents(calendar: dFmt.calendar, timeZone: dFmt.timeZone,
               year: 2001, month: 1, day: 1, hour: 12, minute: 0, second: 0)
dFmt.defaultDate = noon.date
if let date = dFmt.date(from: "1984-04-01") {
    dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
    print(dFmt.string(from: date)) 
}
// 1984-04-01 12:00:00
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • The defaultDate trick is good to know. (Voted) The docs on that property are pretty much worthless. How did you figure out what it does? – Duncan C Sep 01 '18 at 19:23
  • @DuncanC: Actually I *guessed* that the `isLenient` property (which is even less documented) helps here. The `defaultDate` solution is just a translation of the linked-to Objective-C code to Swift, that was figure out by Rob Mayoff. – Martin R Sep 01 '18 at 20:38