I have set a NSTimer.scheduledTimerWithTimeInterval method which has an interval every 20 minutes. I want to be able to find out how much time is left when the the app goes into background mode. How do I find out how much time is left from the interval?
Thanks
You have access to a NSTimer's fireDate, which tells you when the timer is going to fire again.
The difference between now and the fireDate is an interval you can calculate using NSDate's timeIntervalSinceDate API.
E.G. something like:
let fireDate = yourTimer.fireDate
let nowDate = NSDate()
let remainingTimeInterval = nowDate.timeIntervalSinceDate(fireDate)
When using the Solution of #Michael Dautermann with normal Swift type Timer and Date I have noticed, that his solution will give you a negative value e.g.:
let fireDate = yourTimer.fireDate
let nowDate = Date()
let remainingTimeInterval = nowDate.timeIntervalSinceDate(fireDate)
//above value is negative e.g. when the timers interval was 2 sec. and you
//check it after 0.5 secs this was -1,5 sec.
When you insert a negative value in the Timer.scheduledTimer(timeInterval:,target:,selector:,userInfo:,repeats:) function it would lead to a timer set to 0.1 milliseconds as the Documentation of the Timer mentions it.
So in my case I had to use the negative value of the nowDate.timeIntervalSinceDate(fireDate) result: -nowDate.timeIntervalSinceDate(fireDate)
Related
What's the current time Date object time precision when it is initiated using Date()? Does it capture the time to milliseconds?
let currentTime = Date()
print(currentTime) // 2022-10-09 09:13:39 +0000
When I print the date it only shows 2022-10-09 09:13:39 +0000 so I wonder if its precision is only to the second.
Does it capture the time to milliseconds?
Yes, it does. printing a date shows a fixed string description omitting the fractional seconds. A hint is that TimeInterval is defined as a floating point type (Double).
You can prove it
let interval = Date().timeIntervalSince1970
print(interval)
which shows a real fractional part rather than a Is-floating-point-math-broken value like .00000003
I'm adding a second to an instance of Foundation's date, but the result is off by an entire minute.
var calendar = Calendar(identifier: .iso8601)
calendar.locale = Locale(identifier: "en")
calendar.timeZone = TimeZone(identifier: "GMT")!
let date1 = Date(timeIntervalSinceReferenceDate: -62544967141.9)
let date2 = calendar.date(byAdding: DateComponents(second: 1),
to: date1,
wrappingComponents: true)!
ISO8601DateFormatter().string(from: date1) // => 0019-01-11T22:00:58Z
ISO8601DateFormatter().string(from: date2) // => 0019-01-11T21:59:59Z
Interestingly, one of the following makes the error go away:
round time interval since reference date
don't add time zone to calendar
set wrappingComponents to false (even though it shouldn't wrap in this case)
I don't really need sub-second precision in my code, so I created this extension that allows me to discard it.
extension Date {
func roundedToSeconds() -> Date {
return Date(timeIntervalSinceReferenceDate: round(timeIntervalSinceReferenceDate))
}
}
I want to know this:
Why does this error happen?
Am I doing something wrong?
Is there any issue with my workaround?
Why does this error happen?
I would say this is a bug in Core Foundation (CF).
Calendar.date(byAdding:to:wrappingComponents:) calls down to the internal Core Foundation function _CFCalendarAddComponentsV, which in turn uses the ICU Calendar C API. ICU represents a time as an floating-point number of milliseconds since the Unix epoch, while CF uses a floating-point number of seconds since the NeXT reference date. So CF has to convert its representation to ICU's representation before calling into ICU, and convert back to return the result to you.
Here's how it converts from a CF timestamp to an ICU timestamp:
double startingInt;
double startingFrac = modf(*atp, &startingInt);
UDate udate = (startingInt + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
The modf function splits a floating-point number into its integer and fractional parts. Let's plug in your example date:
var startingInt: Double = 0
var startingFrac: Double = modf(date1.timeIntervalSinceReferenceDate, &startingInt)
print(startingInt, startingFrac)
// Output:
-62544967141.0 -0.9000015258789062
Next, CF calls __CFCalendarAdd to add one second to -62544967141. Note that -62544967141 lies in the round one-minute interval -62544967200 ..< -62544967140.0. So when CF adds one second to -62544967141, it gets -62544967140, which would be in the next round one-minute interval. Since you specified wrapping components, CF isn't allowed to change the minute part of the date, so it wraps back to the beginning of the original round one-minute interval, -62544967200.
Finally, CF converts the ICU time back to a CF time, adding in the fractional part of the original time:
*atp = (udate / 1000.0) - kCFAbsoluteTimeIntervalSince1970 + startingFrac + (nanosecond * 1.0e-9);
So it returns -62544967200 + -0.9000015258789062 = -62544967200.9, exactly 59 seconds earlier than the input time.
Am I doing something wrong?
No, the bug is in CF, not in your code.
Is there any issue with my workaround?
If you don't need sub-second precision, your workaround should be fine.
I can reproduce it with more recent dates but so far only with negative reference dates, e.g. Date(timeIntervalSinceReferenceDate: -1008899941.9), which is 1969-01-11T22:00:58Z.
Any negative timeIntervalSinceReferenceDate in the last second of a minute interval should cause the problem. The bug effectively makes the first round whole minute prior to time 0 span from -60.99999999999999 through -1.0, but it should span from -60.0 through -5e324. All more-negative round minute intervals are similarly offset.
Is it possible to customise the UIDatePicker in CountDownTimer mode to not show the picker values past a certain point? For example if I set it to 2 hours it would only show 2 hours downwards.
Currently I have tried a few techniques but all I have succeeded in is setting the countdown point to a certain value using maximumDate.
self.datePickerView.datePickerMode = UIDatePickerMode.CountDownTimer
//For calculating a date with +30 mins
let calendar = NSCalendar.currentCalendar()
let date = calendar.dateByAddingUnit(.CalendarUnitMinute, value: 30, toDate: NSDate(), options: nil)
println("30+Mins \(date)")
//Interval for countdownDuration
let myTimeInterval = NSTimeInterval(timeOffset * 60)
println("myTimeInterval \(myTimeInterval)")
//Tried Methods - countDownDuration sets it to a certain points(in seconds)
self.datePickerView.countDownDuration = myTimeInterval
self.datePickerView.maximumDate = date // This doesn't seem to do anything when in using UIDatePickerMode.CountDownTimer
I presume a possible way is to use just a UIPicker.
Exactly as you say - it's possible to do it yourself with UIPickerView. And that's actually exactly what I did: I built a custom UIPickerView subclass for that purpose. It replicates the functionality of UIDatePicker in countDownTimer mode, while adding support to set maxTimeInterval.
You use it like this:
GSTimeIntervalPicker *picker = [[GSTimeIntervalPicker alloc] init];
picker.maxTimeInterval = (3600 * 3); // set the limit
picker.minuteInterval = 5; // the step. Default is 1 min.
picker.timeInterval = (3600 * 1.5); // 1 h 30 minutes
picker.onTimeIntervalChanged = ^(NSTimeInterval newTimeInterval) {
// Use the value
};
Available on GitHub under MIT license. Blog post here. I'm sorry it's not in Swift, you will have to convert it yourself.
First Thing: you don't need a calendar to add 30 minutes to a given date.
There is a constructor that does what you want in one step:
NSDate(timeInterval: NSTimeInterval, sinceDate: NSDate)
NSDate(timeIntervalSinceNow: NSTimeInterval)
for me,
self.datePickerView.maximumDate = date
shows future dates / time greyed out, so the user has some visual hint.
This doesn't mean, a future date / time is not selectable without additional code.
The user can select a greyed out time. You can check in your handler and when some future date is selected, you just set
self.datePickerView.date = date
as long as you don't call resignFirstResponder(), your date picker scrolls back to now (or any other chosen time) nicely. So the user is not able to select a time in the future with this additional code.
I hope this is near enough to what you need. Future is greyed out instead of invisible and it is not selectable with a little additional code in your handler.
I'm creating an app for playing a ringtone and I'd want to know the current time in milliseconds of the played ringtone every time.
CMTime cTime = player_.currentTime;
float currentTime = cTime.value / cTime.timescale;
That currentTime here gets the value in seconds.
How can I get the exact currentTime value but in milliseconds?
There is a way to get current times in seconds, you can take 1000 times that and ther you have your miliseconds:
Float64 dur = CMTimeGetSeconds([player currentTime]);
Float64 durInMiliSec = 1000*dur;
Sadly you will have to use it as a float or Float64 you can't use that result as a CMTime
I think that the accepted answer loses detail. A more accurate way to get milliseconds would be the following:
let seconds : Double = Float64(time.value * 1000) / Float64(time.timescale)
If you convert to seconds first you'd lose precision.
CMTime.value and CMTime.timescale are both integers, so judging by your code, the result gets rounded and you don't get a precise timestamp. Try CMTimeGetSeconds instead.
I am developing Alarm Clock.
I want to compare a time now and setTime. Is it possible to compare in minutes only.
My Problem is NSDate will compare in seconds, for example 9:38:50 is not equal to 9:38:00.
how can I compare in minutes?
First, convert them to UNIX timestamp ignoring milliseconds.
NSUInteger time1 = (NSUInteger)[date1 timeIntervalSince1970];
NSUInteger time2 = (NSUInteger)[date2 timeIntervalSince1970];
Ignore seconds.
time1 = time1 - (time1 % 60);
time2 = time2 - (time2 % 60);
Now, you can safely compare them.
if (time1 == time2) {
NSLog(#"Bingo");
}
Use NSCalendar to get the NSDateComponents you're interested in. Then it's easy to compare those.
Though it might be sufficient to check whether the current date is later than the alarm date. If it is and the alarm wasn't stopped, you should probably go off even if the computer momentarily lost power or something.