swiftlang / swift-corelibs-foundation

The Foundation Project, providing core utilities, internationalization, and OS independence
swift.org
Apache License 2.0
5.27k stars 1.13k forks source link

[SR-9886] Linux and Mac DateFormatter don't agree on dates in the far past #3543

Open TellowKrinkle opened 5 years ago

TellowKrinkle commented 5 years ago
Previous ID SR-9886
Radar None
Original Reporter @tellowkrinkle
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Foundation | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: ee3953366f931b0a27407face5fe0778

relates to:

Issue Description:

This code gets different results in Mac vs Linux

import Foundation
let formatter = { () -> DateFormatter in
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en-US")
    formatter.dateStyle = .long
    formatter.timeZone = TimeZone(abbreviation: "UTC")!
    return formatter
}()

print(formatter.date(from: "December 31, 1003")!)
print(formatter.date(from: "December 31, 1003")!.timeIntervalSince1970)

On Mac OS using both swift 4.2 and swift-5.0-DEVELOPMENT-SNAPSHOT-2018-12-06, it prints this:

1003-12-31 00:00:00 +0000
-30483648000.0

On Linux using swift-5.0-DEVELOPMENT-SNAPSHOT-2019-02-05-a-ubuntu18.04 it prints this:

1003-12-31 00:00:00 +0000
-30484166400.0

Somehow they both think it's the same date but the timeIntervalSince1970 is different by 6 days. The Mac OS one matches the numbers output by this website when using this code to convert dates to Julian format, but I have no idea if it's actually correct.

Note that while the output of print is consistent with each platform's DateFormatter, the Swift REPL's printout always matches the one on Linux, meaning that on Mac OS the REPL may print a different value than what you just inputted into the DateFormatter.

belkadan commented 5 years ago

This probably means different versions of ICU. @millenomi, any ideas?

spevans commented 5 years ago

This looks to be caused by the switch to the Gregorian calender on 4Oct 1582 where the date was bumped to 15th Oct and the dates 4-14 dont exist (see https://en.wikipedia.org/wiki/Gregorian_calendar)

This means that all dates since 15 Oct 1582 have the same timeIntervalSince1970 but before that it diverges. This can be seen with the following:

import Foundation
let formatter = { () -> DateFormatter in
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en-US")
    formatter.dateStyle = .long
    formatter.timeZone = TimeZone(abbreviation: "UTC")!
    return formatter
}()

let day1Diff = formatter.date(from: "Oct 15, 1582")!.timeIntervalSince1970 - formatter.date(from: "Oct 14, 1582")!.timeIntervalSince1970
print("day1Diff:", day1Diff)

let day10Diff = formatter.date(from: "Oct 15, 1582")!.timeIntervalSince1970 - formatter.date(from: "Oct 5, 1582")!.timeIntervalSince1970
print("day10Diff:", day10Diff)

let day11Diff = formatter.date(from: "Oct 15, 1582")!.timeIntervalSince1970 - formatter.date(from: "Oct 4, 1582")!.timeIntervalSince1970
print("day11Diff:", day11Diff)

macOS (Xcode 10.2b2 with swift 5.0 2019-02-04 toolchain) gives:

day1Diff: -777600.0
day10Diff: 0.0
day11Diff: 86400.0

swift-DEVELOPMENT-SNAPSHOT-2019-02-19-a-ubuntu18.04 and swift-5.0-DEVELOPMENT-SNAPSHOT-2019-02-19-a-ubuntu18.04 on Linux both give:

day1Diff: 86400.0
day10Diff: 864000.0
day11Diff: 950400.0

As to who is correct is subjective, but the Linux one (which uses ICU v61) is probably more consistent with expecting the timeIntervalSince1970 to reduce by approximately 86400 secs per day.