0 Replies
      Latest reply on Mar 30, 2020 10:33 AM by foover
      foover Level 1 Level 1 (0 points)

        When testing some code which parses an Iso8601 duration string and generates a TimeInterval, I came across some interesting behavior, which I take to be bug, but I may have missed some setup which would set things right.  The short of it... Given the Iso8601 duration string, e.g., "P10675199DT2H48M5.4775" which has been parsed into DateComponents, use Calendar.current.date(byAdding:to:) to get a future date.  When futureDate.timeIntervalSince(currentDate) is calculated, the interval returned MAY be different from the result of a manual calculation - off by one hour, or incorrect fractional seconds.  The inital test was run using a number-of-days value for ~30k yrs.  However, using other, smaller values can produce errors as well.  Copy/Paste the following code into a playground to check me.  Anyone know what might be happenging?

         

        import Cocoa
        
        var str = "Hello, playground"
        
        
        // Given the Iso8601 duration string "P10675199DT2H48M5.4775"  (Yeah, made up for testing.  Who really cares about a duration of ~30k yrs. Yikes!)
        let nanoSecs = Int(0.4775 * Double(NSEC_PER_SEC))
        let components = DateComponents(calendar: nil, timeZone: nil, era: nil, year: nil, month: nil, day: 10675199, hour: 2, minute: 48, second: 5, nanosecond: nanoSecs, weekday: nil, weekdayOrdinal: nil, quarter: nil, weekOfMonth: nil, weekOfYear: nil, yearForWeekOfYear: nil)
        //let components = DateComponents(calendar: nil, timeZone: nil, era: nil, year: nil, month: nil, day: 20*365, hour: 2, minute: 48, second: 5, nanosecond: nanoSecs, weekday: nil, weekdayOrdinal: nil, quarter: nil, weekOfMonth: nil, weekOfYear: nil, yearForWeekOfYear: nil)
        let currentDate = Date()
        var calendarDuration: TimeInterval = 0
        if let futureDate = Calendar.current.date(byAdding: components, to: currentDate) {
            let futureDateComponents = Calendar.current.dateComponents([Calendar.Component.day, Calendar.Component.month, Calendar.Component.year], from: futureDate)
            print(futureDateComponents)
            calendarDuration = futureDate.timeIntervalSince(currentDate)
        }
        
        // Compare against a manual calculation
        let secondsPerDay: TimeInterval = 86400
        let secondsPerHour: TimeInterval = 3600
        let secondsPerMinute: TimeInterval = 60
        
        let calcDuration = (10675199 * secondsPerDay) + (2 * secondsPerHour) + (48 * secondsPerMinute) + 5.4775
        //let calcDuration = ( 20*365 * secondsPerDay) + (2 * secondsPerHour) + (48 * secondsPerMinute) + 5.4775
        
        print(calendarDuration) // 922337207285.4775
        print(calcDuration)     // 922337203685.4775
        print(calendarDuration - calcDuration) // = 3600.0  Off by 1 hour!!
        
        // Interestingly, for some values of the number of days (e.g., 20*365, but not 30*365) the fractinal seconds value starts to go heywire.