3

I'm writing some tests to see if a person object is correctly saved into a local database (SQLite, using strings for the dates).

in my tests I have :

let testPerson = Person(firstName:"John", lastName:"Doe")

After saving to the database I test if createdAt != nil

That is ok! Then :

let testPerson2 = Person.LoadByID(testPerson.id)

which is a static function loading a person object from the database into testPerson2

all fields are passing in the tests (so they're exactly the same for testPerson and testPerson2 except the createdAt and updateAt dates

If I do a println of those dates, they're EXACTLY the same :

testPerson.createdAt : Optional(2015-08-14 12:03:01 +0000)
testPerson2.createdAt :Optional(2015-08-14 12:03:01 +0000)

But testing (via 2 different methods):

XCTAssert(testPerson2.createdAt == testPerson.createdAt, "createdAt should be the same")

or

XCTAssertTrue(testPerson2.createdAt!.isEqualToDate(testPerson2.createdAt!), "createdAt should be the same")

fail

Doing those tests with 2x testPerson or testPerson2, succeeds

I also tried the following code:

if testPerson.createdAt!.compare(testPerson.createdAt!) == NSComparisonResult.OrderedDescending
        {
            println("date1 after date2");
        } else if testPerson.createdAt!.compare(testPerson.createdAt!) ==     NSComparisonResult.OrderedAscending
        {
            println("date1 before date2");
        } else
        {
            println("dates are equal");
        }

failing if I use testPerson and testPerson2, succeeding with testPerson/testPerson and testPerson2/testPerson2

Why ? Println and looking into the database itself look ok.

Glenn
  • 2,808
  • 2
  • 24
  • 30
  • 1
    Your output does not proof that the dates are *exactly* the same, because NSDate represents its time internally as a floating point value with sub-second precision. – Martin R Aug 14 '15 at 12:30
  • I see that your createdAt is an optional field. So comparing them may not be what you want, you first need to unwrap the optional. Second even if you unwrap them == operator will not work for NSDate, user compare as you did in later code snippet. – Abdullah Aug 14 '15 at 12:36
  • Moreover both the dates may be different at fraction of millisecond level. That may make them both different. Just for making your job easier look at this library for easy date handling. https://github.com/abdullah-chhatra/iDate – Abdullah Aug 14 '15 at 12:38
  • They SHOULD be EXACTLY the same at the millisecond level as it is the createdAt from testPerson that is saved into SQLite, then restored into the createdAt in testPerson2 and compared in my unit tests – Glenn Aug 14 '15 at 15:39
  • @Glenn: Is the date stored as a REAL in the SQLite database? Otherwise precision could get lost. How much do the dates differ (`date1.timeIntervalSinceDate(date2)`) ? – Martin R Aug 14 '15 at 17:09
  • Dates are stored as Strings, using an NSDate Extension : public class func fromDatatypeValue(stringValue: String) -> NSDate { return SQLDateFormatter.dateFromString(stringValue)! } public var datatypeValue: String { return SQLDateFormatter.stringFromDate(self) } public class func mySQLToSQLiteString (stringValue : String) -> String { return stringValue.substringToIndex(advance(stringValue.startIndex, 19)) + ".000" } – Glenn Aug 15 '15 at 06:17
  • let SQLDateFormatter: NSDateFormatter = { let formatter = NSDateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) return formatter }() – Glenn Aug 15 '15 at 06:18
  • mmm there is indeed a difference : 6.00218772888184e-05. I Guess I'll check for day/month/year and hours/minutes/seconds in my tests and if those are the same, let the test pass... – Glenn Aug 15 '15 at 06:39

1 Answers1

5

Internally NSDate has a resolution of at least one nanosecond. Your dateFormatter removes most of that resolution because the dateformat ss.SSS only handles milliseconds.

If you don't need a nanosecond precision I would recommend to only check your dates up to the millisecond level. I usually use XCTAssertEqualWithAccuracy() for that.

e.g.:

XCTAssertEqualWithAccuracy(date1.timeIntervalSinceReferenceDate,
                           date2.timeIntervalSinceReferenceDate,
                           0.001, 
                           "")
Matthias Bauch
  • 89,811
  • 20
  • 225
  • 247