如何管理不同时区的NSDate [英] How to manage NSDate for different timezones

查看:87
本文介绍了如何管理不同时区的NSDate的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经通过以下方式使用日期对象在我的应用中创建了一个日历:

I have created a calendar in my app, using the date object this way:

    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *weekdayComponents = [gregorian components:(NSDayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit  | NSMinuteCalendarUnit)fromDate:[NSDate date]];
    NSInteger day    = [weekdayComponents day];
    NSInteger month  = [weekdayComponents month]; 
    NSInteger year   = [weekdayComponents year];

    m_dateFormatter.dateFormat = @"dd/MM/yyyy";
    [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    NSDateComponents *timeZoneComps=[[NSDateComponents alloc] init];
    [timeZoneComps setDay:day];
    [timeZoneComps setMonth:month];
    [timeZoneComps setYear:year];
    [timeZoneComps setHour:00];
    [timeZoneComps setMinute:00];
    [timeZoneComps setSecond:01];

    m_currentDate         = [gregorian dateFromComponents:timeZoneComps];

当用户要下个月去时,我突出显示该月的第一个日期.因此,在这种情况下,日期将是1-06-2014,00:00:01.

When the user wants to go next month, I highlight the first date of that month. So, in this case, the date will be 1-06-2014,00:00:01.

这是代码:

    - (void)showNextMonth
    {  
        // Move the date context to the next month
        NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        NSDateComponents *dateComps = [[NSDateComponents alloc] init];
        [dateComps setMonth:1];

        m_currentMonthContext =[gregorian dateByAddingComponents:dateComps toDate:m_currentMonthContext options:0];


        NSDateComponents *weekdayComponents1 = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit) fromDate:m_currentMonthContext];

        NSInteger nextMonth = [weekdayComponents1 month];
        NSInteger nextyear  = [weekdayComponents1 year];

        NSDateComponents *weekdayComponents2 = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit) fromDate:m_currentDate];

        NSInteger currentDay   = [weekdayComponents2 day];
        NSInteger currentMonth = [weekdayComponents2 month];
        NSInteger currentYear  = [weekdayComponents2 year];

        NSInteger selectedDay = 1;

        if(nextMonth == currentMonth && nextyear == currentYear)
        {
            selectedDay = currentDay;
        }

        NSInteger month = nextMonth;

        [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];

        NSDateComponents *timeZoneComps=[[NSDateComponents alloc] init];
        [timeZoneComps setDay:selectedDay];
        [timeZoneComps setMonth:month];
        [timeZoneComps setYear:nextyear];
        [timeZoneComps setHour:00];
        [timeZoneComps setMinute:00];
        [timeZoneComps setSecond:01];

        m_currentMonthContext =[gregorian dateFromComponents:timeZoneComps];

        [self createCalendar];
    }

在上述方法的倒数第二行计算m_currentMonthContext时,其值为1-06-2014,00:00:01.

When m_currentMonthContext is calculated on the second to last line of the above method, its value is 1-06-2014,00:00:01.

createCalendar实现:

-(void)createCalendar
{
    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *weekdayComponents = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit)fromDate:m_currentMonthContext];

    NSInteger month = [weekdayComponents month];
    NSInteger year  = [weekdayComponents year];     
}

在这里,我将month设置为5,将year设置为2014,但日期为2014年1月6日.这种情况仅发生在美国时区,在所有其他时区都可以正常工作.

Here I get month as 5 and year as 2014, but the date is 1-06-2014. This happens only in US time zone, in all other time zones it is working fine.

所以我想知道如何有效地处理时区,或者换句话说,如何确保即使时区发生变化,NSDate也不会发生变化.

So I want to know how to handle timezones effectively, or in other sense, how to make sure that NSDate does not change even if time zone changes.

推荐答案

最直接的原因是,在计算日期和日期成分时,日历上的时区设置不一致.有时您将时区设置为UTC,有时则未设置为UTC,这会导致不一致,因为有时会应用本地时间的偏移量,有时不会.

The proximate cause is that the time zone is not consistently set on the calendar when calculating dates and date components. Sometimes you set the time zone to UTC, and sometimes not, which is going to cause inconsistencies, as sometimes offsets for local time will be applied, and sometimes not.

详细说明,根据您的情况,m_currentMonthContextNSDate,它表示2014年6月1日午夜之后一秒的UTC时间.在您的createCalendar方法中,您创建的日历是当地时间用户,并为该日期计算组成部分.在美国所有时区,仍然是UTC 2014年6月1日午夜之后的5月1秒.可以单独运行的代码示例:

In detail, in your situation, m_currentMonthContext is an NSDate which represents the UTC time one second after midnight on June 1st, 2014. In your createCalendar method, you create a calendar that is the local time of the user, and calculate the components for such a date. In all time zones in the US, it is still the month of May one second after midnight on June 1st, 2014 UTC. An example in code, that can be run in isolation:

    NSCalendar *utcCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    [utcCalendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    NSCalendar *localCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *june = [NSDate dateWithTimeIntervalSince1970:1401580801];
    NSDateComponents *utcComponents = [utcCalendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:june];
    NSDateComponents *localComponents = [localCalendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:june];
    NSLog(@"utc : %@", utcComponents);
    NSLog(@"local: %@", localComponents);

在MDT时区,此日志:

Here in MDT time zone, this logs:

utc: 历年:2014 月:6 月:否 一天:1

utc : Calendar Year: 2014 Month: 6 Leap month: no Day: 1

本地: 历年:2014 月:5 月:否 一天:31

local: Calendar Year: 2014 Month: 5 Leap month: no Day: 31

回顾一下,您要在内存中保留一个日期,该日期已被计算为代表UTC时间中的某个日历日期,然后以用户的本地时间计算日历日期,但是似乎您对日历的期望不正确不同的时区将以相同的方式解释相同的日期.

To recap, you're keeping a date in memory that's been calculated to represent a certain calendar date in UTC time, and then calculating the calendar date in the user's local time, but it seems you have an incorrect expectation that calendars for different time zones will interpret the same date the same way.

那该怎么办?您的示例非常复杂,但是似乎根本不需要有时在UTC时区中存储日期分量,而有时又不需要-保持一致.现在,在我看来,如果您只想查找下个月的第一天,您的代码就会变得简单得多.

So, what to do? Your example is pretty complex, but it seems there's no need at all to store date components sometimes in UTC time zone and sometimes not - be consistent. Now, it also seems to me that you can be much much simpler in your code if you just want to find the first day of the next month.:

    NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *comps = [cal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:[NSDate date]];
    [comps setMonth:[comps month] + 1];
    [comps setDay:1];

我在2014年12月15日进行了测试,并在当地时间创建了2015年1月1日.希望这是一致的行为.

I tested this with December 15th, 2014, and it worked to create January 1st, 2015 in my local time. Hopefully that is consistent behavior.

总而言之-在日期分量计算中不使用一致的日历很可能是一个错误.有时使用UTC,有时在本地使用会导致您的噩梦.似乎您应该始终在当地时间进行计算,但是我不知道您的应用程序的整个上下文,因此无法对此做一个笼统的声明.同样,不要依赖于日期和日期成分之间不断的转换,而应该让日期成分成为事实的来源是安全的.也就是说,我的意思是将日期组件转换为始终存储在实例变量中的日期似乎很费劲,但是每次使用它们后立即将日期立即转换回日期组件-似乎尽可能多地使用日期组件似乎更好尽可能.

To sum up - it's very likely a bug to not use a consistent calendar for your date component calculations. Sometimes having UTC and sometimes local is going to cause you nightmares. It seems like you should always calculate in local time, but I don't know the whole context of your application so can't make a blanket statement for that. Also, it should be safe to not rely on incessant conversions between dates and date components, and instead have the date component be your source of truth. That is, I mean it seems convoluted to convert date components to dates always to store in instance variables, but then to immediately convert the dates back into date components every time they're used - it seems better to just work with date components as much as possible.

这篇关于如何管理不同时区的NSDate的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆