What's Swift current time Date object precision when it is initiated using the default constructor? - swift

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

Related

Convert Unix/epoch timestamp to human readable time in Dart Flutter

Say the EPOCH timestamp I received from an API is 1595216214.
It is equivalent to Monday, July 20, 2020 3:36:54 AM (GMT).
My interest is time value only (Ignoring the date/day value)? How can I code in Dart?
Also, how can I convert it into my time zone (E.g.: GMT+8)
you can use DateTime class to do that. Like this:
var dateUtc = DateTime.fromMillisecondsSinceEpoch(myAPIEpochTimeInMilliseconds, isUtc: true);
var dateInMyTimezone = dateUtc.add(Duration(hours: 8));
var secondsOfDay = dateInMyTimezone.hour * 3600 + dateInMyTimezone.minute * 60 + dateInMyTimezone.second;
NOTE:
If you are doing this for the web, although Dart does support 64+ bit numbers, javascript only takes 32-bit integers. So, use the BigInt class for big numbers tha exceeds 32-bit representation.
DateTime doesn't have an inherent timezone to be defined on the class. Is either the local (machine) time or utc Time. So, it is recomended to always use utc and add timezone offset when needed. Or just create a wrapper.

Result of adding second to date is one minute off; workaround

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.

Picking a date with swift UI automation

In the app I'm testing there is a date picker I'm trying to automate. The wheel defaults to tomorrow and I'm attempting to change it to today's date but 2 minutes from now. Below is the code I'm using to attempt this.
app.pickerWheels.element(boundBy: 0).adjust(toPickerWheelValue: "Today")
app.pickerWheels.element(boundBy: 1).adjust(toPickerWheelValue: "1")
app.pickerWheels.element(boundBy: 2).adjust(toPickerWheelValue: "00")
(In the actual code I'm using variables and not hard coding these string)
This code works for the second and third wheel (hours and minutes) but for the first wheel it won't set the value. The test will fail and not continue past that point.
I have also tried passing today's date instead of just "Today" with the same results.
You can use the DateFormatter class with Date to accomplish this.
// Initialize the date formatter. Set the timeZone and format. I chose hours and minutes.
let dateFormatter: DateFormatter = DateFormatter()
dateFormatter.timeZone = NSTimeZone.local
dateFormatter.dateFormat = "HH:mm"
// Initialize the Date instance using a time interval since now.
let d: Date = Date(timeIntervalSinceNow: 2 * 60)
print("Current Time = \(dateFormatter.string(from: Date())), Two-Minutes-From-Now = \(dateFormatter.string(from: d))")
Output: Current Time = 23:57, Two-Minutes-From-Now = 23:59
A Date is stored as a time interval since January 1st, 1970. You can manipulate the date by adding or subtracting seconds from it. Here, I added 2 * 60 or two 60-second minutes to the current time interval (a large value represented in a double). This points to two minutes in the future.
Now, if you print the date without the formatter, it will just display the current time with no regard to your time zone. So if you want it to be accurate to your time zone, you need to set that in the formatter first. Note that it doesn't change the time, just its representation to you.

method for converting seconds from date to datetime

Is there a method in matlab to convert seconds from a known date to a standard date time format?
For example, if I have a vector of values shown as seconds from 1901/01/01, how would I convert them to a dateTime? In this case a value of 28125 would correspond to 1981/01/01. Is there an efficient method for doing this?
The numbers in your example do not make sense so it is not clear if your time is in seconds or days but since you asked for seconds I will use this.
What you want to achieve can be done using datenum function. This function returns the number of (fractional) days from 1/1/0000. So first you need to find your offset, e.g.:
offsetInDays = datenum(1901,1,1);
Next, you convert the date from seconds to days:
dateInDays = YourRequiredDateInSec * 3600 * 24;
Finally, you date is given by
RequiredDate = datestr(offsetInDays + dateInDays);

Retrieve date from double value in NSUserDefaults (Swift)

Hi I'm creating an that has two controls: A NSSlider and a NSTextField that update continuously. I added a NSDateFormatter to the NSTextField so that it shows the current value of the slider formatted as a time. The slider has a range of 0 to 86,400 units (so that the user slides the seconds in a range of 24 hours).
I connected my app to the shared user defaults controller so that the value is stored for the user.
The app shows the correct time in the NSTextField, but it always stores the value in seconds. for example, if the user selected 06:32:14 when moving the slider, the system will actually store 23534.2261709793 in the key startTime
When the user opens the app again, they will see 06:32:14 as expected.
My problem is when I want to read the variable in my code:
I can read the value as Double:
let value = NSUserDefaults.standardUserDefaults().objectForKey("startTimeValue") as? Double
This works fine, if I try to cast it as NSDate it won't work (the value will be set to nil). So I thought that using a NSDateFormatter in code could work (as this is the way interface builder does the work for the user to see the date):
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.NoStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let date = dateFormatter.dateFromString((value?.description)!)
In the previous code I created the date formatter, then set the date style to nothing (I only want to do the time), and the time style to short. But giving the value.description ("23534.2261709793") still won't work. date will still be nil.
Is there something I'm doing wrong?
The double that you are storing / getting from NSUserDefaults is the time interval from Thursday 1st January 1970. So you need to feed that time interval into the NSDate constructor so that it can construct the date for you.
let value = NSUserDefaults.standardUserDefaults().objectForKey("startTimeValue") as? Double
let date = NSDate(timeIntervalSince1970: value)