how to get time difference form formatted time string values - swift

Hi how to get time difference for two sting value
startTime = "09:00 AM"
EndTime = "05:30 PM"
func timeDifferenceBetweenTwoTime(startTime: String, endTime:String) ->CGFloat{
let start = startTime
let end = endTime
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "hh:mm a"
if let startDate = dateFormatter.date(from: start),
let endDate = dateFormatter.date(from: end) {
let hours: CGFloat = CGFloat(Calendar.current.dateComponents([.hour], from: startDate, to: endDate < startDate ? Calendar.current.date(byAdding: .day, value: 1, to: endDate) ?? endDate : endDate).hour ?? 00 )
return hours
}
return 00.00
}
The expected result is 08.50, But for me its giving 8.0

You just need to get the minutes instead of hours and divide it by 60. Btw you should also set your dateFormatter's default date to today:
func timeDifferenceBetweenTwoTime(startTime: String, endTime: String) -> CGFloat {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.defaultDate = Calendar(identifier: .iso8601).startOfDay(for: Date())
dateFormatter.dateFormat = "hh:mm a"
if let startDate = dateFormatter.date(from: startTime),
var endDate = dateFormatter.date(from: endTime) {
if startDate > endDate {
endDate = Calendar.current.date(byAdding: .day, value: 1, to: endDate)!
}
let minute = Calendar.current.dateComponents([.minute], from: startDate, to: endDate).minute!
return CGFloat(minute) / 60
}
return 0
}
let startTime = "09:00 AM"
let endTime = "05:30 PM"
timeDifferenceBetweenTwoTime(startTime: startTime, endTime: endTime) // 8.5

You asked for the difference in hours. The Calendar function dateComponents(_:from:to:) will give you the number of whole hours between your dates if that's what you ask it for. By not also asking for the number of minutes, that gets truncated.
If you want hours and whole minutes you could ask for hours and minutes and do some math to combine them, or just ask for minutes as Leo suggests and divide minutes by 60.
You could also use endDate.timeIntervalSinceReferenceDate-startDate.timeIntervalSinceReferenceDate)/3600.0. That will give you a Double representing the exact number of hours between the two dates, including seconds and fractions of a second. (Not relevant given that your source Dates only specify hours and minutes, but there are other cases where that might be useful.)

Related

Swift date components incorrect return of date day number

I need to obtain a date from some variable values
So I specify year, month and day and I need a Date as return
Doing the following works except for the day because it return the day input - 1
let todayDate: Date = Calendar.current.startOfDay(for: Date.from(year: 2022, month: 09, day: 05)!)
print("today date = \(todayDate)")
extension Date {
static func from(year: Int, month: Int, day: Int) -> Date? {
let calendar = Calendar(identifier: .gregorian)
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
return calendar.date(from: dateComponents) ?? nil
}
}
And the output is
today date = 2022-09-04 22:00:00 +0000
Date and time can be a bit tricky. The Date struct stores a point in time relative to GMT. If you print it it will show exactly that.
Solution:
DonĀ“t use print, use a proper Dateformatter. To illustrate what I mean use this in a playground:
let date = Calendar.current.startOfDay(for: Date())
print(date)
//2022-09-03 22:00:00 +0000
// when it is 4.th of september 00:00 in my timezone (+- Daylight saving) it is this time in GMT
let formatter = DateFormatter()
formatter.dateFormat = "dd MM yyyy HH:mm:ss"
print(formatter.string(from: date))
//04 09 2022 00:00:00
// this is the time in my timezone
So the issue here is not that it has the wrong time, it is just not presented in the correct time zone.

GMT to local time conversion ERROR with swift UIKIT

Does anyone have an answer to why I am getting -8 hours instead of -7 (my timezone) hours difference upon converting time from GMT to local i using the formula below:
print("TIMEZONE IN HOURS: \(timeZone/3600)")
let interval = Date(timeIntervalSinceReferenceDate: 93866.4142533315)
print("GMT TIME: \(interval)")
let intervalDateComponents = calendar.dateComponents([.hour, .minute, .second], from: interval)
let mHour = intervalDateComponents.hour ?? 0
let mMinute = intervalDateComponents.minute ?? 0
print("INTERVAL DATE COMPONENT: \(intervalDateComponents)")
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let intervalDateString = formatter.string(from: interval)
print("DATE STRING FORMAT: \(intervalDateString)")
prints out the following: (please pay attention to date)
TIMEZONE IN HOURS: -7
GMT TIME: 2001-01-02 02:04:26 +0000 (January 2)
INTERVAL DATE COMPONENT: hour: 18 minute: 4 second: 26 isLeapMonth: false
DATE STRING FORMAT: 2001-01-01 18:04:26 (January 1)
interval HOURS: 18
edit/update:
let mDateString = formatter.string(from: interval)
print("DATE STRING FORMAT: (mDateString)")
let mdate = formatter.date(from: mDateString)
print("MDATE: (mdate)")
let mDateComponents = calendar.dateComponents([.hour, .minute, .second], from: mdate!)
print("M DATE COMPONENT: (mDateComponents)")
prints out:
DATE STRING FORMAT: 2001-01-01 19:01:27 -0700
MDATE: Optional(2001-01-02 02:01:27 +0000)
M DATE COMPONENT: hour: 18 minute: 1 second: 27 isLeapMonth: false
If you notice above in the code my intention is to extract the local time component to trigger alarm at a later time daily. So I fed the string that shows
2001-01-01 19:01:27 -0700
I converted the string to mdate in order to get time components that I need to use for the alarm to work on time so I got (hour: 18) rather than hour: 19 which I used to generate the mdate string.
The problem there is that you are getting the timezone offset for today. You need to get the timezone offset for the same date you are using:
let timeZone = TimeZone.current.secondsFromGMT(for: interval)
print("TIMEZONE IN HOURS: \(timeZone/3600)")

doesRelativeDateFormatting not working for future date in swift

/// A date formatter for displaying the complete relative date plus the time plus day, no seconds.
///
/// Examples: Today at 2:07 PM; Yesterday at 2:07 PM; Monday, Oct 7, 2018 at 5:05 AM
///
static let relativeFullDateShortFormatterWithDay: DateFormatter = {
let formatter = DateFormatter()
formatter.timeStyle = .short
formatter.dateStyle = .medium
formatter.doesRelativeDateFormatting = true
formatter.timeZone = TimeZone.current
formatter.locale = Locale.autoupdatingCurrent
return formatter
}()
From the server, I m receiving the expiry date. If the expiry date is of today, I want to show Today and if the expiry date is within week, I want to show weekday name.
I m setting doesRelativeDateFormatting to true for this. From the server I m getting expiry date of Sep 4 which is on Friday. When I convert Date to a string using relativeFullDateShortFormatterWithDay it is returning Sep 4, 2020 instead of Friday.
is there any more setting i need to set in formatter? For past dates, this is working fine.
Given the formatter in the question and a special weekday formatter
let weekDayFormatter = DateFormatter()
weekDayFormatter.dateFormat = "EEEE HH:mm"
(if the locale is always the same the format could be changed to "EEEE 'at' HH:mm" for English)
We can then use a function that formats a date by first checking if the fist formatter returns something like "Today" and then returns that and otherwise checks if the given date is within the next week and then returns a weekday
func format(date: Date) -> String {
formatter.doesRelativeDateFormatting = true
let relative = formatter.string(from: date)
formatter.doesRelativeDateFormatting = false
let absolute = formatter.string(from: date)
if relative != absolute {
return relative
}
let calendar = Calendar.current
let startDate = calendar.startOfDay(for: Date())
guard let endDate = calendar.date(byAdding: .day, value: 7, to: startDate) else {
return formatter.string(from: date)
}
if date > startDate && date < endDate {
return weekDayFormatter.string(from: date)
} else {
return formatter.string(from: date)
}
}
Example
print(format(date: Date(timeIntervalSinceNow: 60 * 60 * 24 * 1)))
print(format(date: Date(timeIntervalSinceNow: 60 * 60 * 24 * 4)))
print(format(date: Date(timeIntervalSinceNow: 60 * 60 * 24 * 12)))
Tomorrow at 17:07
Saturday 17:07
13 Sep 2020 at 17:07

Get day name and number of times day repeat in date range in swift

I have a scenario in which I get a date from backend API, I get a start date and end date, now I want to first check the day on that date, for example, today is 8-11-2019 is Friday, and end date is 20-11-2019. Now I want to check each date day name and also a number of times day are repeating in this date range. For example if Friday is twice in this date range then I want to get those dates on click of Friday, The dates range are dynamic it can vary. Example check in screen shot,
Here is the code :
let startDate : Date = Date()
let endDate : Date = Date()
let calendar = Calendar.current
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss.SSS"
let dateFormatter2 = DateFormatter()
dateFormatter2.dateFormat = "EEEE"
let components = calendar.dateComponents([.day], from: startDate, to: endDate) //returns number of days
var date : Date = startDate
var numberOfDays : [String : (Int,[Date])] = ["Sunday" : (0, []), "Monday" : (0, []), "Tuesday" : (0, []), "Wednesday" : (0, []), "Thursday" : (0, []), "Friday" : (0, []), "Saturday" : (0, [])]
while date <= date2 {
let day : String = dateFormatter2.string(from: date)
print(day)
if let value = numberOfDays[day] {
var tempDateArr = value.1
tempDateArr.append(date)
numberOfDays[day] = ((value.0 + 1), tempDateArr)
}
date = calendar.date(byAdding: .day, value: 1, to: date)!
}
print(numberOfDays) // number of times day are repeating
The below function return an [Int: Int] dictionary, where the key is the weekday (as defined by the current calendar) and the value is the number of occurrences in the date range
func distributionOfDays(from date1: Date, to date2:Date) -> [Int:Int]{
var date = min(date1, date2)
let endDate = max(date1, date2)
var days = [Int:Int]()
while date <= endDate {
let day = Calendar.current.component(.weekday, from: date)
days[day] = (days[day] ?? 0) + 1
date = Calendar.current.date(byAdding: .day, value: 1, to: date)!
}
return days
If you want, you can format/output the data using something like this:
let days = distributionOfDays(from: date1, to: date2).sorted(by: {$0.key < $1.key})
days.forEach{
print("\(Calendar.current.shortWeekdaySymbols[$0.key - 1]), \($0.value)")
}
Need to subtract - 1 from the key as the .weekday component is 1-indexed, and shortWeekDaySymbols is 0-indexed.

Time stamp from string issue?

I have two string , date and time . date string has a date in format "MM-dd-yyyy" and time in format "hh:mm a" , I want to create a 10 digit timestamp from the same . I did the following but I am getting issue with this. Any help is appreciated.
let idate = (userInstance.userData?.Date!)! + "T" + (userInstance.userData?.Time!)! + "+0000"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
let date = dateFormatter.date(from: idate)!
print(date)
let timestamp = Int(date.timeIntervalSince1970)
print(timestamp)
You cannot force a date containing AM/PM time to ISO 8601. ISO 8601 dates are always represented in 24-hour mode.
Besides your order of year, month and day is not ISO 8601 compliant.
Specify the appropriate date format MM-dd-yyyyhh:mm aZ
let datePart = "09-18-2018"
let timePart = "4:22 pm"
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM-dd-yyyyhh:mm aZ"
let date = dateFormatter.date(from: datePart + timePart + "+0000")!
let timestamp = Int(date.timeIntervalSince1970)
print(timestamp)
You are crashing here:
let date = dateFormatter.date(from: idate)!
That's because you are claiming that idate is a string in this format:
"yyyy-MM-dd'T'HH:mm:ssZ"
But it isn't. When you convert from a string to a date, your format string must exactly match the format of the string.
Then you can supply a different format and convert the date to a new string.