I'm still kind of new to iOS and struggling a bit with NSDate. Basically I have certain CostPeriods in my app:

CostPeriodDaily
CostPeriodWeekly
CostPeriodMonthly
CostPeriodSemiAnnually
CostPeriodAnnually

Depending on the CostPeriod I want the start and end date for that certain period. CostPeriodDaily would of course be from (i.e. today) 16.Feb 2014 00:00:00 (local time) - 16.Feb 2014 23:59:59 (local time). Monthly would be from 01.Feb 2014 00:00:00 - 28.Feb 2014 23:59:59 etc.

I have two specific questions first:

  1. NSWeekdayCalendarUnit: Is 1 ALWAYS Sunday and 7 Saturday? No matter what locale is set?
  2. If I NSLog my endDate (as in the following code) or start date it usually is the date and 23:00:00 since I'm living in the time zone GMT+1. Is this correct?

And now to my endDate function, two further questions :-)

  • Is this function in general ok or will I run in some problems depending on specific locale settings?
  • My CostPeriodWeekly case gives me the wrong date 2014-02-15 23:00:00 +0000 for today. I don't know why...?

+(NSDate *)endDateForCurrentCostPeriod:(CostPeriod)costPeriod{ NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *todayComponents =
[gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit ) fromDate:[NSDate date]];
NSRange daysOfCurrentMonth = [gregorian rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:[NSDate date]];


NSDateComponents *endDateComponents = [[NSDateComponents alloc] init];

switch (costPeriod) {
    case CostPeriodDaily:
        [endDateComponents setDay:[todayComponents day]];
        [endDateComponents setMonth:[todayComponents month]];
        [endDateComponents setYear:[todayComponents year]];
        break;

    case CostPeriodWeekly:
        //Weekday 1 = sunday, 2 = monday
        if([todayComponents weekday] == 1){
            //Sunday -> do nothing, we have the end date
            [endDateComponents setDay:[todayComponents day]];
            [endDateComponents setMonth:[todayComponents month]];
            [endDateComponents setYear:[todayComponents year]];

        } else {
            // all other days of current week, so 8- weekday (if Friday: 8 - 6 = + 2 -> Sunday)
            NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
            [componentsToAdd setDay: 8 - [todayComponents weekday]];
            NSDate *endOfWeek = [gregorian dateByAddingComponents:componentsToAdd
                                                                 toDate:[NSDate date] options:0];

            NSDateComponents *endOfWeekComponents = [gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:endOfWeek];

            [endDateComponents setDay:[endOfWeekComponents day]];
            [endDateComponents setMonth:[endOfWeekComponents month]];
            [endDateComponents setYear:[endOfWeekComponents year]];
        }
        break;

    case CostPeriodMonthly:
        [endDateComponents setDay:daysOfCurrentMonth.length];
        [endDateComponents setMonth:[todayComponents month]];
        [endDateComponents setYear:[todayComponents year]];
        break;

    case CostPeriodSemiAnnually:
        [endDateComponents setYear:[todayComponents year]];

        if([todayComponents month] <= 6){
            [endDateComponents setDay:30];
            [endDateComponents setMonth:6];
        } else {
            [endDateComponents setDay:31];
            [endDateComponents setMonth:12];
        }
        break;

    case CostPeriodAnnually:
        [endDateComponents setDay:31];
        [endDateComponents setMonth:12];
        [endDateComponents setYear:[todayComponents year]];
        break;

    default:
        [endDateComponents setDay:daysOfCurrentMonth.length];
        [endDateComponents setMonth:[todayComponents month]];
        [endDateComponents setYear:[todayComponents year]];
        break;
}

NSDate *endDate = [gregorian dateFromComponents:endDateComponents];
return endDate;

}

有帮助吗?

解决方案

NSWeekdayCalendarUnit: Is 1 ALWAYS Sunday and 7 Saturday? No matter what locale is set?

That is correct. But that does not mean that 1=Sunday is always considered to be the first day in the week. NSCalendar has a firstWeekDay method, and that is locale dependent. (For example, in Germany Monday is the first day of the week.)

If I NSLog my endDate (as in the following code) or start date it usually is the date and 23:00:00 since I'm living in the time zone GMT+1. Is this correct?

That is correct.

Is this function in general ok or will I run in some problems depending on specific locale settings?

Your method looks overly complicated. To determine a period you can use

NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *startOfPeriod, *endOfPeriod;
NSTimeInterval lengthOfPeriod;

NSCalendarUnit unit = NSDayCalendarUnit; // or NSWeekCalendarUnit, NSMonthCalendarUnit, ...
[cal rangeOfUnit:unit startDate:&startOfPeriod interval:&lengthOfPeriod forDate:now];
endOfPeriod = [startOfPeriod dateByAddingTimeInterval:lengthOfPeriod]

That should work correctly according to your locale. For the current week of today (Sunday, Feb 16) in Germany that would give the results

startOfPeriod =  2014-02-10 00:00:00 CET
endOfPeriod   =  2014-02-17 00:00:00 CET

because the week starts on a Monday here. (So actually endOfPeriod is not the end of the period, but the start of the following period.)

For the semi-annual period there is no corresponding NSCalendarUnit, so you could proceed as follows:

NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];

// Compute start and end of the current year (as above):
NSDate *startOfYear, *endOfYear;
NSTimeInterval lengthOfYear;
NSCalendarUnit unit = NSYearCalendarUnit;
[cal rangeOfUnit:unit startDate:&startOfYear interval:&lengthOfYear forDate:now];
endOfYear = [startOfYear dateByAddingTimeInterval:lengthOfYear];

// Compute middle of the year:
NSDateComponents *halfYear = [[NSDateComponents alloc] init];
NSRange range = [cal rangeOfUnit:NSMonthCalendarUnit inUnit:NSYearCalendarUnit forDate:now];
halfYear.month = range.length/2;
NSDate *middleOfYear = [cal dateByAddingComponents:halfYear toDate:startOfYear options:0];

// Compare current date with middle of the year and set start/end of period:
NSDate *startOfPeriod, *endOfPeriod;
if ([now compare:middleOfYear] == NSOrderedAscending) {
    startOfPeriod = startOfYear;
    endOfPeriod = middleOfYear;
} else {
    startOfPeriod = middleOfYear;
    endOfPeriod = endOfYear;
}

EDIT: Changed everything with the help from Martin to this. Works, but still confused about the end date +1day +1 hour necessity...

+ (NSDate *)startDateForCostPeriod:(CostPeriod)costPeriod withDate:(NSDate *)date
{
    NSCalendar *calender = [NSCalendar currentCalendar];
    NSDate *startOfPeriod;
    NSTimeInterval lengthOfPeriod;
    //If unit is not initialized here, semi annual throws always a caution message since no unit is initialized there
    NSCalendarUnit unit = NSMonthCalendarUnit;

    //For the SemiAnnual period we need date components
    NSDateComponents *todayComponents =
    [calender components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit ) fromDate:date];
    NSDateComponents *startDateComponents = [[NSDateComponents alloc] init];

    switch (costPeriod) {
        case CostPeriodDaily:
            unit = NSDayCalendarUnit;
            break;

        case CostPeriodWeekly:
            unit = NSWeekCalendarUnit;
            break;

        case CostPeriodMonthly:
            unit = NSMonthCalendarUnit;
            break;

        case CostPeriodSemiAnnually:
            [startDateComponents setDay:1];
            [startDateComponents setYear:[todayComponents year]];

            if([todayComponents month] <= 6){
                [startDateComponents setMonth:1];
            } else {
                [startDateComponents setMonth:7];
            }
            break;

        case CostPeriodAnnually:
            unit = NSYearCalendarUnit;
            break;

        default:
            unit = NSMonthCalendarUnit;
            break;
    }

    if(costPeriod != CostPeriodSemiAnnually){
        [calender rangeOfUnit:unit startDate:&startOfPeriod interval:&lengthOfPeriod forDate:date];
        return startOfPeriod;
    } else {
        return [calender dateFromComponents:startDateComponents];
    }
}

+ (NSDate *)endDateForCostPeriod:(CostPeriod)costPeriod withDate:(NSDate *)date
{
    NSCalendar *calender = [NSCalendar currentCalendar];
    NSDate *startOfPeriod;
    NSTimeInterval lengthOfPeriod;
    //If unit is not initialized here, semi annual throws always a caution message since no unit is initialized there
    NSCalendarUnit unit = NSMonthCalendarUnit;

    //For the SemiAnnual period we need date components
    NSDateComponents *todayComponents =
    [calender components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit ) fromDate:date];
    NSDateComponents *endDateComponents = [[NSDateComponents alloc] init];

    switch (costPeriod) {
        case CostPeriodDaily:
            unit = NSDayCalendarUnit;
            break;

        case CostPeriodWeekly:
            unit = NSWeekCalendarUnit;
            break;

        case CostPeriodMonthly:
            unit = NSMonthCalendarUnit;
            break;

        case CostPeriodSemiAnnually:
            [endDateComponents setYear:[todayComponents year]];

            if([todayComponents month] <= 6){
                [endDateComponents setDay:30];
                [endDateComponents setMonth:6];
            } else {
                [endDateComponents setDay:31];
                [endDateComponents setMonth:12];
            }
            break;

        case CostPeriodAnnually:
            unit = NSYearCalendarUnit;
            break;

        default:
            unit = NSMonthCalendarUnit;
            break;
    }

    if(costPeriod != CostPeriodSemiAnnually){
        [calender rangeOfUnit:unit startDate:&startOfPeriod interval:&lengthOfPeriod forDate:date];
        return [startOfPeriod dateByAddingTimeInterval:lengthOfPeriod];
    } else {
        //somehow if you take the date without a time it is always 1 day and 1 hour before. i.e. 16.2.2014 is 15.2.2014 22:00:00
        //thats why you need to add 1 day and 1 hour!
        NSDate *endDate = [calender dateFromComponents:endDateComponents];
        NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
        [componentsToAdd setDay: 1];
        [componentsToAdd setHour: 1];
        endDate = [calender dateByAddingComponents:componentsToAdd
                                             toDate:endDate options:0];

        return endDate;
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top