Original problem is encountered during my post, testing and code in the link below https://stackoverflow.com/questions/27339072/working-with-nsdate-components-in-swift/30822018#30822018
Here is the minimal test-case
- I am creating a date that is offset from a particular date (self in this case as I do it in NSDate() extension) using NSCalendar.dateByAddingUnit(..., value:x, ...)
- I then get a difference "fromDate" (created date or self) "toDate" (current time which is NSDate()) using NSCalendar.components(..., fromDate, toDate, ....).minute
- If x>0, the answer I get in the step above is x-1. If x<0, the answer I get is x. x is defined in step 1 above.
- This has the potential to cause bugs in applications that filter events using human readable attributes such as all events in past 3 hrs since 180 minutes is not three hours due to this behavior.
- This is not a bug in step 1 since the date prints correctly as seen in printout at the end of the post.
Is there an option in NSCalendar.components that I am missing or is this a bug?
Here is the code.
//
//  NSDateTest.swift
//  NSDateTestCase
//
//  Created by Jitendra Kulkarni on 6/13/15.
//
import Foundation
public extension NSDate {
        func xMins(x:Int) -> NSDate {
        return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMinute, value: x, toDate: self, options: nil)!
    }
     var hoursFromToday: Int{
        var fromDate: NSDate = self
        var toDate: NSDate = NSDate()
        var sign: Int = -1
        (fromDate,toDate, sign) = self.sanitizedDates()
        return (sign * NSCalendar.currentCalendar().components(.CalendarUnitHour, fromDate: fromDate, toDate: toDate, options: nil).hour)
    }
     var minsFromToday: Int{
        var fromDate: NSDate = self
        var toDate: NSDate = NSDate()
        var sign: Int = 1
        (fromDate,toDate) = self.sanitizedDates()
        return ( sign * NSCalendar.currentCalendar().components(.CalendarUnitMinute, fromDate: fromDate, toDate: toDate, options: nil).minute)
    }
    //Date Comparisons
    //https://stackoverflow.com/questions/26198526/nsdate-comparison-using-swift
    func isGreaterThanDate(dateToCompare : NSDate) -> Bool
    {
        //Declare Variables
        var isGreater = false
        //Compare Values
        if self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
        {
            isGreater = true
        }
        //Return Result
        return isGreater
    }
    // Date printing conversions
       var timeDayMonthYear: String {
        let dateMonthYearFormatter: NSDateFormatter = NSDateFormatter()
        let currentLocale: NSLocale = NSLocale.currentLocale()
        let dateMonthYearFormatString: NSString! = NSDateFormatter.dateFormatFromTemplate("EdMMMyyyy' ' HH':'mm",options: 0, locale: currentLocale)
        dateMonthYearFormatter.dateFormat = dateMonthYearFormatString as! String
        return dateMonthYearFormatter.stringFromDate(self)
    }
    private  func sanitizedDates() -> (fromDate: NSDate, toDate: NSDate) {
        var toDate: NSDate = self
        var fromDate: NSDate = NSDate()
        if(toDate.isGreaterThanDate(fromDate)) {
         //   toDate = toDate.xMins(1)  // Uncomment this to make the answer work
        }
             return (fromDate,toDate)
    }
    static func test (mins: Int = 0) {
        NSLog("****************** Start Testing of NSDate **************************")
        NSLog("Inputs: mins: \(mins)")
        var today = NSDate()
        NSLog("Today is: \(today.timeDayMonthYear)")
              var minsFromToday = today.xMins(mins)
        NSLog("** Date created \(mins) minutes from today is: \(minsFromToday.timeDayMonthYear)")
        NSLog("minsFromToday returns: \(minsFromToday.minsFromToday)");
        NSLog("hoursFromToday returns: \(minsFromToday.hoursFromToday)");
        NSLog("__________________ End Testing of NSDate    _________________________")
    }
}
Here is the result of NSDate(60) followed by NSDate(-60). Look for *ERROR. If you were remove comments from the one commented line in sanitizedDate(), you will get the expected result.
2015-06-13 14:56:44.877 NSDateTestCase[23136:1150501] ****************** Start Testing of NSDate **************************
2015-06-13 14:56:44.878 NSDateTestCase[23136:1150501] Inputs: mins: 60
2015-06-13 14:56:44.882 NSDateTestCase[23136:1150501] Today is: Sat, Jun 13, 2015, 14:56
2015-06-13 14:56:44.883 NSDateTestCase[23136:1150501] ** Date created 60 minutes from today is: Sat, Jun 13, 2015, 15:56
2015-06-13 14:56:44.883 NSDateTestCase[23136:1150501] minsFromToday returns: 59  // *ERROR should be 60
2015-06-13 14:56:44.884 NSDateTestCase[23136:1150501] hoursFromToday returns: 0 // *ERROR should be 1
2015-06-13 14:56:44.884 NSDateTestCase[23136:1150501] __________________ End Testing of NSDate    _________________________
2015-06-13 14:56:44.884 NSDateTestCase[23136:1150501] ****************** Start Testing of NSDate **************************
2015-06-13 14:56:44.884 NSDateTestCase[23136:1150501] Inputs: mins: -60
2015-06-13 14:56:44.885 NSDateTestCase[23136:1150501] Today is: Sat, Jun 13, 2015, 14:56
2015-06-13 14:56:44.885 NSDateTestCase[23136:1150501] ** Date created -60 minutes from today is: Sat, Jun 13, 2015, 13:56
2015-06-13 14:56:44.886 NSDateTestCase[23136:1150501] minsFromToday returns: -60
2015-06-13 14:56:44.886 NSDateTestCase[23136:1150501] hoursFromToday returns: -1
2015-06-13 14:56:44.893 NSDateTestCase[23136:1150501] __________________ End Testing of NSDate    _________________________
Here is the code based on Kurt's answer below. Notice that xFromToday API has to be changed to get an input about "today":
//
//  NSDateTest.swift
//  NSDateTestCase
//
//  Created by Jitendra Kulkarni on 6/13/15.
//
import Foundation
public extension NSDate {
        func xMins(x:Int) -> NSDate {
        return NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitMinute, value: x, toDate: self, options: nil)!
    }
    func hoursFromToday(today: NSDate) -> Int {
        var fromDate: NSDate = self
        var toDate: NSDate = today
        return (-NSCalendar.currentCalendar().components(.CalendarUnitHour, fromDate: fromDate, toDate: toDate, options: nil).hour)
    }
    func minsFromToday(today: NSDate) -> Int {
        var fromDate: NSDate = self
        var toDate: NSDate = today
        return (-NSCalendar.currentCalendar().components(.CalendarUnitMinute, fromDate: fromDate, toDate: toDate, options: nil).minute)
    }
    //Date Comparisions
    //https://stackoverflow.com/questions/26198526/nsdate-comparison-using-swift
    func isGreaterThanDate(dateToCompare : NSDate) -> Bool
    {
        //Declare Variables
        var isGreater = false
        //Compare Values
        if self.compare(dateToCompare) == NSComparisonResult.OrderedDescending
        {
            isGreater = true
        }
        //Return Result
        return isGreater
    }
    // Date printing converstions
       var timeDayMonthYear: String {
        let dateMonthYearFormatter: NSDateFormatter = NSDateFormatter()
        let currentLocale: NSLocale = NSLocale.currentLocale()
        let dateMonthYearFormatString: NSString! = NSDateFormatter.dateFormatFromTemplate("EdMMMyyyy' ' HH':'mm",options: 0, locale: currentLocale)
        dateMonthYearFormatter.dateFormat = dateMonthYearFormatString as! String
        return dateMonthYearFormatter.stringFromDate(self)
    }
    static func test (mins: Int = 0) {
        NSLog("****************** Start Testing of NSDate **************************")
        NSLog("Inputs: mins: \(mins)")
        var today = NSDate()
        NSLog("Today is: \(today.timeDayMonthYear)")
              var minsFromToday = today.xMins(mins)
        NSLog("** Date created \(mins) minutes from today is: \(minsFromToday.timeDayMonthYear)")
        //NSLog("minsFromToday returns: \(minsFromToday.minsFromToday)")
        NSLog("minsFromToday(today) returns: \(minsFromToday.minsFromToday(today))");
       //NSLog("hoursFromToday returns: \(minsFromToday.hoursFromToday)")
        NSLog("hoursFromToday(today) returns: \(minsFromToday.hoursFromToday(today))");
        NSLog("__________________ End Testing of NSDate    _________________________")
    }
Here is the output produced:
2015-06-13 16:23:55.362 NSDateTestCase[23758:1196619] ****************** Start Testing of NSDate **************************
2015-06-13 16:23:55.363 NSDateTestCase[23758:1196619] Inputs: mins: 60
2015-06-13 16:23:55.366 NSDateTestCase[23758:1196619] Today is: Sat, Jun 13, 2015, 16:23
2015-06-13 16:23:55.367 NSDateTestCase[23758:1196619] ** Date created 60 minutes from today is: Sat, Jun 13, 2015, 17:23
2015-06-13 16:23:55.367 NSDateTestCase[23758:1196619] minsFromToday(today) returns: 60  // All good now.
2015-06-13 16:23:55.367 NSDateTestCase[23758:1196619] hoursFromToday(today) returns: 1 // All good now.
2015-06-13 16:23:55.367 NSDateTestCase[23758:1196619] __________________ End Testing of NSDate    _________________________
2015-06-13 16:23:55.368 NSDateTestCase[23758:1196619] ****************** Start Testing of NSDate **************************
2015-06-13 16:23:55.368 NSDateTestCase[23758:1196619] Inputs: mins: -60
2015-06-13 16:23:55.368 NSDateTestCase[23758:1196619] Today is: Sat, Jun 13, 2015, 16:23
2015-06-13 16:23:55.369 NSDateTestCase[23758:1196619] ** Date created -60 minutes from today is: Sat, Jun 13, 2015, 15:23
2015-06-13 16:23:55.370 NSDateTestCase[23758:1196619] minsFromToday(today) returns: -60
2015-06-13 16:23:55.380 NSDateTestCase[23758:1196619] hoursFromToday(today) returns: -1
2015-06-13 16:23:55.381 NSDateTestCase[23758:1196619] __________________ End Testing of NSDate    _________________________
 
     
    