5

I'm making an alarm clock where it will tell you how many hours and minutes of sleep you get. I set up a UIDatePicker where the user chooses what time they wanna wake up. It also tells the exact time to the very second. The part that I'm stuck on is how many hours of sleep they are going to get. I tried just basically subtracting the exact time from the UIDatePicker. This worked if they were both in the AM. For example if the user wanted to wake up at 10:30 AM and it is 9:30 AM all you have to do is subtract 10:30 from 9:30 to get 1 hour. I soon realized this wouldn't work if they were different time of days e.g. AM or PM.

How I got the time from UIDatePicker

func handler(sender: UIDatePicker) {
        var timeFormatter = NSDateFormatter()
        timeFormatter.timeStyle = NSDateFormatterStyle.ShortStyle

        var strDate = timeFormatter.stringFromDate(theDatePicker.date)


    }
theDatePicker.addTarget(self, action: Selector("handler:"), forControlEvents: UIControlEvents.ValueChanged)

How I got the exact time

var date = NSDate()
var outputFormat = NSDateFormatter()
outputFormat.locale = NSLocale(localeIdentifier:"en_US")
outputFormat.dateFormat = "HH:mm:ss"
timeLabel.text = (outputFormat.stringFromDate(date))
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("getTime"), userInfo: nil, repeats: true)

My Question:

How do I subtract the UIDatePicker from the exact time to get the hours of sleep the user is getting?

theff

alex
  • 1,746
  • 6
  • 19
  • 34
  • Change your date picker to be in 24-hr format rather than 12, maybe. – Abizern Nov 09 '14 at 00:38
  • @Abizern That would probably work, but how would I be able to do that is that some kind of function or just adding 12 to the hour? – alex Nov 09 '14 at 00:39
  • It's set by your locale (I live in a 24hr clock country, so I don't see am/pm) But why not just add a day to the next firing time if the current time is later than the alarm time. – Abizern Nov 09 '14 at 00:47
  • @Abizern I know how to add seconds to the firing time but how would you add a day? – alex Nov 09 '14 at 00:49
  • 5
    NSDateComponents. From the trouble you've been having recently I suggest you have a read through the [Date and Time Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/DatesAndTimes/DatesAndTimes.html). I particularly recommend the section on performing calendar calculations. – Abizern Nov 09 '14 at 00:52

2 Answers2

12

You can use NSCalendar method components:fromDate:toDate:options:, for example:

@IBAction func valueChangedForPicker(sender: UIDatePicker) {
    let now = NSDate()
    let wakeUpTime = sender.date

    let calendar = NSCalendar.currentCalendar()
    let components = calendar.components(.HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit, fromDate: now, toDate: wakeUpTime, options: nil)

    println(String(format: "%02d:%02d:%02d", components.hour, components.minute, components.second))
}

If you're getting negative values, that's because fromDate is not before toDate. In this case, if you're dealing with a NSDatePicker with time only, you might want to adjust the time of the wakeUpTime to make sure it is in the future.

var wakeUpTime = datePicker.date

if wakeUpTime.compare(now) == .OrderedAscending {
    wakeUpTime = calendar.dateByAddingUnit(.DayCalendarUnit, value: 1, toDate: wakeUpTime, options: nil)!
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I want to get the hours left and show it where it says `Xhours Xminutes` so is there any way to get the hours of sleep they get? For example if I wanna wake up at 7:30 AM (what's inside the UIDatePicker) and it's 7:30 PM I need a way that calculates the time between those 2 times – alex Nov 11 '14 at 03:33
  • That's what the above does. `components.hour` contains the number of hours until the date/time of the picker, `components.minute` contains how many minutes, etc. I'm just printing it to the console, but you could update your `UILabel` objects accordingly. – Rob Nov 11 '14 at 03:43
  • This is exactly what I'm looking for but, is there any way to remove the negative signs so it just shows the hours positive for example if it was -11 hours. I want it to be just 11 hours not -11. Thanks in advance Rob this really helped just answer this and you get the bounty. – alex Nov 11 '14 at 05:40
  • Just make sure `fromDate` and `toDate` are (a) in the right order; and (b) that `toDate` is really a date in the future. With date picker only showing time, if you say "6:00am", it may return a `NSDate` that is "today at 6:00am". So you might want to compare the resultant `NSDate`, and if it's before `now`, then you might want to add one day, so it becomes "6:00am tomorrow". See revised answer. – Rob Nov 11 '14 at 11:28
  • You'd do this whenever you grab the date from the date picker. You need to make sure you're looking at a `NSDate` in the future. So, in my example, yes, I'm doing this in `valueChangedForPicker`. – Rob Nov 11 '14 at 18:31
  • i added the if statement but how do I call it so the negatives aren't negatives anymore. Do I just keep the same calling? Like this: `timeWakeUp.text = (String(format: "%02d:%02d:%02d", components.hour, components.minute, components.second))` – alex Nov 11 '14 at 18:32
  • If you correct `wakeUpDate` before you call `components:fromDate:toDate:options:`, you shouldn't have negative values. – Rob Nov 11 '14 at 18:40
5

Here is an example from a Swift playground:

// Setting up a date since I don't have a UIDatePicker
let dateString = "2014-11-12 07:25"
let dateFormatter: NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm"

var wakeupTime: NSDate = dateFormatter.dateFromString(dateString)!
// var wakeupTime: NSDate = theDatePicker.date

let fromDate = NSDate()
let gregorianCalendar: NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)!
let flags: NSCalendarUnit = .HourCalendarUnit | .MinuteCalendarUnit

let components = gregorianCalendar.components(flags, fromDate: fromDate, toDate: wakeupTime, options: NSCalendarOptions(0))

println("\(components.hour) hours, \(components.minute) minutes")
user212514
  • 3,110
  • 1
  • 15
  • 11