1

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
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
NullPointerException
  • 36,107
  • 79
  • 222
  • 382
  • What is your local Timezone? UTC+1? Are you in the UK but now have daylight savings time? When you print a data it always prints as utc, not local time. If your local time is 1 hour ahead of utc then you have the correct date. Try printing `now`. Try printing `date.description(with:Locale.current)` – Paulw11 Mar 18 '21 at 20:10
  • @Paulw11 now prints 18, and date 17, why? I'm in spain, madrid timezone. I need to work with the correct date 18. How can I correct the wrong date 17 and work with a Date with correct day 18 selected? I mean, how can I have a Date object like date but with the correct day? That string was only for testing purposes, I need an object with the correct date for selecting events in the database. – NullPointerException Mar 18 '21 at 20:51
  • And what time zone are you using for the dates in the database? – Joakim Danielson Mar 18 '21 at 21:16
  • Because of time zones. A Date is a fixed point in time. "Now" for me is 8:27 on Friday the 19th because I am in Sydney. For you it would be 22:27 on the 18th, it it is the same point in time. You should always just store dates and convert to local time when you need to display it or any other time that time zone is important. – Paulw11 Mar 18 '21 at 21:29
  • You can use `Calendar` and `DateComponents` to get a specific day in a month relative to a specific Timezone – Paulw11 Mar 18 '21 at 21:30

1 Answers1

3

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.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • now prints 18... why? can you explain me why now is 18 and date is 17? I need to work with the correct date 18. How can I correct the wrong date 17 and work with a Date with correct day 18 selected? – NullPointerException Mar 18 '21 at 20:51
  • I mean, how can I have a Date object like date but with the correct day? That string was only for testing purposes, I need an object with the correct date for selecting events in the database. – NullPointerException Mar 18 '21 at 20:53
  • @NullPointerException a date nas no timezone. It is just a point in time. It might be a different "date" depending on where you are located in the globe. When storing the date as a string in your database you usually store the UTC date description followed by its time zone (+0000) or (Z) which means the same (UTC / zero seconds from GMT) – Leo Dabus Mar 18 '21 at 22:57
  • @NullPointerException Note that your date format is wrong. you are using`hh` (1-12 hours) without am pm info. you should use `HH` (0-23 hours) `"yyyy-MM-dd HH:mm:ss"`. You should also add the timezone to your date string. The common practice is to use ISO8601 with or without fractional seconds. You can check this post [date formatter ISO8601](https://stackoverflow.com/questions/28016578/how-can-i-parse-create-a-date-time-stamp-formatted-with-fractional-seconds-utc/28016692#28016692) – Leo Dabus Mar 18 '21 at 23:03
  • Take a deep breath and hear what we are telling you. The result of the function call `Date()` returns a representation of the current instant in time, anywhere on the planet. It does not have a time zone. That date will be in different calendar days depending on what time zone you express it in. For example, here on the east coast of the US, it is about 9:15 PM on Thursday March 18th. (EDT). however, in Greenwich Mean Time, it is already 1:15 AM on Friday. The exact same moment in time has a different date depending on the time zone. – Duncan C Mar 19 '21 at 01:21