Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a date by string format to nsdate result one day before string date

Tags:

ios

nsdate

I searched about my problem but I did not find any help related...

The story: The user create a continues event by name, start date, end date and note, so saves all fields on the Sqlite database. The both dates format are "yyyy-MM-dd". After that, when user want to do an action on event, the app checks the date that user selected by start and end date and if the entered date is before start date or after end date notifies user.

The problem: I, as an user, created an event from 18-8-2012 to 18-9-2012. Then I put log on 18-8-2012 and I was notified. I debugged app. and I was amazed. the date object from string with format "yyyy-MM-dd" is "2012-08-17 19:30:00 +0000". The code is:

           #define kPickerDate @"yyyy-MM-dd"
           NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
           [formatter setDateFormat:kPickerDate];
           NSDate *sDate = [formatter dateFromString:start_date]; 
           NSDate *eDate = [formatter dateFromString:exam_date]; 

How does app that??????????????

like image 776
Fa.Shapouri Avatar asked Dec 21 '22 16:12

Fa.Shapouri


2 Answers

An NSDate is not just a date, but also a time.

You need to check this at every stage:

  • Is your date being built correctly when parsed? It should be midnight UTC, rather than midnight local. You're probably using a NSDateFormatter for this. Make sure it's time zone is set to UTC.
  • Are your dates being stored in SQLite correctly? Again, make sure you're storing it as UTC. If you're parsing it correctly, though, this should work.
  • Are you formatting the output correctly? probably using a NSDateFormatter for this. Make sure it's time zone is set to UTC.

Only once all three pieces are perfect will you be free of potential bugs here.

To set the time zone of a date formatter, use:

formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];

Do not try to work around this by adopting local time.

The problem with local times:

  • In many time zones the time changes a couple times a year with Daylight Savings Time. That one hour difference will throw the date part of your times onto a previous day!
  • If the user is in a time zone without Daylight Savings Time, they may travel to a different time zone. Again, the date part of your times may be thrown off a day.

The only safe thing to do is use UTC everywhere.

(I actually kind of pity people who live in UTC; they're not forced to shake these bugs out of their apps!)

like image 66
Steven Fisher Avatar answered May 20 '23 05:05

Steven Fisher


You can create an NSDate object using a better method. First amend the @interface for NSDate (you can do this anywhere). To do this, put this in your header file.

@interface NSDate (missingFunctions) 
+ (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
@end

Then in the main file put:

@implementation NSDate (missingFunctions)
+ (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *components = [[NSDateComponents alloc] init];
    [components setYear:year];
    [components setMonth:month];
    [components setDay:day];

    //BUT MOST IMPORTANTLY:
    [components setTimeZone:[NSTimeZone localTimeZone]];
    [components setHour:hour];
    [components setMinute:minute];
    [components setSecond:second];

    return [calendar dateFromComponents:components];
}   
@end

Then from anywhere in code call (for example):

[NSDate dateWithYear:1969 month:8 day:15];

And it will return an NSDate as per your request in the right time zone. You can change which time zone to use also by reading more about NSTimeZone at the official apple docs.

Note I can't take credit for writing this code, but I can vouch for it working.

like image 28
achi Avatar answered May 20 '23 05:05

achi