UIDatePicker和美国/加拿大时区通过日期弄乱了NSFetchRequest过滤器

时间:2014-04-14 16:31:21

标签: ios core-data nsfetchedresultscontroller uidatepicker

我有一个UITabBarController,有两个标签(时间轴和日期);两者都是UITableViewControllers。用户通过点击UINavigationBar按钮然后填写姓名,事件,金额和日期等信息,向这些表视图添加信息。前三个是UITextFields,日期是UIDatePicker。保存到Core-Data的信息后,UITableViewController会使用NSFetchedResultsControllers进行更新。

CoreData模型是一个与年份实体有关系的交易实体。我还有一个年度实体来检查其他地方的日期(但不仅仅是几年)。

第二个标签是"日期"选项卡仅根据日期中的年份进行过滤,因此如果用户在2014年有5个条目,则仅显示2014年一次,然后当用户点击2014年单元格时,他们将看到所有条目年= 2014年。这同样适用于其他年份。

问题

我在英国的语言环境,但是我编写该应用程序的Mac是在美国的语言环境中,我刚刚发现了一个大问题。

如果您在devie上将时区更改为新加坡或亚洲(及时在英国前面的某个地方),则日期选项卡将与您保留的时区保持一致。但是,如果您通过电话,墨西哥或加拿大(通常在英国之后的任何时间)将其更改为纽约,那么“日期”选项卡会每年减少一次。

我的意思是,如果您在日期标签中有2014年,2013年,2012年,并且您在英国地区,如果您前往纽约时区并进入应用程序,它现在显示2013年,2012年和2011年。

发生了什么事?

这里有" Save"中的一些代码。添加条目的方法:

NSCalendar *yearCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *yearComponent = [yearCal components:NSYearCalendarUnit fromDate:self.datePicker.date];
[yearComponent setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
NSDate *selectedYear = [yearCal dateFromComponents:yearComponent];

// Calling the year category to check whether it exists.
Years *years = (Years *)[Years matchingYear:selectedYear inManagedObjectContext:context];
transaction.years = years;

这会调用年份Entity上的类别:

+ (Years *)matchingYear:(NSDate *)enteredYear inManagedObjectContext:(NSManagedObjectContext *)context
{
    Years *years = nil;

    // Creating a fetch request to check whether the year already exists, calling from the Add/DetailViewController.
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Years"];
    request.predicate = [NSPredicate predicateWithFormat:@"yearOfEvent = %@", enteredYear];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"yearOfEvent" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *matchedYears = [context executeFetchRequest:request error:&error];
    if (!matchedYears)
    {
        // No errors to handle
    }
    else if (![matchedYears count])
    {
        // If the year count is 0 then create it
        years = [NSEntityDescription insertNewObjectForEntityForName:@"Years" inManagedObjectContext:context];
        years.yearOfEvent = enteredYear;
    }
    else
    {
        // If the year exists, return it.  
        years = [matchedYears lastObject];
    }
    return years;

}

我的UIDatePicker设置为当前日期,因此用户将来无法选择日期。

这里是日期标签的fetchResultsController代码:

- (NSFetchedResultsController *)fetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Years" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSPredicate *d = [NSPredicate predicateWithFormat:@"transactions.years.@count !=0"];
    [fetchRequest setPredicate:d];
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"yearOfEvent" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}

在我的保存方法中,我注意到我将时区设置为UTC,我之所以这样做是因为如果我没有,当我去另一个国家并更改我的时区时,它会添加" 2014"对于日期选项卡中2014年的每个条目,这显然是错误的。但这当然会导致一些问题。

要求

我只需要能够保持日期标签相同,无论我在哪个时区。

要点

格式保存的原因有几个原因,因为它会影响整个应用程序。 UIDatePicker在Storyboard中设置为UK Locale,原因如下。但是,即使将语言环境设置为"默认",也会出现同样的问题。美国和加拿大的时区消除了一年,因此2014年成为2013年,2013年成为2012年,2012年成为2011年的日期标签。这对我来说没什么意义。

对此的任何指导都非常感谢!

1 个答案:

答案 0 :(得分:2)

  

发生了什么事?

正在发生的事情是,无论时区如何,保持日期一致的要求并不像您所希望的那么容易。它是这样的:

  • NSDate没有时区概念。它基本上是NSTimeInterval的对象包装器,用于测量自UTC参考日期以来的时间。
  • 当您将NSDate转换为人类可读的内容时,时区适用于转化。必须申请,因为当您要求提供与NSDate相对应的日期时,您必须回答问题,日期,地点,确切

您的初始保存代码可以解决此问题:

NSCalendar *yearCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *yearComponent = [yearCal components:NSYearCalendarUnit fromDate:self.datePicker.date];
[yearComponent setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
NSDate *selectedYear = [yearCal dateFromComponents:yearComponent];

如果所选日期为今天,则最终NSDate自参考日期起为410227200秒,其中UTC对应于2014年1月1日00:00:00。在UTC以东的时区中,它是一个或多个几个小时后,现在仍然是2014年。在UTC以西的时区早一个小时,这仍然是2013年。问题的每一个细节都源于这个事实。

如果你关心这一年,最好的解决方案是存储年度,而不是NSDate。将2014,2013,2012等存储为整数,而不是NSDate暗示的值。

顺便提一下,这部分问题完全不相关:

  

我在英国的区域设置,但是我编写应用程序的Mac是在美国的区域设置,我刚刚发现了一个大问题。

重要的是运行代码的设备上的时区,而不是用于编译和链接该代码的设备上的区域。