【问题标题】:Some NSDate questions considering locale settings etc一些考虑语言环境设置等的 NSDate 问题
【发布时间】:2014-02-16 13:31:17
【问题描述】:

我对 iOS 还是有点陌生​​,并且在使用 NSDate 时遇到了一些困难。基本上我的应用中有特定的 CostPeriods:

CostPeriodDaily
CostPeriodWeekly
CostPeriodMonthly
CostPeriodSemiAnnually
CostPeriodAnnually

根据 CostPeriod,我想要那个特定时期的开始和结束日期。 CostPeriodDaily 当然是从(即今天)2014 年 2 月 16 日 00:00:00(当地时间)- 2014 年 2 月 16 日 23:59:59(当地时间)。每月将从 2014 年 2 月 1 日 00:00:00 到 2014 年 2 月 28 日 23:59:59 等。

我首先有两个具体问题:

  1. NSWeekdayCalendarUnit:1 总是星期日和 7 星期六?不管 设置了什么语言环境?
  2. 如果我 NSLog 我的 endDate(如下面的代码)或开始日期,它通常是日期和 23:00:00,因为我生活在时区 格林威治标准时间+1。这是正确的吗?

现在到我的 endDate 函数,还有两个问题:-)

  • 这个功能一般可以吗?或者我会根据特定的语言环境设置出现一些问题?
  • 我的 CostPeriodWeekly 案例给了我今天的错误日期 2014-02-15 23:00:00 +0000。我不知道为什么...?

+(NSDate *)endDateForCurrentCostPeriod:(CostPeriod)costPeriod{ NSCalendar *gregorian = [[NSCalendar 分配] 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;

}

【问题讨论】:

    标签: ios localization nsdate


    【解决方案1】:

    NSWeekdayCalendarUnit:1 总是星期日和 7 星期六?无论设置什么语言环境?

    没错。但这并不意味着 1=Sunday 总是被认为是一周中的第一天。 NSCalendar 有一个 firstWeekDay 方法,那个 取决于语言环境。 (例如,在德国,星期一是一周的第一天。)

    如果我 NSLog 我的 endDate(如下代码所示)或开始日期 通常是日期和 23:00:00,因为我住在时区 格林威治标准时间+1。这是正确的吗?

    没错。

    这个功能一般都可以吗,或者我会在一些问题中运行 取决于特定的区域设置?

    您的方法看起来过于复杂。要确定您可以使用的时间段

    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]
    

    根据您的语言环境,这应该可以正常工作。对于今天的当前一周 (2 月 16 日星期日)在德国会给出结果

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

    因为这里的一周从星期一开始。 (所以实际上endOfPeriod并不是结束 期间的开始,但下一个期间的开始。)

    半年期 没有对应的NSCalendarUnit,可以这样处理:

    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;
    }
    

    编辑:在 Martin 的帮助下改变了一切。有效,但仍然对结束日期 +1 天 +1 小时的必要性感到困惑......

    + (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;
        }
    }
    

    【讨论】:

    • 非常感谢马丁。如果我理解正确,您的这种方法适用于每个地区,无论星期一还是星期日是一周的第一天,无论什么时区等?你能告诉我你是如何用这种方法得到一个时期的开始日期的吗?
    • @MichiZH:开始日期已经用那个代码计算出来了。它在 startDate:&amp;startOfPeriod 参数中有点“隐藏”。是的,这应该会根据您的语言环境和时区给出正确的结果。
    • 再次感谢您。现在将在我的应用程序中尝试 :-) 总是很高兴学到一些东西 :-)
    • 还有一个问题:半年期间你会使用什么单位和lengthOfPeriod?
    • @MichiZH:半年没有日历单位。对于这种情况,您必须使用 NSDateComponents 计算它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2010-09-09
    相关资源
    最近更新 更多