30 Min Time Increments not working in Swift - swift

I am having issues getting my code to work. I am trying to create 30 minutes slots in Swift, but it seems to randomly jump an hour every so often. See code below:
let calendar = Calendar.current
var hour = 07
var hour2 = 07
var minute = 0
var timeLoop = 1
var startDate = calendar.date(bySettingHour: hour, minute: 0, second: 0, of: editedDate)
var endDate = calendar.date(bySettingHour: hour2, minute: 0, second: 0, of: editedDate)
repeat {
if(timeLoop % 2 == 0){
startDate = calendar.date(bySettingHour: hour2, minute: 30, second: 0, of: editedDate)
endDate = calendar.date(bySettingHour: hour, minute: 0, second: 0, of: editedDate)
}
else {
startDate = calendar.date(bySettingHour: hour2, minute: 0, second: 0, of: editedDate)
endDate = calendar.date(bySettingHour: hour2, minute: 30, second: 0, of: editedDate)
}
if (timeLoop == 1) {
startDate = calendar.date(bySettingHour: hour, minute: 0, second: 0, of: editedDate)
endDate = calendar.date(bySettingHour: hour, minute: 30, second: 0, of: editedDate)
}
let eventDate = EventDate()
eventDate.startDate = startDate!
eventDate.endDate = endDate!
self.suggestedDates.append(eventDate)
self.suggestedDates.sort(by: {$0.startDate < $1.startDate}) //Recheck this
//Only need to add this once for day purposes
if (hour == 07) {
self.allDayDates.append(eventDate)
self.allDayDates.sort(by: {$0.startDate < $1.startDate}) //Recheck this
}
//update hours
hour2 = hour
hour += 1
timeLoop += 1
} while hour <= 21
This gives the following results
07:00 - 07:30,
07:30 - 08:00,
08:00 - 08:30,
09:30 - 10:00,
10:00 - 10:30,
11:30 - 12:00,
12:00 - 12:30,
13:30 - 14:00,
As you can see theres a jump from 10:30 to 11:30.

Your code does not give enough idea of what you are trying to do.
However to get the time slots you can do something like this:
let calendar = Calendar.current
let startHour = 07 // Hour from where you want to start the slots
var hourCounter = 07 // Hour counter for looping
let slotDuration = 30 // Constant for duration of slots
let editedDate = Date() // The selected date, as per your code
// Create the start date by setting hour value to the `startHour` of `editedDate`
var startDate = calendar.date(bySettingHour: startHour, minute: 0, second: 0, of: editedDate)
// Assign the same value to endDate as the initial value
var endDate = startDate
repeat {
// Assign the end date to the start date,
// This will get you the slot start duration from earlier slot's end date
// For example,
// If this is the starting slot,
// then the startDate and endDate will be same, as per above code.
// If this is any subsequent slot,
// then the slot should start from the last slot's endDate.
// i.e. if previous slot was 01:30 - 2:00,
// then current slot should start from 2:00.
startDate = endDate
if let date = startDate {
// Get the new endDate by adding your slot duration to the startDate
endDate = calendar.date(byAdding: .minute, value: slotDuration, to: date)
}
// ...
// Do whatever you want to do with these slot values here...
// ...
// Increment the counter for looping
hourCounter += 1
} while hourCounter <= 21
The code generates time slots based on the slot duration. You can change the slotDuration to any desired value (in minute), and it will generate slots accordingly. Try changing it from 30 to 15 and see the results.

Related

Swift Correct calculation of time as a proportion of day

I am trying to calculate the proportion of a day that a specific time equates to. For example, 06:00 is 0.25, 18:00 is 0.75 etc.
I am evaluating a series of dates of Date type, which were created in timeZone = "GMT". The routine below works fine. However when I evaluate a time after 23:00 for dates in DST, then the calculation goes wrong, as the time is evaluated as the next day (e.g. 23:08 is evaluated as 00:08)
Is there any way that I can recognise when the move from GMT to DST takes the date into the next day? I can then adjust the calculation accordingly.
My function for determining the proportion that the input time represents is:
func getTimeAsProportionOfDay(time: Date) -> Double {
//calculates the amount of day, between 0 and 1 given the input date
let calendar = Calendar.current
let hours = calendar.component(.hour, from: time)
let minutes = calendar.component(.minute, from: time)
let seconds = calendar.component(.second, from: time)
let totalSeconds = Double(hours * 60 * 60 + minutes * 60 + seconds)
return Double(totalSeconds) / Double(secondsInDay)
}
Also, I'm aware that my constant of secondsInDay (= 24*60*60) may not be technically correct but I'm not sure what system constant to replace it with.
Thanks.
You just need get the day after the original date and subtract a second. Then calculate the number of seconds in that date using calendar method
func ordinality(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Int?
You can make your life easier with some helpers
extension Date {
var dayAfter: Date { Calendar.current.date(byAdding: .day, value: 1, to: noon)!}
var noon: Date { Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! }
var startOfDay: Date { Calendar.current.startOfDay(for: self) }
var endOfDay: Date { Calendar.current.date(byAdding: .init(second: -1), to: dayAfter.startOfDay)! }
}
Testing the endOfDay
Date().endOfDay // "Feb 7, 2020 at 11:59 PM"
And your method:
func getTimeAsProportionOfDay(time: Date) -> Double {
// discarding the fractional seconds
let time = Calendar.current.date(bySetting: .nanosecond, value: 0, of: time)!
return Double(Calendar.current.ordinality(of: .second, in: .day, for: time)!-1) /
Double(Calendar.current.ordinality(of: .second, in: .day, for: time.endOfDay)!-1)
}
Playground testing:
let date = DateComponents(calendar: .current, year: 2020, month: 2, day: 7, hour: 23, minute: 08).date!
date.endOfDay
let result = getTimeAsProportionOfDay(time: date) // 0.9639000451394113
Generally speaking, I would do something like this:
let date = Date()
var dayStart = Date()
var dayDuration: TimeInterval = 0
Calendar.current.dateInterval(of: .day, start: &dayStart, interval: &dayDuration, for: date)
let timeInterval = date.timeIntervalSince(dayStart)
let percentage = timeInterval / dayDuration
print(percentage)
All, thanks for your help. I think I have found a solution to this by using a fixed start of day to compare against.
func getTimeAsProportionOfDay(time: Date) -> Double {
//calculates the amount of day, between 0 and 1 given the input date
if Int(time.timeIntervalSince(tides.tideDate)) > secondsInDay { //time has been moved to next day by BST change) so return 1 for gradient
return 1.0
} else {/// this was my original code
let calendar = Calendar.current
let hours = calendar.component(.hour, from: time)
let minutes = calendar.component(.minute, from: time)
let seconds = calendar.component(.second, from: time)
let totalSeconds = Double(hours * 60 * 60 + minutes * 60 + seconds)
return Double(totalSeconds) / Double(secondsInDay)
}
}

Convert Double (number of days since Dec 30, 1899) to Date in Swift?

I have a double value that represents the number of days since December 30, 1899 (the usual TDateTime value in Delphi). For example: 43854.4410269444
Is it possible to create a valid Date value from this double?
One way is to get a Date representing "1899-12-30", then call addingTimeInterval.
// either parse a date string...
let formatter = DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd"
let epoch = formatter.date(from: "1899-12-30")!
// or calculate the time directly from 1970
//let epoch = Date(timeIntervalSince1970: -86400 * 25569)
// now we add the quality of the number of days times the number of seconds in a day
let numberOfDays = 43854.4410269444
let result = epoch.addingTimeInterval(86400 * numberOfDays)
Here's my attempt to solve the problem (see below how I calculated the magic number):
extension Date {
init(fromDelphiTDateTime delphiDate: Double) {
//Number of seconds between 12/30/1899 12:00 AM and 1/1/1970 12:00 AM
let tDateTimeUnixTimeOffset = 2209161600.0
//24 * 60 * 60
let numberOfSecondsInDay = 86400.0
let delphiTDateTimeAsUnixTime = delphiDate * numberOfSecondsInDay - tDateTimeUnixTimeOffset
self.init(timeIntervalSince1970: delphiTDateTimeAsUnixTime)
}
}
I assumed that TDateTime doesn't have any timezone information.
Since the Unix time (timeIntervalSince1970) is in UTC, you'll need to convert your TDateTime value to UTC if it's in a different time zone.
Examples:
//Dec 30, 1899 at 12:00 AM
let date1 = Date(fromDelphiTDateTime: 0)
//Jan 24, 2020 at 11:04 AM
let date2 = Date(fromDelphiTDateTime: 43854.4410269444)
And here's how I calculated the magic number 2209161600.0 using Swift:
let zeroTDateTimeComponents = DateComponents(calendar: Calendar.init(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0), year: 1899, month: 12, day: 30, hour: 0, minute: 0, second: 0)
let zeroTDateTime = zeroTDateTimeComponents.date
let tDateTimeUnixTimeOffset = zeroTDateTime?.timeIntervalSince1970 //the value is 2209161600.0
Hope this helps and I'd be grateful if the community validates or further improves my answer. Some unit tests with the existing know pairs of TDateTime and its double value would certainly help.

Get time within radius

Lets imagine we have time line with two points:
A - start time of day
B - end time of day
extension Date: {
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var endOfDay: Date? {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfDay)
}
}
On the time line there is going one event -> it goes from specifed time, has event time stamp as an interval from 1970 ( event.eventTimestamp ) and there are two options :
It doesn't have end time -> it goes out of bounds after B- point ( duration = 0, to check for it we need to use Int(Date().timeIntervalSince1970) - event.eventTimeStamp
It does have specified end time -> We do have duration.
In both cases It might start few days before A and finish few days after B.
How could I check whenever event is in current day -> specifed by user. After we detect this let's save in variable 24h.
let duration = 24 * 60 * 60
struct Event {
var eventTimestamp: Int
var duration: Int = 0
}

How to adding day to Date with DateToolsSwift?

I'm using a package call DateToolsSwift. Now, whenever I want to add a day to a Date object, I would do like this
let date = Date().add(TimeChunk(seconds: 0, minutes: 0, hours: 0, days: 3, weeks: 0, months: 0, years: 0))
This code is too long and it does't feel right. So my question is, is this the way to do it in DateToolsSwift? Or I'm doing it wrong?
*Without using DateToolsSwift. For example, if you want to add 3 days to Jan 1, 1970
let aDate = Date(timeIntervalSince1970: 0)
var dateComponent = DateComponents()
dateComponent.day = 3
let next3Days = Calendar.current.date(byAdding: dateComponent, to: aDate)
Similarly, you can set aDate = Date() if you want to add days to today.
EDIT
Alternatively, from #CodeDifferent
let next3Days = Calendar.current.date(byAdding: .day, value: 3, to: aDate)!
The DateToolsSwift package defines extension methods in Integer+DateTools.swift which allow the simple creation of
TimeChunks, e.g. 3.days or 2.weeks. Therefore you can do
let date = Date().add(3.days)
or, since DateToolsSwift also defines a custom + operator,
let date = Date() + 3.days

Swift timezones with winter/summer times

I am working on a world clock, and i have gotten the times for various countries as so:
let date = NSDate();
var beltz = NSDateFormatter();
beltz.dateFormat = "HH:mm";
beltz.timeZone = NSTimeZone(name: "Europe/Brussels")
let belTzStr = beltz.stringFromDate(date);
println(belTzStr) //<-- correct time
My question is when summer/winter times adjust +- 1 hour, will this adjustment be reflected in the code by NSTimeZone? If not, how do i achieve this?
Consider this modified code:
let cal = NSCalendar.currentCalendar()
cal.timeZone = NSTimeZone(name: "America/Phoenix")!
let date = cal.dateWithEra(1, year: 2015, month: 12, day: 24, hour: 16, minute: 0, second: 0, nanosecond: 0)!
//date = NSDate();
var beltz = NSDateFormatter();
beltz.dateFormat = "dd MMMM HH:mm zzzz";
beltz.timeZone = NSTimeZone(name: "Europe/Brussels")
let belTzStr = beltz.stringFromDate(date);
print(belTzStr) //<-- correct time
I modified your code to add time as Phoenix AZ, which does not use summer time and added some extra formatting, especially the TZ to the printed data.
Now, if you use December (no DST in either region)
let date = cal.dateWithEra(1, year: 2015, month: 12, day: 24, hour: 16, minute: 0, second: 0, nanosecond: 0)!,
you get
25 December 00:00 Central European Standard Time
and if you use July (DST in EU)
let date = cal.dateWithEra(1, year: 2015, month: 7, day: 24, hour: 16, minute: 0, second: 0, nanosecond: 0)!,
you get
25 July 01:00 Central European Summer Time
So yes, it adjusts the TZ appropriately.