0

I am finding the weirdest bug in DateFormatter's .date(from: String) function in that it doesn't seem to like 1895-01-01 specifically. Dates before and after work but for that particular day the date is nil.

I can take the 1894-12-31 and add a day to show that the underlying NSDate is fine -- just seems to be the DateFormatter conversion.

Here is the playground code to demonstrate.

import Foundation

let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"

let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
let date1 = fmt.date(from: "1894-12-31")
let date2 = fmt.date(from: "1895-01-01")
let date3 = fmt.date(from: "1895-01-02")
let calcDate2 = date1?.addingTimeInterval(86400)

print("Date 1: \(date1)")
print("Date 2: \(date2)")
print("Date 3: \(date3)")
print("\nCalculated Date 2: \(calcDate2)")

Output is:

Date 1: Optional(1894-12-31 05:17:32 +0000)
Date 2: nil
Date 3: Optional(1895-01-02 05:00:00 +0000)

Calculated Date 2: Optional(1895-01-01 05:17:32 +0000)

I also found a prior issue issue about weird behaviour with time being added around 1895 so perhaps this is somehow related?

Is this a bug? Any idea how I can get around this when JSON decoding since I can't know what dates my data will contain? So far I'm just using it as a string in my code but that isn't going to work for long.

  • There was a WWDC video few years ago where the presenter shows that in certain time zones things like DST adjustments where conducted at 00:00. his general advice was, to do such calculation during other times, like midday – vikingosegundo Feb 26 '22 at 13:04
  • Now there wasn't DST in 1895, but some other alterations? – vikingosegundo Feb 26 '22 at 13:07
  • what TZ are you located in? – vikingosegundo Feb 26 '22 at 13:07
  • around that times TZs where introduced to the US by railway companies. might be related. – vikingosegundo Feb 26 '22 at 13:11
  • voted to reopen: the other question's problem was do to language, that shouldn't be the case here. – vikingosegundo Feb 26 '22 at 13:18
  • @vikingosegundo no the language doesn’t matter. When you set the calendar it will return the first available time after midnight regardless of the language. – Leo Dabus Feb 26 '22 at 13:21
  • Well, for the question it does. this is a different question — though the answer might be the same. – vikingosegundo Feb 26 '22 at 13:23
  • Btw you should always set the calendar to avoid parsing the date string year using the device’s calendar. If the device calendar is set to Buddhist it will result in 500 years offset. – Leo Dabus Feb 26 '22 at 13:24
  • @Jim for the first date there has been an adjustment for 17.5 Minutes. probably standard time was adopted officially in your location a 1/1/1895 — or something similar. – vikingosegundo Feb 26 '22 at 13:28
  • Thanks everyone... I'm in Toronto/New York timezone, but because I'm not using time in this example I'm not sure if that should really matter. The post I referenced did comment on the time adjustment messing up their calculations. In this case, I'm actually receiving "1895-01-01 00:00:00" from the API, I just dropped the time for this example to simplify since it still causes the same error. – Jim Sutcliffe Feb 26 '22 at 23:05
  • You got me thinking and I tried something... If I include time then any value from "1895-01-01 00:00:00" through "1895-01-01 00:17:31" fails. I guess it is because of the time shift those times don't actually exist. Now I have to solve the JSON decode issue since I don't control this data. Even with the field defined as optional JSON fails. Any suggestions on how to approach? – Jim Sutcliffe Feb 26 '22 at 23:10

1 Answers1

0

Ok, the answer is that because the use of railroads required better time keeping a number of states and provinces adopted timezones in 1895 as suggested by Fleming several years earlier.

This established Pacific, Mountain, Central, and Eastern (where I am) timezones. As a result there was an adjust to Eastern of 00:17:32 required. So the clock went from 23:59:59 to 00:17:32 at New Years.

This can be shown with the following code:

import Foundation

let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd HH:mm:ss"

let date1 = fmt.date(from: "1894-12-31 23:59:59")
let date2 = fmt.date(from: "1895-01-01 00:00:00")
let date3 = fmt.date(from: "1895-01-01 00:17:31")
let date4 = fmt.date(from: "1895-01-01 00:17:32")

print("Date 1 (1 sec to midnight): \(date1)")
print("Date 2 (midnight): \(date2)"). // invalid
print("Date 3 (17:31 past midnight): \(date3)"). // invalid
print("Date 4 (17:32 past midnight: \(date4)")

let calc1 = date1?.addingTimeInterval(1)
print("Date 1 + 1 second (midnight): \(calc1)")

Output is (for EST):

Date 1 (1 sec to midnight): Optional(1895-01-01 05:17:31 +0000)
Date 2 (midnight): nil
Date 3 (17:31 past midnight): nil
Date 4 (17:32 past midnight: Optional(1895-01-01 05:17:32 +0000)
Date 1 + 1 second (midnight): Optional(1895-01-01 05:17:32 +0000)

Thanks to all that helped.

The solution for actual usage is to add the "Z" time zone to the end of the string format and likewise append the "Z" to the data from the API I'm calling. That forces it all to GMT which doesn't suffer from the problem.

Thanks all for the assistance