Bug Description:
Setup a basic calendar, when you pick a date (e.g 15th of April) the value of date as provided to the calendar:didSelectDate delegate method is the 14th of April... same for all days (all are one day behind)
Integrated through cocoapods (v2.5.1)
Tested in an iPhone 7 simulator, ran through xcode 8.2.1
Using v2.5.1 because one of our developer started the project with this version and edited inside the code (I think this would have been avoidable by providing a custom class for the cell but not so sure) anyways the changes can be seen here: https://github.com/Eweev/FSCalendar/tree/MastrowRequirements
Thanks
The returned date is in UTC, not your local timezone. Check the time component of the date. Set your NSDateFormatter to your local timezone, the date will be displayed as midnight on the correct date.
@CodeWriter23 the time is indeed in UTC but that shouldn't be an issue.
Also you suggest to set my NSDateFormatter, but the date that is coming wrong is the date of this Delegate:
So given this is the date provided by the Calendar I'm not sure how/where to perform what you suggested ?
I see your point and now understand why you insist on having the date converted with a formatter. The problem is if I do the following:
-> get the date from (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date and store it in an array (at that moment the calendar lights up the right day (i.e 20th if I picked the 20th)
-> When I leave the calendar and show it again and initialize it with [_calendar selectDate:date scrollToDate:NO]; (date being the stored date in the previous step) then the day that lights up is the 19th and not the 20th as it should.
Shouldn't that case simply work out of the box with no conversion ?
Here's the gotcha. The UTC date delivered via the delegate protocol has a time component to it. Basically it is the time and date in UTC, of midnight in your timezone. For example, for myself residing in PDT (UTC-0700), if I tap April 16, 2017, I get April 16, 2017 07:00 UTC, which is midnight PDT. If your timezone is UTC+0100 you'll get April 15, 2017 23:00 UTC.
So to answer your question, it sounds like you're losing the time and timezone components in between capturing the date via "...didSelectDate..." and initializing the calendar. What method are you using to store the date?
Yup about the time I noticed that, I'm at GMT+2 so I always see the time at 22:00 one day before.
Now to store the date I'm simply adding the date object delivered via the delegate into an NSMutableArray and on initialization I iterate over the array to set the dates that are in the array.
So basically I directly store NSDate objects and use them as is.
@CodeWriter23 @WenchaoD Well now that's getting strange.
According to this thread I thought that [_calendar selectDate:date scrollToDate:NO]; would dismiss the time component and simply focus on the date one (day, month, year).
Following this logic I shifted the time of the returned date before storing it so it reads the correct day. For exemple, If I pick the 20th of April, calendar's delegate return the 19th at 10pm, I shift it back to 20th (but keep it UTC) and store it.
Then when I loop over my elements I extract NSDates that are on the correct day (i.e 20th of April) and set the date with [_calendar selectDate:date scrollToDate:NO]; yet the calendar still lights up the 19th !
I then tried with different times on the 20th (i.e midnight and 4am to make sure there's no other timezone offset being applied here...) but that didn't fix it.
Any idea ?
Ok I've debugged it to see what's going on and it seems that the following line is "messing" things up:
NSDate *targetDate = [self.gregorian dateBySettingHour:0 minute:0 second:0 ofDate:date options:0];
Line 1154 of FSCalendar.m (method - (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate forPlaceholder:(BOOL)forPlaceholder)
After this call my date is reverted back to the 19th at 21h (even the offset is different from the one applied to the delegate... depends on Daylight Time Saving offset)
What I think is happening is that this function is also considering that the time given is in my local timezone and converts it again to UTC.. and given it is stripping the time components it assumes the time given is midnight 20th my timezone and thus converts it to UTC...
Now this doesn't make much sense to me given the date selection is already converted to UTC AND that a user is expecting to see the day corresponding to the date he provided to turn on (in his timezone).
Because I don't know all the ins and outs of FSCalendar I'm not so sure on how to fix this (don't want to break something else) but specifying the timezone of the self.gregorian calendar to UTC fixed my issue..
So I now have:
FSCalendarAssertDateInBounds(date,self.gregorian,self.minimumDate,self.maximumDate);
[self.gregorian setTimeZone:[NSTimeZone timeZoneWithName:@"UTC" ]];
NSDate *targetDate = [self.gregorian dateBySettingHour:0 minute:0 second:0 ofDate:date options:0];
My question is, is this a bug or am I doing something wrong and my fix is just making things worst ?
I am also facing the same issue.
@jfayad I was able to solve the problem by simply adding the time difference to the obtained date as a quick fix.
func calendar(_ calendar: FSCalendar, didSelect date: Date) {
let newDate = date .addingSeconds(NSTimeZone.local.secondsFromGMT())
}
I am also facing the same issue.
@ashishprasadgit Thank you for your code.
you save my day. :)
For anyone who will come here from Google. You don't need to add the time difference or edit the calendar code. You just need to set your DateFormatter's timeZone to the time zone you need. For example, to display the date in your system time:
dateFormatter.timeZone = TimeZone.current
let dateString = dateFormatter.string(from: date)
In addition to the answer of @ashishprasadgit, since secondsFromGMT() uses the current date as default, you should explicitly use the selected date to compute the time difference :
let newDate = date.addingTimeInterval(TimeInterval(TimeZone.current.secondsFromGMT(for: date))
In addition to the answer of @ashishprasadgit, since
secondsFromGMT()uses the current date as default, you should explicitly use the selected date to compute the time difference :let newDate = date.addingTimeInterval(TimeInterval(TimeZone.current.secondsFromGMT(for: date))Thanks it help me. :)
Most helpful comment
I am also facing the same issue.
@jfayad I was able to solve the problem by simply adding the time difference to the obtained date as a quick fix.
func calendar(_ calendar: FSCalendar, didSelect date: Date) { let newDate = date .addingSeconds(NSTimeZone.local.secondsFromGMT()) }