How to adding day to Date with DateToolsSwift? - swift

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

Related

Swift - Get weekOfYear for a DateComponents entry

I'm facing some problems to get the weekOfYear for a valid DateComponents value. This code
let cal = Calendar.current
let dc = DateComponents(calendar:cal, year: 2023, month: 1, day:12)
let woy = dc.weekOfYear
print("Week of year: \(woy)")
generates
Week of year: nil
as an output.
I've expected 2...
DateComponents is a simple type. It is merely a collection of date components. It does not do date computations like calculating the week of year. That's the job of Calendar. You did not give it a week of year when you initialise the date components, so you get nil when you ask for it.
You can ask the calendar to make a Date out of the DateComponents you have, and ask it what the week of year is:
let cal = Calendar.current
let dc = DateComponents(calendar:cal, year: 2023, month: 1, day:12)
if let date = cal.date(from: dc) {
// prints 2 as expected
print(cal.component(.weekOfYear, from: date))
}

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.

TimeDifference between minimum date and maximum date

i am completely new in iPhone app development. i am trying to find difference between min_date and max_date in hours. and wants save its value in textfield. Kindly Provide me complete code to find out difference between both of dates. e.g. if min_date: 12/07/1989, 12:00 am and max_date: 13/07/1989,12:00 am , then total hours will be 24 hours. Please provide me code in swift 3.0.
First, use timeIntervalSince to get the difference in seconds:
let timeInterval = max_date.timeIntervalSince(min_date)
Then you can do some maths to calculate the number of hours
let hours = timeInterval / 60 / 60
You can choose to floor or ceiling this number, depending on your requirements.
let previousDate = ...
let now = Date()
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.allowedUnits = [.month, .day, .hour, .minute, .second]
formatter.maximumUnitCount = 2 // often, you don't care about seconds
if the elapsed time is in months, so you'll set max unit to whatever is
appropriate in your case
let string = formatter.string(from: previousDate, to: now)
let minDate = Date() // your min date
let maxDate = Date() // your max date
let components = Calendar.current.dateComponents([.hour], from: minDate, to: maxDate)
It's the most flexible method for search difference between two dates. You can use other components to search for months, days, years etc.

How to get the last date of the month [duplicate]

This question already has answers here:
first and last day of the current month in swift
(11 answers)
Closed 6 years ago.
I have spent almost a week with unsuccessful tries. How do I get the last date of the current month. For example if it is February in non-leap year then the last date of the month must be 28. How do get the '28'. Another example: If it is January then the last date must be '31'
There are many ways, here are two of them:
Get next 1. and subtract one day
func lastDayOfMonth1() -> Date
{
let calendar = Calendar.current
let components = DateComponents(day:1)
let startOfNextMonth = calendar.nextDate(after:Date(), matching: components, matchingPolicy: .nextTime)!
return calendar.date(byAdding:.day, value: -1, to: startOfNextMonth)!
}
print(lastDayOfMonth1())
Use range(of:in:for:) to get the last day from the range and set the components accordingly:
func lastDayOfMonth2() -> Date
{
let calendar = Calendar.current
let now = Date()
var components = calendar.dateComponents([.year, .month, .day], from: now)
let range = calendar.range(of: .day, in: .month, for: now)!
components.day = range.upperBound - 1
return calendar.date(from: components)!
}
print(lastDayOfMonth2())