【问题标题】:UIDatePicker and American/Canadian Timezone messing Up NSFetchRequest filter by DatesUIDatePicker 和美国/加拿大时区弄乱了 NSFetchRequest 按日期过滤
【发布时间】:2014-04-14 16:31:21
【问题描述】:

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

CoreData 的模型是一个与 Years 实体有关系的事务实体。我还有一个 Years Entity 用于检查其他地方的日期(但不仅仅是年份)。

第二个选项卡是一个“日期”选项卡,它只过滤从日期开始的年份,因此如果用户有 5 个 2014 年条目,它只会显示 2014 一次,然后当用户单击 2014 单元格时,他们将看到年份 = 2014 的所有条目。这同样适用于其他年份。

问题

我在英国地区,但我在其上编写应用程序的 Mac 在美国地区,我刚刚注意到一个大问题。

如果您在 devie 上将时区更改为新加坡或亚洲(时间上在英国之前的某个地方),日期选项卡将与您留下的相同。但是,如果您通过电话将其更改为纽约、墨西哥或加拿大(基本上在英国之后的任何时间),那么“日期”选项卡就会变得混乱,每年都会下降一个。

我的意思是,如果您在“日期”选项卡中有 2014、2013、2012 并且您在英国地区,如果您转到纽约时区并进入应用程序,它现在会显示 2013, 2012 年和 2011 年。

发生了什么事?

这是添加条目的“保存”方法中的一些代码:

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

在我的 Save 方法中,我注意到我将时区设置为 UTC,我这样做的原因是因为如果我没有这样做,当我去另一个国家并更改我的时区时,它会添加“2014”日期选项卡中 2014 年的每个条目显然是错误的。但这当然会在这里引起一些问题。

要求

我只需要能够保持“日期”选项卡不变,无论我在哪个时区。

要点

格式保存为年份等原因有几个原因,并且无法更改,因为它会影响整个应用程序。 UIDatePicker 在 Storyboard 中设置为英国语言环境,这有几个原因。但是,即使将语言环境设置为“默认”,也会出现同样的问题。美国和加拿大的时区删除了一年,因此“日期”选项卡中的 2014 变为 2013,2013 变为 2012,2012 变为 2011。这对我来说没有意义。

任何关于这方面的指导将不胜感激!

【问题讨论】:

  • 你应该保存 NSDate 而不是 D、M、Y...
  • 感谢@Larme - 将其保存为该格式而不是 NSDate 有几个原因,但我不确定这是否是导致问题的原因,因为它适用于除美国和加拿大以外的每个时区..你对此有什么想法吗?
  • @Tom Harrington 解释了为什么它“更好”。另外,您可以轻松使用NSPredicate 并检查年份,使用NSDateComponents 创建日期,如果这可能会吓到您。

标签: ios core-data nsfetchedresultscontroller uidatepicker


【解决方案1】:

发生了什么事?

发生的情况是,无论时区如何,保持日期一致的要求并不像您想的那么容易。是这样的:

  • 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 以西的时区中,它比 UTC 早一个或多个小时,仍然是 2013 年。问题的所有其他细节都源于这个事实。

如果您关心年份,最好的解决方案是存储年份,而不是NSDate。将 2014、2013、2012 等存储为整数,而不是 NSDate 隐含的值。

顺便说一句,您的这部分问题完全无关紧要:

我在英国地区,但我编写应用程序的 Mac 在美国地区,我刚刚注意到一个大问题。

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

【讨论】:

  • 谢谢汤姆——这绝对是绝妙的,而且很有意义。在另一个问题中,我确实对核心数据模型的迁移有疑问,但是对于这个问题,这是完全合理的,我无法在网上找到更好的解决方案。我对 NSStrings 和核心数据很满意(我对开发还很陌生),所以我可以简单地使用 NSString 来提取年份并进行比较,还是必须是整数?从逻辑上讲,这是有道理的,但我想确保我首先使用 NSString 等
  • 整数排序会快很多(比较一个数值与比较 N 个字符)。字符串可以工作,但在字符串长度不同的情况下,它们并不总是按数字顺序排序。
  • 谢谢汤姆 - 太棒了 - 非常感谢你的帮助
猜你喜欢
  • 1970-01-01
  • 2014-04-17
  • 2017-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-18
  • 1970-01-01
相关资源
最近更新 更多