Strange issue while summing time interval and date in Swift 3 - swift

While trying to sum date and timeinterval i am getting Ambigous use of operator + error. How can it be fixed?
func +(date: Date, timeInterval: Int) -> Date {
return date + TimeInterval(timeInterval)
}

Why are you attempting to define such a + operator? Swift already provides one.
The following works just fine:
let date = Date()
let interval = TimeInterval(45)
let newDate = date + interval
And the code you posted in your question works just fine for me.
func +(date: Date, timeInterval: Int) -> Date {
return date + TimeInterval(timeInterval)
}
let date = Date()
let interval = 45
let anotherDate = date + interval

Related

Check if between two dates is no longer working under iOS16.3

Using iOS16.3, XCode14.2, Swift5.7.2,
Why is the following method no longer working ?
I call this method by setting date = Date() and maximumDate = Date() as well...
According to this solution, it should work - but it doesn't
public class THManager : ObservableObject {
#Published public var minimumDate: Date = Date()
#Published public var maximumDate: Date = Date()
public func isBetweenMinAndMaxDates(date: Date) -> Bool {
print(min(minimumDate, maximumDate))
print(max(minimumDate, maximumDate))
print(min(minimumDate, maximumDate)...max(minimumDate, maximumDate))
print(date)
print((min(minimumDate, maximumDate)...max(minimumDate, maximumDate)).contains(date))
return (min(minimumDate, maximumDate)...max(minimumDate, maximumDate)).contains(date)
}
}
2022-02-08 19:45:51 +0000
2023-02-03 19:45:51 +0000
2022-02-08 19:45:51 +0000...2023-02-03 19:45:51 +0000
2023-02-03 19:45:51 +0000
false
It supposed to return true ! Why does it return false ???
By the way it works if date = Date() and maximumDate = Date().addingTimeInterval(1)
Very strange, isn't it ?
There is no need for such complexity. Date objects conform to the Comparable and Equatable protocols, so testing for a date being between 2 other dates is one line:
extension Date {
func between(start: Date, end: Date) -> Bool {
return self > start && self < end
}
}
You'd use that like this:
let date = Date()
if date.betweeen(start: someDate, end: someOtherDate) {
// the date is between the start and the end
} else {
// The date is not between the start and end dates
}
The above will only return true if the date in question is not equal to start or end date. You could easily change it to match dates that match the beginning and end dates by using >= and <= instead of > and < in the comparisons.
And as discussed in the comments, Date objects have sub-millisecond precision. Two dates that appear identical may be different by a tiny fraction of a second, the best way to verify your comparisons is to convert your Dates to decimal seconds and log the seconds values. (See the Date property timeIntervalSinceReferenceDate.)
Edit:
Check out this sample code using the above exension:
extension Date {
func between(start: Date, end: Date) -> Bool {
return self > start && self < end
}
var asStringWithDecimal: String {
return DateFormatter.localizedString(from: self, dateStyle: .medium, timeStyle: .medium) + " ( \(self.timeIntervalSinceReferenceDate) seconds)"
}
}
let now = Date()
for _ in (1...5) {
let random = Double.random(in: -1.5...1.5)
let test = now.advanced(by: random)
let start = now.advanced(by: -1)
let end = now.advanced(by: 1)
let isBetween = test.between(start: start, end: end)
let isOrNot = isBetween ? "is" : "is not"
let output = "\(test.asStringWithDecimal) \n \(isOrNot) between \n \(start.asStringWithDecimal) and \n \(end.asStringWithDecimal)"
print(output)
}
That will generate 5 random dates ± 1.5 seconds from the current date, and then test each one to see if it is within 1 second of the current date. It logs the result as both Date strings and Doubles, so you can see what's happening when the seconds match (but the fractions of a second likely don't match.)

Find difference between just the time of two dates in seconds

I have 2 dates. I don't care about the date portion, just the time.
How can I compare 2 dates and get the timeinterval between 2 dates?
Should I set the dates to 01-01-2000 and leave the time alone to compare?
Use DateComponents and get the hour, minute, and second of the two dates. At this point you have to assume a full 24 hour, 86400 seconds per day. There's no need to worry about daylight saving or leap seconds or anything since you are doing date independent calculations.
Convert the hours, minutes, and seconds into total seconds of the day for the two dates. Then simply subtract the two totals and you have the difference.
Here's a helpful Date extension:
extension Date {
func secondsSinceMidnight() -> TimeInterval {
let comps = Calendar.current.dateComponents([.hour,.minute,.second], from: self)
return TimeInterval(comps.hour! * 3600 + comps.minute! * 60 + comps.second!)
}
func timeDifference(to date: Date) -> TimeInterval {
return date.secondsSinceMidnight() - self.secondsSinceMidnight()
}
}
Call timeDifference(to:) using your two dates and you will get the difference in seconds ignoring the date portion of the dates.
A negative result means that the to date is closer to midnight.
This is an alternative to rmaddy's solution completely based on DateComponents
extension Date {
func timeComponents() -> DateComponents {
return Calendar.current.dateComponents([.hour,.minute,.second], from: self)
}
func timeDifference(to date: Date) -> Int {
return Calendar.current.dateComponents([.second], from: date.timeComponents(), to: self.timeComponents()).second!
}
}
If you have two dates you can use the method timeIntervalSince(Date).
For instance:
func calculateElapsedTime(from someTime: Date) -> TimeInterval {
let currentTime = Date()
var elapsedTime = currentTime.timeIntervalSince(someTime)
return elapsedTime
}
If you only want to consider the time difference between the two dates, you first have to normalize the date. This can be done in the following cumbersome way:
let currentDate = Date()
let anotherDate = Date(timeInterval: 60, since: currentDate)
let formatter = DateFormatter()
formatter.timeStyle = .short
let currentTime = formatter.string(from: currentDate)
let anotherTime = formatter.string(from: anotherDate)
let currentIntervalTime = formatter.date(from: currentTime)
let anotherIntervalTime = formatter.date(from: anotherTime)
let elapsedTime = anotherIntervalTime?.timeIntervalSince(currentIntervalTime!)

Swift unix timestamp too short [duplicate]

I am taking the current time, in UTC, and putting it in nanaoseconds and then I need to take the nanoseconds and go back to a date in local time.
I am able to do get the time to nanoseconds and then back to a date string but the time gets convoluted when I go from a string to date.
//Date to milliseconds
func currentTimeInMiliseconds() -> Int! {
let currentDate = NSDate()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.timeZone = NSTimeZone(name: "UTC") as TimeZone!
let date = dateFormatter.date(from: dateFormatter.string(from: currentDate as Date))
let nowDouble = date!.timeIntervalSince1970
return Int(nowDouble*1000)
}
//Milliseconds to date
extension Int {
func dateFromMilliseconds(format:String) -> Date {
let date : NSDate! = NSDate(timeIntervalSince1970:Double(self) / 1000.0)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.timeZone = TimeZone.current
let timeStamp = dateFormatter.string(from: date as Date)
let formatter = DateFormatter()
formatter.dateFormat = format
return ( formatter.date( from: timeStamp ) )!
}
}
The timestamp is correct but the date returned isn't.
I don't understand why you're doing anything with strings...
extension Date {
var millisecondsSince1970:Int64 {
Int64((self.timeIntervalSince1970 * 1000.0).rounded())
}
init(milliseconds:Int64) {
self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
}
}
Date().millisecondsSince1970 // 1476889390939
Date(milliseconds: 0) // "Dec 31, 1969, 4:00 PM" (PDT variant of 1970 UTC)
As #Travis Solution works but in some cases
var millisecondsSince1970:Int WILL CAUSE CRASH APPLICATION ,
with error
Double value cannot be converted to Int because the result would be greater than Int.max if it occurs Please update your answer with Int64
Here is Updated Answer
extension Date {
var millisecondsSince1970:Int64 {
return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
//RESOLVED CRASH HERE
}
init(milliseconds:Int) {
self = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
}
}
About Int definitions.
On 32-bit platforms, Int is the same size as Int32, and on 64-bit platforms, Int is the same size as Int64.
Generally, I encounter this problem in iPhone 5, which runs in 32-bit env. New devices run 64-bit env now. Their Int will be Int64.
Hope it is helpful to someone who also has same problem
#Travis solution is right, but it loses milliseconds when a Date is generated. I have added a line to include the milliseconds into the date:
If you don't need this precision, use the Travis solution because it will be faster.
extension Date {
func toMillis() -> Int64! {
return Int64(self.timeIntervalSince1970 * 1000)
}
init(millis: Int64) {
self = Date(timeIntervalSince1970: TimeInterval(millis / 1000))
self.addTimeInterval(TimeInterval(Double(millis % 1000) / 1000 ))
}
}
//Date to milliseconds
func currentTimeInMiliseconds() -> Int {
let currentDate = Date()
let since1970 = currentDate.timeIntervalSince1970
return Int(since1970 * 1000)
}
//Milliseconds to date
extension Int {
func dateFromMilliseconds() -> Date {
return Date(timeIntervalSince1970: TimeInterval(self)/1000)
}
}
I removed seemingly useless conversion via string and all those random !.
let dateTimeStamp = NSDate(timeIntervalSince1970:Double(currentTimeInMiliseconds())/1000) //UTC time //YOUR currentTimeInMiliseconds METHOD
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.dateStyle = NSDateFormatterStyle.FullStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let strDateSelect = dateFormatter.stringFromDate(dateTimeStamp)
print("Local Time", strDateSelect) //Local time
let dateFormatter2 = NSDateFormatter()
dateFormatter2.timeZone = NSTimeZone(name: "UTC") as NSTimeZone!
dateFormatter2.dateFormat = "yyyy-MM-dd"
let date3 = dateFormatter.dateFromString(strDateSelect)
print("DATE",date3)
#Prashant Tukadiya answer works. But if you want to save the value in UserDefaults and then compare it to other date you get yout int64 truncated so it can cause problems. I found a solution.
Swift 4:
You can save int64 as string in UserDefaults:
let value: String(Date().millisecondsSince1970)
let stringValue = String(value)
UserDefaults.standard.set(stringValue, forKey: "int64String")
Like that you avoid Int truncation.
And then you can recover the original value:
let int64String = UserDefaults.standard.string(forKey: "int64String")
let originalValue = Int64(int64String!)
This allow you to compare it with other date values:
let currentTime = Date().millisecondsSince1970
let int64String = UserDefaults.standard.string(forKey: "int64String")
let originalValue = Int64(int64String!) ?? 0
if currentTime < originalValue {
return false
} else {
return true
}
Hope this helps someone who has same problem
Heres a simple solution in Swift 5/iOS 13.
extension Date {
func toMilliseconds() -> Int64 {
Int64(self.timeIntervalSince1970 * 1000)
}
init(milliseconds:Int) {
self = Date().advanced(by: TimeInterval(integerLiteral: Int64(milliseconds / 1000)))
}
}
This however assumes you have calculated the difference between UTF time and local time and adjusted and accounted for in the milliseconds. For that look to calendar
var cal = Calendar.current
cal.timeZone = TimeZone(abbreviation: "UTC")!
let difference = cal.compare(dateGiven, to: date, toGranularity: .nanosecond)
Simple one-line code to get time token in UInt64
let time = UInt64(Date().timeIntervalSince1970 * 1000)
print(time) <----- prints time in UInt64
Additional tip:
For timestamp with 10 Digit milliseconds since 1970 for API call then
let timeStamp = Date().timeIntervalSince1970
print(timeStamp) <-- prints current time stamp
Watch out if you are going to compare dates after the conversion!
For instance, I got simulator's asset with date as TimeInterval(366144731.9), converted to milliseconds Int64(1344451931900) and back to TimeInterval(366144731.9000001), using
func convertToMilli(timeIntervalSince1970: TimeInterval) -> Int64 {
return Int64(timeIntervalSince1970 * 1000)
}
func convertMilliToDate(milliseconds: Int64) -> Date {
return Date(timeIntervalSince1970: (TimeInterval(milliseconds) / 1000))
}
I tried to fetch the asset by creationDate and it doesn't find the asset, as you could figure, the numbers are not the same.
I tried multiple solutions to reduce double's decimal precision, like round(interval*1000)/1000, use NSDecimalNumber, etc... with no success.
I ended up fetching by interval -1 < creationDate < interval + 1, instead of creationDate == Interval.
There may be a better solution!?
Unless you absolutely have to convert the date to an integer, consider using a Double instead to represent the time interval. After all, this is the type that timeIntervalSince1970 returns. All of the answers that convert to integers loose sub-millisecond precision, but this solution is much more accurate (although you will still lose some precision due to floating-point imprecision).
public extension Date {
/// The interval, in milliseconds, between the date value and
/// 00:00:00 UTC on 1 January 1970.
/// Equivalent to `self.timeIntervalSince1970 * 1000`.
var millisecondsSince1970: Double {
return self.timeIntervalSince1970 * 1000
}
/**
Creates a date value initialized relative to 00:00:00 UTC
on 1 January 1970 by a given number of **milliseconds**.
equivalent to
```
self.init(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
```
- Parameter millisecondsSince1970: A time interval in milliseconds.
*/
init(millisecondsSince1970 milliseconds: Double) {
self.init(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
}
}

Turning an Int into a String

I'm pretty new to swift (and programming altogether). I'm trying to convert an Int into a String. I've tried using switch statements but every time I use them, it never changes to the String (AKA it prints the number 4) An example of what I'm trying to do is as follows:
class Birthday(_ month: Int, _ day:Int, _ year:Int) -> String{
//Here is where I'd like to turn my month into April
Return (month)
}
let example = Birthday()
example(4,15,1988)
If you really just want to get a month name from a month number, you can do the following:
let formatter = DateFormatter()
let monthName = formatter.monthSymbols[month - 1] // Assuming 1 means January
But since you are passing in a month, day, and year, you presumably want to create a Date and then you want to format that Date into a `String.
Create a Date using Calendar and DateComponents.
let date = Calendar.current.date(from: DateComponents(year: year, month: month, day: day))
Then you format the Date into a String using DateFormatter.
let formatter = DateFormatter()
formatter.dateStyle = .long // choose a desired style
formatter.timeStyle = .none
let string = formatter.string(from: date)
You can use a dictionary which maps objects to each other. For example, a months dictionary could look like:
let months: [Int:String] = [1:"January", 2:"February",...]
return months[4] // returns "April"
Simple solution to get you started would be a method that takes an integer and return your month string.
func numberToMonth(number: Int) -> String {
guard number > 0, number < 13 else { return "" }
return DateFormatter().monthSymbols[number-1]
}

Convert Date to Integer in Swift

I am updating some of my old Swift 2 answers to Swift 3. My answer to this question, though, is not easy to update since the question specifically asks for NSDate and not Date. So I am creating a new version of that question that I can update my answer for.
Question
If I start with a Date instance like this
let someDate = Date()
how would I convert that to an integer?
Related but different
These questions are asking different things:
Swift convert unix time to date and time
Converting Date Components (Integer) to String
Convert Date String to Int Swift
Date to Int
// using current date and time as an example
let someDate = Date()
// convert Date to TimeInterval (typealias for Double)
let timeInterval = someDate.timeIntervalSince1970
// convert to Integer
let myInt = Int(timeInterval)
Doing the Double to Int conversion causes the milliseconds to be lost. If you need the milliseconds then multiply by 1000 before converting to Int.
Int to Date
Including the reverse for completeness.
// convert Int to TimeInterval (typealias for Double)
let timeInterval = TimeInterval(myInt)
// create NSDate from Double (NSTimeInterval)
let myNSDate = Date(timeIntervalSince1970: timeInterval)
I could have also used `timeIntervalSinceReferenceDate` instead of `timeIntervalSince1970` as long as I was consistent. This is assuming that the time interval is in seconds. Note that Java uses milliseconds.
Note
For the old Swift 2 syntax with NSDate, see this answer.
If you are looking for timestamp with 10 Digit seconds since 1970 for API call then, below is code:
Just 1 line code for Swift 4/ Swift 5
let timeStamp = UInt64(Date().timeIntervalSince1970)
print(timeStamp) <-- prints current time stamp
1587473264
let timeStamp = UInt64((Date().timeIntervalSince1970) * 1000) // will give 13 digit timestamp in milli seconds
timeIntervalSince1970 is a relevant start time, convenient and provided by Apple.
If u want the int value to be smaller, u could choose the relevant start time you like
extension Date{
var intVal: Int?{
if let d = Date.coordinate{
let inteval = Date().timeIntervalSince(d)
return Int(inteval)
}
return nil
}
// today's time is close to `2020-04-17 05:06:06`
static let coordinate: Date? = {
let dateFormatCoordinate = DateFormatter()
dateFormatCoordinate.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let d = dateFormatCoordinate.date(from: "2020-04-17 05:06:06") {
return d
}
return nil
}()
}
extension Int{
var dateVal: Date?{
// convert Int to Double
let interval = Double(self)
if let d = Date.coordinate{
return Date(timeInterval: interval, since: d)
}
return nil
}
}
Use like this:
let d = Date()
print(d)
// date to integer, you need to unwrap the optional
print(d.intVal)
// integer to date
print(d.intVal?.dateVal)