1

I have a double value that represents the number of days since December 30, 1899 (the usual TDateTime value in Delphi). For example: 43854.4410269444

Is it possible to create a valid Date value from this double?

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
vitaliy-zh
  • 185
  • 9

2 Answers2

2

One way is to get a Date representing "1899-12-30", then call addingTimeInterval.

// either parse a date string...
let formatter = DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd"

let epoch = formatter.date(from: "1899-12-30")!
// or calculate the time directly from 1970
//let epoch = Date(timeIntervalSince1970: -86400 * 25569)

// now we add the quality of the number of days times the number of seconds in a day
let numberOfDays = 43854.4410269444
let result = epoch.addingTimeInterval(86400 * numberOfDays)

enter image description here

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • I am not familiar with TDateTime, so I just wonder: Do you have to take daylight saving time into account? A day does not necessarily have 86400 seconds. – Martin R Feb 01 '20 at 13:32
  • @MartinR I’m not either. But judging by its representation (some unit of time since an epoch) and it’s name, I would guess that it would be designed in a way that is easier to implement. I.e not taking into account DST. – Sweeper Feb 01 '20 at 13:37
  • @MartinR also, to take into account DST, we’d also need a timezone, which OP has not provided, so I just assumed that OP knows what they are doing. – Sweeper Feb 01 '20 at 13:48
1

Here's my attempt to solve the problem (see below how I calculated the magic number):

extension Date {
    init(fromDelphiTDateTime delphiDate: Double) {
        //Number of seconds between 12/30/1899 12:00 AM and 1/1/1970 12:00 AM
        let tDateTimeUnixTimeOffset = 2209161600.0 

        //24 * 60 * 60
        let numberOfSecondsInDay = 86400.0 

        let delphiTDateTimeAsUnixTime = delphiDate * numberOfSecondsInDay - tDateTimeUnixTimeOffset
        self.init(timeIntervalSince1970: delphiTDateTimeAsUnixTime)
    }
}

I assumed that TDateTime doesn't have any timezone information. Since the Unix time (timeIntervalSince1970) is in UTC, you'll need to convert your TDateTime value to UTC if it's in a different time zone.

Examples:

//Dec 30, 1899 at 12:00 AM
let date1 = Date(fromDelphiTDateTime: 0)

//Jan 24, 2020 at 11:04 AM
let date2 = Date(fromDelphiTDateTime: 43854.4410269444)

And here's how I calculated the magic number 2209161600.0 using Swift:

let zeroTDateTimeComponents = DateComponents(calendar: Calendar.init(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0), year: 1899, month: 12, day: 30, hour: 0, minute: 0, second: 0)
let zeroTDateTime = zeroTDateTimeComponents.date
let tDateTimeUnixTimeOffset = zeroTDateTime?.timeIntervalSince1970 //the value is 2209161600.0

Hope this helps and I'd be grateful if the community validates or further improves my answer. Some unit tests with the existing know pairs of TDateTime and its double value would certainly help.

Vadim Belyaev
  • 2,456
  • 2
  • 18
  • 23
  • Thank You for Your effort, but the result values are wrong. For example: 43854.4410269444 = Jan 24, 2020 at 09:04 AM instead of Jan 24, 2020 at 11:35 AM (correct value). – vitaliy-zh Feb 01 '20 at 18:56
  • Thanks for pointing that out, I think I found my mistake. I didn't pass the time zone to the DateComponents initializer and it apparently created a date in my current time zone (which is UTC+3), hence the roughly 3 hour difference. When I explicitly pass the time zone there, the results seems to match Sweeper's example. I've corrected my answer based on my findings just in case someone decides to use it and I'm happy to see that your problem got solved :) – Vadim Belyaev Feb 01 '20 at 21:42