iosswiftdatefscalendar

FSCalendar select day selects previous day at 23:00


I'm using FSCalendar in a Swift app, and when user selects a day, I'm printing the selected day, and the it prints the previous day, at 23:00. I'm not sure why and how can I solve this. I'm in spain. Maybe it's related with where you are and your local hour?

This is how I'm printing the selected day:

extension CalendarDataViewViewController: FSCalendarDataSource {    
    func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
        let df = DateFormatter()
        df.dateFormat = "yyyy-MM-dd hh:mm:ss"
        let now = df.string(from: date)
        logger.debug("Date: \(date)")
       
    }    
}

And this is what it's printed when I select 18 march:

21:01:24.646 💚 DEBUG CalendarDataViewViewController.calendar():258 - Date: 2021-03-17 23:00:00 +0000

Solution

  • Your code creates a date formatter, converts the returned date to a date string with that formatter, and then ignores that and simply prints the date, which is being displayed in UTC. (Note the output Date: 2021-03-17 23:00:00 +0000)

    Change your log command to read:

        logger.debug("Date: \(now)")
    

    And by the way, the variable name now is a terrible choice for holding a user-selected date that is not the current date.

    I'd suggest renaming the returned date parameter selectedDate and the String output of the formatter as selectedDateString


    Edit:

    Consider this code:

    import Foundation
    
    func dateStringFromDate(_ inputDate: Date) -> String {
        let df = DateFormatter()
        df.dateFormat = "yyyy-MM-dd hh:mm:ss a"
        let dateString = df.string(from: inputDate)
        return dateString
    }
    
    func isoDateStringFromDate(_ inputDate: Date) -> String {
        let df = ISO8601DateFormatter()
        df.formatOptions = .withInternetDateTime
        df.timeZone = TimeZone.current //Force the formatter to express the time in the current time zone, including offset
        let dateString = df.string(from: inputDate)
        return dateString
    }
    
    let now = Date()
    print("Current timezone = \(TimeZone.current)")
    print("now in 'raw' format = \(now)")
    let localizedDateString = DateFormatter.localizedString(from: now,
                                                            dateStyle: .medium,
                                                            timeStyle: .medium)
    print("localizedString for the current date = \(localizedDateString)")
    print("dateStringFromDate = \(dateStringFromDate(now))")
    print("isoDateStringFromDate = \(isoDateStringFromDate(now))")
    

    Right now, at about 9:16 PM EDT on Thursday March 18th, that logs the following:

    Current timezone = America/New_York (current)
    now in 'raw' format = 2021-03-19 01:16:52 +0000
    localizedString for the current date = Mar 18, 2021 at 9:16:52 PM
    dateStringFromDate = 2021-03-18 09:16:52 PM
    isoDateStringFromDate = 2021-03-18T21:16:52-04:00

    The 'raw' date format is in GMT, with an offset value of 0. In that form, in GMT, the calendar date is already March 19th. (Because GMT is 4 hours ahead of EDT)

    The class function NSDateFormatter.localizedString(from:dateStyle:timeStyle) displays a date in the current time zone and using the device's locale settings. The dateStyle and timeStyle parameters give you the option to choose whether or not, and in what format (short, medium, or long) to display the date or time.

    An ISO8601DateFormatter displays the date following the conventions in the ISO8601 standard. The isoDateStringFromDate(:) function above uses the .withInternetDateTime option to express the date in the ISO8601 "internet date and time" format. I forced that date to be in the local time zone, so it displays the date with a -4 hour offset from GMT (since it is EDT, eastern daylight savings time where I live.)

    The function dateStringFromDate(_:) is a slight variation on your function. It returns a date string in the current time zone, using 12 hour times and an AM/PM string.