【问题标题】:CoreData for storing dates recommended practicesCoreData 用于存储日期的推荐做法
【发布时间】:2014-10-25 15:15:42
【问题描述】:

我正在制作一个简单的班次跟踪器/工资计算器来练习 CoreData 和 Swift。我遇到了与此相关的不同任务的问题,因为我需要使用日期来:

  • 显示它们
  • 检查一周内的轮班数量,看看你是否应该得到额外的报酬(如果你一周工作超过 12 小时,挪威的规则会给你每小时 5/10 美元以上的额外报酬)
  • 将一个月内的所有工资相加
  • 如果用户输入错误,删除数据

可能还有更多我忘记了。

截至目前,我从日期选择器获取数据,这些数据选择器使用日期格式化程序将它们作为“fromDate”和“toDate”作为字符串添加到 DB。这导致我不得不使用一堆糟糕的代码。很大程度上是因为 NSDate 时区问题等。

我应该如何布置我的数据库以最有效地做到这一点?我应该为我需要的每种类型的数据创建一个字段吗? ('fromMinute'|'fromHour'|'fromDay'|'fromWeek'|'fromMonth'|'fromYear') 然后对应 'to' 字段?我是数据库的新手,但我认为我不应该拥有那么多领域。我应该有一个月表,然后是周表,然后使用关系(在这种情况下我该怎么做?)

我不能只使用时间间隔,因为奖金取决于时间间隔。

希望有人可以帮助我提出一些关于 CoreData 的推荐做法


更新:

这是我超级复杂的核心数据模型!

还有一个 NSManagedObject 类(我认为)

import UIKit
import CoreData

@objc(Shifts)

class Shifts: NSManagedObject {
    @NSManaged var fromDate : String
    @NSManaged var toDate : String

    func getShift () -> Shift{
        let formatter = NSDateFormatter()
        formatter.dateFormat = "yyyy-MM-dd w W HH:mm"
        let from:NSDate = formatter.dateFromString(self.fromDate)!
        let to:NSDate = formatter.dateFromString(self.toDate)!
        let length:Double = to.timeIntervalSinceDate(from) as Double / 60

        let newShift = Shift(from:from,to:to,length:length)
        return newShift
    }


}

Shit 是我的视图控制器中的一个类,用于显示等我的数据,它看起来像这样 (TW:Code Gore)

class Shift {
var fromDate : NSDate
var toDate : NSDate
var length : Double //In Minutes
var salary : Double = Double()
var UB : Bool = false

let etter18hverdag:Double = 22
let etter21hverdag:Double = 45
let helligdag:Double = 90
let helgEtter13:Double = 45
let helgEtter16:Double = 90 //HUSK AT PAUSE FINNES


init (from : NSDate, to : NSDate, length:Double){
    self.fromDate = from
    self.toDate = to
    self.length = length
}
func calcSalary(ub: Bool)->Double{

    let userDefaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
    let hour: Double = userDefaults.doubleForKey("salary")

    println("Checking salary on shift fromDate:\(fromDate) with length:\(length). Shift UB: \(UB)")

    if (ub){
        let calendar : NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
        calendar.locale = NSLocale.systemLocale()
        calendar.timeZone = NSTimeZone.localTimeZone()

        var checkTime:NSDate = fromDate
        var checkToTime:NSDate = toDate

        var totalSalary:Double = Double()

        while (checkToTime.timeIntervalSinceDate(checkTime)>0.0){

            let split = calendar.components(NSCalendarUnit.WeekdayCalendarUnit | NSCalendarUnit.HourCalendarUnit, fromDate: checkTime)

            println("Checking: \(checkTime) to: \(checkToTime) diff:\(checkToTime.timeIntervalSinceDate(checkTime)) currentSal: \(totalSalary) splitTime:\(split.hour)")
            if(split.weekday==7){ //Søndag
                let lønn = hour + helligdag
                totalSalary += (lønn/4)
            }

            else if (split.weekday == 6){ //Lørdag
                if(split.hour < 13){
                    let lønn = hour
                    totalSalary += (lønn/4)
                }
                if (split.hour>13 && split.hour<16){
                    let lønn = hour + helgEtter13
                    totalSalary += (lønn/4)

                }
                if (split.hour > 16){
                    let lønn = hour + helgEtter16
                    totalSalary += (lønn/4)
                }

            }

            else if (split.weekday < 6){ //Hverdag
                if(split.hour < 18){
                    let lønn = hour
                    totalSalary += (lønn/4)

                }
                if (split.hour>18 && split.hour<21){
                    let lønn = hour + etter18hverdag
                    totalSalary += (lønn/4)

                }
                if (split.hour > 21){
                    let lønn = hour + etter21hverdag
                    totalSalary += (lønn/4)
                }
            }


            checkTime = checkTime.dateByAddingTimeInterval(15*60) //15 min ganger 60 sek i min
        }
        println("Calculated salary WITH UB: \(totalSalary)")

        return totalSalary
    } else {
        let calendar : NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
        calendar.locale = NSLocale.systemLocale()
        calendar.timeZone = NSTimeZone.localTimeZone()

        var checkTime:NSDate = fromDate
        var totalSalary:Double = Double()
        let multiple = (length/60)

        while (self.toDate.timeIntervalSinceDate(checkTime)>0.0){

            let lønn = hour
            totalSalary +=  (lønn/4)

            checkTime = checkTime.dateByAddingTimeInterval(15*60) //15 min ganger 60 sek i min
        }
        println("Calculated salary WITHOUT UB: \(totalSalary)")
        return totalSalary
    }
}

【问题讨论】:

  • 为什么不直接使用 unix 时间戳呢?易于存储和计算。
  • @Oscar Apeland:你的问题对我来说不够清楚。我什至不知道您的核心数据模型是什么样的。你是什​​么实体,它们的属性和关系是什么?他们将帮助我们了解您对 NSDate 属性的需求...
  • @POB 我稍微更新了我的问题,看看。而且,据我所知,不可能将 NSData 存储在 coredata 中?
  • @Apfelsaft Apple Juice :D 需要详细说明吗?

标签: ios sql sqlite core-data swift


【解决方案1】:

我建议您将日期存储为日期对象。日期对象包含检索日期片段所需的所有数据。 然后,您可以使用类别扩展核心数据对象以检索其片段。添加一些方法,例如 -(NSInteger)year、-(NSInteger)month。 有关如何获取日期片段的信息,请参阅 NSCalendar 和 NSDateComponents。它还可以帮助您添加和执行日期操作。

【讨论】:

    【解决方案2】:

    短版

    最佳做法是将fromDatetoDate 存储为NSDates。

    长版

    约会有什么内容?

    NSDate 实际上是自参考日期以来的NSTimeIntervalNSTimeInterval 值表示自该日期以来的秒数,与时区、日历或任何其他地理或政治结构无关。从概念上讲,这与 unix 时间戳相同,但 unix 时间戳使用不同的参考日期(相当于 timeIntervalSince1970)。 NSDate 代表一个时间点 - 天、年、时区和夏令时都是表示级别的问题,而 NSDate 只是一个值对象。这是日期表示的蜜獾。没关系。

    也就是说,显示NSDate 涉及许多其他组件,其中大部分与本地化有关。用户的本地化设置决定了使用哪种日历表示 (NSCalendar),以及日期的视觉格式 (NSDateFormatter)。用户甚至可以在您的应用程序运行时更改这些设置,这就是为什么会有一些系统通知通知您的应用程序发生了更改。

    一旦您很好地理解了上述概念,使用NSCalendar 执行日期计算就不会太难,并且去年NSCalendar 被扩展为更易于使用(不幸的是,这些改进只是最近才出现在iOS 中)。 Apple 在 Date and Time Programming Guide: Performing Calendar Calculations 中有一个很好的日历计算指南,去年 WWDC 的一个会话 WWDC 2013: Solutions to Common Date and Time Challenges 详细介绍了对 NSCalendar 的改进,并涵盖了一些非常好的一般日期处理技巧。

    如果您的数据模型将日期存储为NSDate,则更容易根据用户的本地化设置正确调整日期值的视觉呈现。模型不需要知道时区、日历和本地化设置 - 这些最好在演示(视图或视图控制器)级别处理。

    如果您将日期存储为字符串、日期组件或日历组件,您会遇到很多问题。例如,如果当前日历发生更改,则所有持久化数据都将无效,需要重新构建。夏令时也可能是个大问题。

    这对 Core Data 有何影响?

    在您的情况下,您代表模型中的日期范围(从fromDatetoDate 的秒数范围)。有一些表示同一事物的替代方法,例如使用 fromDateduration 指示从 fromDate 到换班结束时的 NSTimeInterval。两种方法都有效。

    在查询特定小时的数据时,首先您将执行必要的日历计算以获取用户当前区域设置中小时的开始和结束。

    例如,在objective-c中,使用新的NSCalendar方法:

    NSCalendar  *calendar   = [NSCalendar autoupdatingCurrentCalendar];
    NSDate      *startOfDay = [calendar startOfDayForDate:fromDate];
    NSDate      *startOfHour= [calendar dateByAddingUnit:kCFCalendarUnitHour value:8 options:NSCalendarMatchStrictly];
    NSDate      *endOfHour  = [calendar dateByAddingUnit:kCFCalendarUnitHour value:9 options:NSCalendarMatchStrictly];
    

    这将为您提供fromDate 所在的一天中第 8 个小时的开始和结束时间。这只是一个示例,您的业务规则可能会规定更多逻辑,例如检查 toDate 的值与 fromDate 等 - 尽管其中许多问题最好通过自定义 Core Data validation 来处理。

    在谓词中使用 this 是一组直接的数字比较:

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.fromDate <= %@ AND SELF.fromDate >= %@", endOfHour, startOfHour];
    

    当使用NSFetchedResultsController 显示部分时,sectionNameKeyPath 可以指向 Core Data 瞬态属性,以允许您根据需要显示基于小时、月份等的部分。对查询结果进行排序和分组不能使用瞬态属性,只能使用由建模属性支持的属性 - 在您的情况下,fromDatetoDate。由于日期是数字并代表一个时间点,因此这适用于许多业务规则。

    【讨论】:

    • 感谢您的出色回答,我认为它解决了我所有的问题。
    • 不错且非常全面的答案 +1!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 2013-05-23
    • 2011-04-03
    相关资源
    最近更新 更多