Search code examples
iosswiftdatensdateformatterdateformatter

Swift date formatter wrongfully converting/creating dates/strings


Greetings there m'ladies, m'lords!

Long story short I have a helper struct where I have some helper functions for creating date formatters, Date objects and String representations of dates. I've debugged and narrowed it down to this:

func getFormattedString(from stringDate: String,
                                   with timeZoneID: String? = nil,
                                   appFormat appDateFormat: DateFormats = .api,
                                   apiFormat apiDateFormat: DateFormats = .api) -> String? {
        //this must use the default utc timezone so pass in the nil value here
        let apiDateFormatter = dateFormatter(with: apiDateFormat, timeZoneID: nil)
        if let utcDate = apiDateFormatter.date(from: stringDate) {
            let appDateFormatter = dateFormatter(with: appDateFormat, timeZoneID: timeZoneID)
            return appDateFormatter.string(from: utcDate)
        }
        return nil
    }

For the dateFormatter(with:) use this function:

enum DateFormats: String {
    case api = "YYYY-MM-dd HH:mm:ss"
    // more cases here
}

let utcTimeZone = TimeZone(abbreviation: "UTC")

func dateFormatter(with dateFormat: DateFormats = .api, timeZoneID: String?) -> DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = dateFormat.rawValue
        if let timeZoneID = timeZoneID {
            if let timezoneIdentifier = TimeZone(identifier: timeZoneID) {
                dateFormatter.timeZone = timezoneIdentifier
            } else if let abbreviatedTimezone = TimeZone(abbreviation: timeZoneID) {
                dateFormatter.timeZone = abbreviatedTimezone
            } else {
                dateFormatter.timeZone = utcTimeZone
            }
        } else {
            dateFormatter.timeZone = utcTimeZone
        }
        return dateFormatter
    }

Basically I get a string from the server and it is in UTC, so I create my Date object from that string in the UTC timezone. Then I'm creating another date formatter which this time uses the timezone that comes from the server. Everything's been going great so far, but I've recently come across a weird case.

Let's say we have the following strings from the "server": let start = "2021-12-25 00:00:00" let end = "2021-12-26 23:59:59"

When we're calling getFormattedString(from: start) it gives us a string that represents the same date (year, month and day), but if we're calling it for 26th of Dec of any year the string created by the date formatter will autoincrement the year from 2021 to 2022... I've seen this only happens for the following dates: from 26th to 31st of December, including 26 and 31. But for any other months this does NOT occur.

I'm really wondering if I've been doing something wrong from the very start or if this is indeed a very edge case bug?

I have to display dates and string dates based on the timezone of an event, combined with the fact that from the server we get UTC timezone based dates and there's a calendar which has to show everything from UTC to the event's correct timezone while also ensuring the whole calendar may change based on the user's current timezone. I know, working with dates is not pleasant, but thing is I'm 100% positive the code is working properly. I mean it's a feature that's been in production for years and no one ever rose this issue (thankfully that'a period where everyone's spending time not working, unlike me lol). Honestly, I don't even know how I've come across this super edge case...

Any help or suggestion would mean a lot. Thanks everyone in advance :)


Solution

  • The issue was the uppercased "YYYY", for there should be lowercased ones, as in "yyyy". The solution. Many thanks to m'lord Joakim.