Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assuming gregorian calendar, why is adding a time interval of 86,400 (one day) to a date never the answer?

I created a method to calculate the first and last day of week, by adding a time interval of one day (86,400) to a date, multiplied by the required number of days.

A colleague commented "adding multiples of 86,400 is never the answer". Why is this so?

Here's the category method that I created, how can I improve this?

- (NSDate*)firstDayOfWeek
{
    return [self dateWithDaysAddedGivenDayOfWeek:@{
        @1 : @-6,
        @2 : @0,
        @3 : @-1,
        @4 : @-2,
        @5 : @-3,
        @6 : @-4,
        @7 : @-5
    }];
}

- (NSDate*)lastDayOfWeek
{
    return [self dateWithDaysAddedGivenDayOfWeek:@{
        @1 : @0,
        @2 : @6,
        @3 : @5,
        @4 : @4,
        @5 : @3,
        @6 : @2,
        @7 : @1
    }];
}

- (NSDate*)dateWithDaysAddedGivenDayOfWeek:(NSDictionary*)daysToAddGivenDay
{
    NSDateComponents* components = [[NSCalendar currentCalendar]
        components:NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit fromDate:self];

    NSInteger daysToAdd = [[daysToAddGivenDay objectForKey:@([components weekday])] integerValue];
    return [self dateByAddingTimeInterval:daysToAdd * 60 * 60 * 24];
}

Do I need to use a different time interval for the day? (eg 23 hours, 56 minutes, 4.1 seconds)

like image 920
Jasper Blues Avatar asked Dec 19 '22 21:12

Jasper Blues


1 Answers

You cannot assume that days have the same length. Due to daylight saving times they can i.e. be 23, 24 or 25 hours long. It is easy to imagine the trouble it might cause if we assume a day to be exactly 24*60*60 seconds long.

Also your code might be faulty as in different countries and cultures the first day of the week might differ (Sunday vs. Monday). So time and date calculations must be in respect to the calendar and the users locale. It is not simply done by counting seconds.

And we haven't even mentioned more nasty things as leap seconds, politicians changing a states time zone (Russia under Putin, Spain under Franco) or even shifting them across the international date line (refer to Samoa — they did it twice).

So you should trust the framework's tool, nicely explained in WWDC2011: Performing Calendar Calculations.

For the code you posted in the question I would suggest some thing like

NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit 
       startDate:&startOfTheWeek 
        interval:&interval //<-- interval will hold the length of the time unit, 
         forDate:now];     //    here week, taking DST et al into account
//startOfWeek holds now the first day of the week, according to locale (monday vs. sunday)

endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval-1];
// holds 23:59:59 of last day in week.

In your case another option would be to skip NSDate, as they are not representing a day but a specific moment in time and work with NSDateComponents. Also explained in the video.

like image 99
vikingosegundo Avatar answered Dec 22 '22 11:12

vikingosegundo