Swift XCTAssertEqual() for Date values - how to set part accuracy? - swift

Inside my Unit Test for Dates
let day = StockMarketDay(year: 2022, month: 1, day: 4)
let formatter = ISO8601DateFormatter() // no timezone
formatter.formatOptions = [.withFullDate]
let targetDate = formatter.date(from: "2022-01-04")!
let timeAccuracy = TimeInterval(60*60*24)
let dateAccuracy = Date(timeIntervalSince1970: timeAccuracy)
print("dateAccuracy - \(dateAccuracy)")
// XCTAssertEqual(day!.date, targetDate, accuracy: dateAccuracy) // Error
XCTAssertEqual(day!.date, targetDate, accuracy: timeAccuracy) // Error
I get error: No exact matches in call to global function 'XCTAssertEqual'
It appears that the generic AssertEqual function must have a matching Type (e.g. Date) for the Accuracy parameter. But I don't seem to guess at creating a proper accuracy date to pass in.
Anyone know of a way to Unit Test Dates with a specified accuracy?

It might be worth trying to use TimeIntervals instead of dates, so change the XCTAssertEqual to this:
XCTAssertEqual(day!.date.timeIntervalSince1970, targetDate.timeIntervalSince1970, accuracy: timeAccuracy)

Related

Cannot find the right logic to translate the NSDate to string

I am very new to swift and found this one difficult, so its saying that it cannot be parsed to a variable because its not a string.
I searched and found something but it was not related near to my type of code so I am kindly asking for it :)
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateFormatterPrint = DateFormatter()
dateFormatterPrint.dateFormat = "MMM dd,yyyy"
let date: NSDate? = dateFormatterGet.date(from: currentVechicle.LastCommunicationDate!) as NSDate?
print(dateFormatterPrint.string(from: date! as Date))
cell.perditsuarOutlet.text = date // Error: Cannot assign value of type 'NSDate?' to type 'String?'
You are printing the correct String value, but assigning the variable date which typed as NSDate? to the text property, which should be a String.
Try doing this:
let dateString = dateFormatterPrint.string(from: date! as Date)
cell.perditsuarOutlet.text = dateString
One piece of advice: don't force unwrap here (date! as Date). Instead, include a way to handle nil values for your "optional" NSDate? variable. One way to do this would look like this:
if let date = dateFormatterGet.date(from: currentVechicle.LastCommunicationDate!) as NSDate? {
cell.perditsuarOutlet.text = dateFormatterPrint.string(from: date as Date)
} else {
cell.perditsuarOutlet.text = "Empty or invalid date"
}
I know this looks like a lot of overhead, but it is worth it to avoid crashes down the line...
your date constant is an NSDate.
and the cells text property is a String.
You can't pair the two together since they aren't the same type.
Since you can't change the cells text type, that only leaves you with 1 option. You'll have to turn the NSDate into a String.
I recommend taking a look at Paul Hudsons 100 Days of Swift. It was a fantastic resource when I first started. The link I've provided points to his lessons from Day 1 about Strings
Keep up the good work. I promise it gets better :)

Create time in future swift

I'm new to Swift and not so familiar with date and time classes. I want to make an object of type Date that refers to sometime in the future (like several hours).
I'm not sure if this is going to be a UNIX timestamp or not.
How can I do that?
Swift Date (or NSDate) is a class in the Foundation framework. According to the docs:
The Date structure provides methods for comparing dates, calculating
the time interval between two dates, and creating a new date from a
time interval relative to another date. Use date values in conjunction
with DateFormatter instances to create localized representations of
dates and times and with Calendar instances to perform calendar
arithmetic.
So you'd want to make use of the Calendar class to do date conversions. Something like this should do the job:
func getDateTimeForHoursInTheFuture(hours: Int) -> Date {
var components = DateComponents();
components.setValue(hours, for: .hour);
let date: Date = Date();
let expirationDate = Calendar.current.date(byAdding: components, to: date);
return expirationDate!;
}
Of course it can be changed to work with minutes and seconds instead of hours.
You can format the output using:
extension Date {
func toDateTimeString() -> String {
let formatter = DateFormatter();
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
let myString = formatter.string(from: self);
return myString;
}
}
Just call the toDateTimeString() method on the result of getDateTimeForHoursInTheFuture function.

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]
}

Need simple way to compare a time string ONLY to the current dates time value

Say time string value is "7:00 AM" call it reminder time.
Now all I need to do is compare this time with the current dates time say its "9:00 AM" if reminder time is later than current time - return true else false. This is the format "h:mm a" for date formatters.
Simple right? It should be but I have burned too much time on this. I can get hour and minute values but when the AM/PM is considered it gets harder.
I just want to compare two time values and determine if the first is later or after the second one. The date is always today or current date so I only care about the time part of the date. Of course you have to convert to dates to do the comparison but current date is easy to get however date from "7:00 AM" string does not seem to work right in comparisons.
Anyone have a function to do this?
Thanks.
the approach would be lets date the Date() object from your current time object so you will get
default date + your time = 2000-01-01 00:00:00 +your time (7.00 AM or 9.00 PM)
now we will get the current time from today only, in same format. (Only time)
it will be something like 3.56 PM
now again we will convert this 3.56 PM to Date() with default date as prev. so now we will have two date time object with same Date(2000-01-01) and respective times.
2000-01-01 7:00:00 => this will your 7.00 AM with default date
2000-01-01 15:56:00 => this will be current time with default date
now we will compare two date object.
Check the fiddle Fiddle
func CompareMyTimeInString(myTime:String)->Bool
{
// create the formatter - we are expecting only "hh:mm a" format
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "hh:mm a"
dateFormatter.locale = Locale.init(identifier: "en_GB")
// default date with my time
var dt_MyTime = dateFormatter.date(from: yourTime)!
// current time in same format as string "hh:mm a"
var currentTimString = dateFormatter.string(from: Date());
print("Current Time is - "+currentTimString);
// current time with default date.
var dt_CurrentTime = dateFormatter.date(from: currentTimString)!
// now just compare two date objects :)
return dt_MyTime > dt_CurrentTime;
}
// then call it like
var yourTime = "7.00 AM"
var isDue = CompareMyTimeInString(myTime:yourTime);
print(isDue);
My solution was as follows.
private func ReminderAfterCurrentTime(reminderTimeString: String) -> Bool {
//Compare the two time strings and if reminderTimeString is later than current time string
//return true since Reminder is after current time.
//Get the current date and time
let currentDateTime = Date()
// Create calendar object
let calendar = NSCalendar.current
// Get current date hour and minute values for comparison.
let currentHourValue = Int(calendar.component(.hour, from: currentDateTime))
let currentMinuteValue = Int(calendar.component(.minute, from: currentDateTime))
//Now get a date from the time string passed in so we can get just the hours and minutes to compare
let dateformatter = DateFormatter()
dateformatter.dateStyle = DateFormatter.Style.none
dateformatter.timeStyle = DateFormatter.Style.short
//Now get the date using formatter.
let reminderDateTime = dateformatter.date(from: reminderTimeString)
print("reminderDateTime = \(reminderDateTime)")
//Get reminder hour and minute for comparison.
let reminderHourValue = Int(calendar.component(.hour, from: reminderDateTime!))
let reminderMinuteValue = Int(calendar.component(.minute, from: reminderDateTime!))
print("currentHourValue = \(currentHourValue)")
print("currentMinuteValue = \(currentMinuteValue)")
print("reminderHourValue = \(reminderHourValue)")
print("reminderMinuteValue = \(reminderMinuteValue)")
//This works due to 24 hour clock. Thus AM/PM is already taken into account.
if currentHourValue < reminderHourValue {
return true
}
//Check for same hour then use minutes
if currentHourValue == reminderHourValue {
if currentMinuteValue < reminderMinuteValue {
return true
}
}
//Otherwise return false
return false
}

Find difference in seconds between NSDates as integer using Swift

I'm writing a piece of code where I want to time how long a button was held down. To do that I recorded an NSDate() when the button was pressed, and tried using the timeIntervalSinceDate function when the button was released. That seems to work but I can't find any way to print the result or switch it to an integer.
var timeAtPress = NSDate()
#IBAction func pressed(sender: AnyObject) {
println("pressed")
timeAtPress = NSDate()
}
#IBAction func released(sender: AnyObject) {
println("released")
var elapsedTime = NSDate.timeIntervalSinceDate(timeAtPress)
duration = ???
}
I've seen a few similar questions, but I don't know C so I had a hard time understanding the answers given. If there is a more efficient way to find out how long the button was held down I'm open to suggestions.
Your attempt to calculate elapsedTime is incorrect. In Swift 3, it would be:
let elapsed = Date().timeIntervalSince(timeAtPress)
Note the () after the Date reference. The Date() instantiates a new date object, and then timeIntervalSince returns the time difference between that and timeAtPress. That will return a floating point value (technically, a TimeInterval).
If you want that as truncated to a Int value, you can just use:
let duration = Int(elapsed)
And, BTW, your definition of the timeAtPress variable doesn't need to instantiate a Date object. I presume you intended:
var timeAtPress: Date!
That defines the variable as a Date variable (an implicitly unwrapped one), but you'd presumably defer the actual instantiation of that variable until pressed is called.
Alternatively, I often use CFAbsoluteTimeGetCurrent(), e.g.,
var start: CFAbsoluteTime!
And when I want to set startTime, I do the following:
start = CFAbsoluteTimeGetCurrent()
And when I want to calculate the number of seconds elapsed, I do the following:
let elapsed = CFAbsoluteTimeGetCurrent() - start
It's worth noting that the CFAbsoluteTimeGetCurrent documentation warns us:
Repeated calls to this function do not guarantee monotonically increasing results. The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.
This means that if you're unfortunate enough to measure elapsed time when one of these adjustments take place, you can end up with incorrect elapsed time calculation. This is true for NSDate/Date calculations too. It's safest to use a mach_absolute_time based calculation (most easily done with CACurrentMediaTime):
let start = CACurrentMediaTime()
and
let elapsed = CACurrentMediaTime() - start
This uses mach_absolute_time, but avoids some of its complexities outlined in Technical Q&A QA1398.
Remember, though, that CACurrentMediaTime/mach_absolute_time will be reset when the device is rebooted. So, bottom line, if you need accurate elapsed time calculations while an app is running, use CACurrentMediaTime. But if you're going to save this start time in persistent storage which you might recall when the app is restarted at some future date, then you have to use Date or CFAbsoluteTimeGetCurrent, and just live with any inaccuracies that may entail.
Swift 5
let differenceInSeconds = Int(endDate.timeIntervalSince(startDate))
NSDate() and NSCalendar() sound like a good choice. Use calendar calculation and leave the actual math part to the framework. Here is a quick example of getting the seconds between two NSDate()
let startDate = NSDate()
let endDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let dateComponents = calendar.components(NSCalendarUnit.CalendarUnitSecond, fromDate: startDate, toDate: endDate, options: nil)
let seconds = dateComponents.second
println("Seconds: \(seconds)")
According with the Freddy's answer, this is the function in swift 3:
let start = Date()
let end = Date(timeIntervalSince1970: 100)
let calendar = Calendar.current
let unitFlags = Set<Calendar.Component>([ .second])
let datecomponents = calendar.dateComponents(unitFlags, from: start, to: end)
let seconds = datecomponents.second
print(String(describing: seconds))
This is how you can get the difference in latest version of Swift 3
let calendar = NSCalendar.current
var compos:Set<Calendar.Component> = Set<Calendar.Component>()
compos.insert(.second)
compos.insert(.minute)
let difference = calendar.dateComponents(compos, from: fromDate, to: toDate)
print("diff in minute=\(difference.minute!)") // difference in minute
print("diff in seconds=\(difference.second!)") // difference in seconds
Reference: Getting the difference between two NSDates in (months/days/hours/minutes/seconds)
Swift 5
let startDate = Date()
let endDate = Date()
let calendar = Calendar.current
let dateComponents = calendar.compare(startDate, to: endDate, toGranularity: .second)
let seconds = dateComponents.rawValue
print("Seconds: \(seconds)")
For Swift 3
Seconds between 2 time in "hh:mm"
func secondsIn(_ str: String)->Int{
var strArr = str.characters.split{$0 == ":"}.map(String.init)
var sec = Int(strArr[0])! * 3600
var sec1 = Int(strArr[1])! * 36
print("sec")
print(sec+sec1)
return sec+sec1
}
Usage
var sec1 = secondsIn(shuttleTime)
var sec2 = secondsIn(dateToString(Date()))
print(sec1-sec2)