I keep failing to get a consistent list or count of dates between two datetimes when the date is different in the GMT and the user's timezone and when the difference in datetimes is less than 24 hours (even with separate dates). I've googled and keep trying different approaches, but I cannot figure it out. It's even uglier when the user wants to use multiple time zones, but I think that I can solve that once I understand what I am doing wrong here.
In my application, I get dates from the user in several time zones, store them as strings, like: 2017-12-08 12:30:00 +0900
I then attempt to make an array of dates between the start and end date. However, I have not been able to get the array to include all of the actual dates.
First, I tried to expand an array directly between the first and last days: (This works in my project but not in a playground?? I don't know why.
func getMyDates(_ start:String,_ end: String) -> [Date]? {
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let fromDate = dateFormatter.date(from: start)
let toDate = dateFormatter.date(from: end)
var dates = [Date]()
dates.append(fromDate!)
dates.append(toDate!)
print(dates)
return dates
}
func getDays() -> [Date]? {
guard let dates = getMyDates() else {return nil}
print("getDays got \(dates) from getMyDates()")
var userdates = dates
let fromDate = dates[0]
let toDate = dates[1]
print("changed it to: \(fromDate ... toDate)")
return fromDate ... toDate
}
This is working if the user's first and last dates are the same for their timezone and the GMT. For example, if the string entries are: 2017-11-08 12:30:00 +0900 and 2017-11-10 23:30:15 +0900, the output includes the three expected dates.
//getDays got [2017-11-08 03:30:00 +0000, 2017-11-10 14:30:15 +0000] from getMyDates()
//changed it to: [2017-11-08 03:30:00 +0000, 2017-11-09 03:30:00 +0000, 2017-11-10 03:30:00 +0000]
However, it fails when the time of day in the second datetime is earlier than the first, like: 2017-12-08 12:30:00 +0900 and 2017-12-09 09:30:15 +0900. In that case, I only get 1 day out:
//getDays got [2017-12-08 03:30:00 +0000, 2017-12-09 01:00:00 +0000] from getTripDates()
//changed it to: [2017-12-08 03:30:00 +0000]
Then, I tried calculating the number of days between them as suggested in this This question, but I get the same problem, as in the first one counts 2 days and the second counts 0 days. My plan was to take the number of days and use this approach.
I think it is a time zone issue, but I can't figure out how to solve it. I've tried setting and not setting the time zone to .current.
I cannot arbitrarily add a day because that will fail when the first datetime is earlier in the day than the second datetime ( like 2017-12-08 12:30:00 +0900 and 2017-12-09 15:30:15 +0900.
Using @malik's suggestion seems like it will work, but somehow it does not. We get the same behavior. If the second datetime TIME is later in the day it doesn't need an additional day (and somehow the seconds remainder does not work - gives too many days.
//in playground swift4
import UIKit
let start = "2017-12-08 12:30:00 +0900"
let end = "2017-12-10 13:30:00 +0900"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
var fromDate = dateFormatter.date(from: start)
let toDate = dateFormatter.date(from: end)
let cal = Calendar.autoupdatingCurrent
let seconds=cal.dateComponents([.second], from: fromDate!, to: toDate!)
var days = Int(seconds.second ?? 0)/(86400)
//days prints 2
//if (Int(seconds.second ?? 0) % (86400)) > 0 {
// days += 1
//}
var newDates = [Date]()
newDates.append(fromDate!)
for _ in 1 ... days {
let daysBetween = cal.date(byAdding: .day, value: 1, to: fromDate!)
fromDate = daysBetween
newDates.append(fromDate!)
}
//print(newDates)
outputs desired:
// [2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000, 2017-12-10 03:30:00 +0000]
However: if I change the end date to:
let end = "2017-12-10 09:30:00 +0900"
the output is wrong:
//[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000]
Adding in the additional day for partial days helps in this case (uncommenting out the logic:)
if (Int(seconds.second ?? 0) % (86400)) > 0 {
days += 1
}
yields correctly:
//[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000, 2017-12-10 03:30:00 +0000]
BUT: if I change end back while leaving the logic in, I get:
//[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000, 2017-12-10 03:30:00 +0000, 2017-12-11 03:30:00 +0000]
I thought it would be fine, but with the additional day, it messes up my later calls to see what else is going on those days because the extra day isn't in the original data.
So, maybe I need to just set up some comparisons of the HH:mm strings? It just doesn't make sense to me. I feel like I'm missing something obvious that would allow me to expand dates in the users' locale.
Using @malik's revised suggestion which lops off the hours and then calculates days works. Then, I can loop over the number of days to create an array.