Displaying text based on what day it is - swift

I'm trying to display the time of something being open based on what day it is. Something like this:
Opening Hours
**Monday: 8:00-17:00**
Tuesday: 8:00-17:00
Wednesday: 8:00-17:00
Thursday: 8:00-17:00
Friday: 8:00-17:00
Saturday: 8:00-13:00
Sunday: closed
Or simply display
Monday: 8:00-17:00
My assumption would be to use switch statements, but what would I need to do to find out what day it is?

Another solution could be:
import Foundation
let today = NSDate()
let dateFormatter = NSDateFormatter()
let currentDay = NSCalendar.currentCalendar().component(.Weekday, fromDate:today);
dateFormatter.dateFormat = "EEEE"
let dayOfWeekString = dateFormatter.stringFromDate(today)
switch currentDay
{
case 2,3,4,5:
print("\(dayOfWeekString): 8:00 - 17:00")
case 6:
print("\(dayOfWeekString): 8:00 - 13:00")
default:
print("\(dayOfWeekString): closed")
}

You can use component(_:fromDate:) to get the week day from the current date. That would look like:
let currentDay = NSCalendar.currentCalendar().component(.Weekday, fromDate:NSDate());
Based on the value you get for currentDay, you can provide the correct opening hours.

You can make use of NSCalendar to get the .Weekday unit as an integer (Sunday through Saturday as 1 ... 7 for the Gregorian calendar).
Given you know the day of the week represented as an Int, rather than using a switch statement, you could use a [Int: String] dictionary for the different opening hours.
let calendar = NSCalendar.currentCalendar()
let today = calendar.component(.Weekday, fromDate: NSDate())
// Gregorian calendar: sunday = 0, monday = 1, ...
let openingHours: [Int: String] = [1: "Sunday: closed", 2: "Monday: 8:00-17:00", 3: "Tuesday: 8:00-17:00"] // ...
print("Opening hours:\n\(openingHours[today] ?? "")")
/* Opening hours:
Monday: 8:00-17:00 */
Another alternative is to create a computed property extension to NSDate() that returns the current weekday as a String
extension NSDate {
var weekday : String {
let formatter = NSDateFormatter()
formatter.dateFormat = "EEEE"
return formatter.stringFromDate(self)
}
}
This can be readily used with a [String: String] dictionary for holding the set of weekday : opening hours:
/* example usage */
let openingHours: [String: String] =
["Sunday": "closed",
"Monday": "8:00-17:00",
"Tuesday": "8:00-17:00"] // ...
let today = NSDate().weekday
print("Opening hours:\n\(today): \(openingHours[today] ?? "")")
/* Opening hours:
Monday: 8:00-17:00 */

Rather than going with switch statements I would prefer a more generic solution. This is also a nice demonstration of leveraging tuples and type aliases for enhancing code expressiveness and readability.
typealias Time = (start: Int, end: Int)
// starting with Sunday
let openTimes: [Time] = [(0,0), (9,17), (9,17), (9,17), (9,17), (9,17), (9,12)]
let flags : NSCalendarUnit = [.Hour, .Weekday]
func isOpenAtTime(date: NSDate) -> Bool {
let time = NSCalendar.currentCalendar().components(flags, fromDate: date)
let openingHours = openTimes[time.weekday - 1]
let hour = time.hour
return hour >= openingHours.start && hour <= openingHours.end
}
You might want to handle a few edge cases as well, but you get the idea.
You could make this work with more granular time by using minutes instead of hours.

Related

Swift 5: Calculate current time is between 2 times

I am not good at Swift so I would like to know how to calculate the current time is between two times.
I got the following response from the backend.
{"workHours":"M-F 9:00 - 18:00"}
From the above string, how to write a class/struct(or another best way) to validate today's current time is in the described time range("M-F 9:00 - 18:00")?
public final class DateValidator {
let startTime: Date?
let endTime: Date?
func isInRange() -> Bool {
// write modules here
// How to validate
}
public init(dateString: String) {
// dateString should be "M-F 9:00 - 18:00"
// Extract startTime and endTime from dateString
}
}
Set up the DateComponentsFormatter
let dcf = DateComponentsFormatter()
dcf.allowedUnits = [.day, .hour, .minute, .second]
dcf.unitsStyle = .full
Set up ordinary date formatter
let df = ISO8601DateFormatter()
df.formatOptions = [.withFullDate, .withDashSeparatorInDate]
Perform formatting
if let future = df.date(from: "2019-04-29"), let diff = dcf.string(from: Date(), to: future) {
print(diff)
}
Output is
3 days, 6 hours, 9 minutes, 9 seconds

How to calculate the correct time interval between two times that span two days in swift?

I'm trying to get the correct time interval between two times that span two days (Overnight). Here is my code successfully printing out the difference between two times - however for my use case I need the ability to span overnight, how might I do this?
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
//This time represents (23:00, Aug 07) for example
let date1 = dateFormatter.date(from: "23:00")!
//This time represents (06:00, Aug 08) for example
let date2 = dateFormatter.date(from: "06:00")!
let elapsedTime = date2.timeIntervalSince(date1)
print(abs(elapsedTime)/60/60)
//prints 17.0
My desired result is a print out of 7, as that is the amount of hours between 23:00, Aug 7 and 06:00, Aug 8 - My current code is correctly showing me the interval between those two times (as if they were from the same day) but I am trying to work out how to account for when those times overlap two days. Any help would be much appreciated.
UPDATE:
To give a more complete picture I have an object that has a start and and end date represented by a string:
Activity(startTime: "23:00", endTime: "06:00")
I use some functions to turn those strings into dates:
func startDate(startTime: String) -> Date {
let currentDate = Date().string(format: "dd-MM-yyyy")
let myStartingDate = "\(currentDate) \(startTime)"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy HH:mm"
let startDate = dateFormatter.date(from: myStartingDate)
return startDate!
}
func endDate(endTime: String) -> Date {
let currentDate = Date().string(format: "dd-MM-yyyy")
let myEndingDate = "\(currentDate) \(endTime)"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy HH:mm"
let endDate = dateFormatter.date(from: myEndingDate)
return endDate!
}
So my more complete workings look more like this:
func calculateTimeInterval(activity: Activity) {
let startHourDate = self.startDate(startTime: activity.startTime)
let endHourDate = self.endDate(endTime: activity.endTime)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
//This time represents (23:00, Aug 07) for example
let date1 = startHourDate!
//This time represents (06:00, Aug 08) for example
let date2 = endHourDate!
let elapsedTime = date2.timeIntervalSince(date1)
print(abs(elapsedTime)/60/60)
}
//prints 17.0
Without a date part the only way to determine if the end time is past midnight is if the end time is less than the start time. If so your code can be changed to
var elapsedTime = date2.timeIntervalSince(date1)
if elapsedTime < 0 {
let date3 = date2 + 60 * 60 * 24
elapsedTime = date3.timeIntervalSince(date1)
}
print(elapsedTime/60/60)
You can write an Extension to Date like this:
extension Date {
func hours(from date: Date) -> Int {
return Calendar.current.dateComponents([.hour], from: date).hour ?? 0
}
}
And just use it on any Date directly. This way you don't need DateFormatter at all. Hope this helps!

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

A complicated two dates comparison in swift

Building an app that issues queueing system tickets. The user checks the branch he will reserve his ticket in and issues a ticket with a certain delay time. The delay time is the time he will need to reach the branch.
The queueing system API branches database has the opening and closing time fields of the branch in string format as "9:00" and "17:00". Also API has a boolean field i use it if branch is closed or open so i check that boolean to not issue tickets if branch status is closed.
The issue is when the branch is open the user will issue a ticket with a certain delay time. What i need to do is to calculate:
var branchClosingTime = "17:00"
var delay = 3000 //sec
var timeNow = Date().TimeIntervalSince1970
if (timeNow + delay) < branchClosingTime {
print("Issue ticket")
} else {
print("Don't issue ticket")
}
By this calculation i will check if he didn't by pass the branch closing time. I have the algorithm in mind but i really don't know how this could be done. I played a little bit in the xcode playground converting dates to unix timestamps but i don't know how to convert this closing time string to a unix timestamp and compare.
You can parse out the contents of the string using:
let dateString = "20:34"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
if let date = dateFormatter.date(from: dateString) {
let calendar = Calendar.current
let components = calendar.dateComponents([.hour,.minute], from: date)
print(components.hour!)
print(components.minute!)
}
The Calendar class has routines that should help you figure out what date (relative to right now) that the string represents. I don't know, for example, if your "17:00" means today, or the next occurrence of 17:00... but the calendar class has routines to help you figure it out.
Thanks to #Scott's advices i was able to solve my issue and here is how it was done.
var orderTime: TimeInterval = 1494520200 - 7200 // -7200 sec to adabt to my time zone
var delay: TimeInterval = 2000 //sec
var orderDate = Date(timeIntervalSince1970: orderTime) // "May 11, 2017, 4:30 PM"
var orderDateWithDelay = orderDate + delay //"May 11, 2017, 5:03 PM"
let dateString = "17:00"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
if let date = dateFormatter.date(from: dateString) {
let calendar = Calendar.current
let components = calendar.dateComponents([.hour,.minute], from: date)
let closeDate = calendar.nextDate(after: orderDate, matching: components, matchingPolicy: .nextTime, repeatedTimePolicy: .first, direction: .forward) //"May 11, 2017, 5:00 PM"
if closeDate!.compare(orderDateWithDelay) == .orderedDescending
{
print("Close date after order date")
print("Issue ticket")
} else if closeDate!.compare(orderDateWithDelay) == .orderedAscending {
print("Close date before order date")
print("Don't issue ticket")
}
}

NSDate: Getting values for Tomorrow or Yesterday

I have a piece of code that basically acts in 2 parts:
Part 1: The user sets a Date with a UIDatePicker. For example, the user selects 1 day ahead from the current date. So the selected new date is 5/19/16 instead of 5/18/16.
Part 1 code
let dateFormatter = NSDateFormatter()
dateFormatter.locale = NSLocale.currentLocale()
var dateString = "May-19-2016"
dateFormatter.dateFormat = "MMMM-dd-yyyy"
var due_date = dateFormatter.dateFromString(dateString)!
Part 2: I have created code that counts how many days are left from the selected date to the current date. In this example, somehow my code is saying its 0 days before tomorrow. Here is the code for the second part:
Second Part:
func computeDates(dueDate:NSDate)-> Int {
let currentDate = NSDate()
// Adding days to currentDate
let daysToAdd = 1
// Adding on Unit to the current instance
let calculateDate = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Day, value: daysToAdd, toDate: currentDate, options: NSCalendarOptions.init(rawValue: 0))
// Figure out many days from may 3rd
let cal = NSCalendar.currentCalendar()
let unit = NSCalendarUnit.Day
let components = cal.components(unit, fromDate: currentDate, toDate: dueDate, options: [])
let countLeft = components.day
return countLeft
}
print("Days left: \(computeDates(due_date)) ")
// Tests
let calc_date = computeDates(due_date)
if calc_date <= -1 {
print("Yesterday")
} else if calc_date == 0 {
print("Today")
} else if calc_date > 1 {
print("Tomorrow")
}
In the part 1 example, I used a static date which I use to test this code. In this part, I set May 19, 2016, one day ahead. In the second part below in the if statement, It somehow says I have 0 days left and from what i am trying to do, it's suppose to say 1 day left before tomorrow the 19th.
Another example, If i change the 19th to the 20th, I want it to say "In 2 days" from now.
Now if I revert the day to lets say, the 15th of may (in the past), Then the if statement would say something like Overdue or the past.
How can I solve this?
It would help if you thought of NSDate as a structure that operates with the number of seconds from 2001. It means if you pick a "date", it contains "date and time". All you need to do to calculate the correct number of days between specific "dates" is to truncate a time component.
But if you only need to check whether the provided date is Yesterday, Today, or Tomorrow, NSCalendar has methods for this purpose:
Objective-C:
- (BOOL)isDateInToday:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
- (BOOL)isDateInYesterday:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
- (BOOL)isDateInTomorrow:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
And Swift:
#available(OSX 10.9, *)
public func isDateInYesterday(date: NSDate) -> Bool
#available(OSX 10.9, *)
public func isDateInTomorrow(date: NSDate) -> Bool
#available(OSX 10.9, *)
public func isDateInWeekend(date: NSDate) -> Bool