I coded a SwiftUI App, and the User can choose how much days are left. For example he choose 20 days, it should count in a Circle. For example 10 days are done, the circle should be at 50%.
So I thought I create a Int that is everyday increasing by one. When the user choose 20 days, the Int should start to increase and after 10 days for example the Int is at 10 and because the user choose 20 days, the circle should be at 50%.
Days / Int(that is increasing)
But I dont know how to code the Int that is increasing everyday.
Could anyone help me?
Adding to the suggestion of storing the start date and using today's date to know how many days have passed, you can use this extension
extension Date {
func daysSinceDate(_ fromDate: Date = Date()) -> Int {
let earliest = self < fromDate ? self : fromDate
let latest = (earliest == self) ? fromDate : self
let earlierComponents:DateComponents = Calendar.current.dateComponents([.day], from: earliest)
let laterComponents:DateComponents = Calendar.current.dateComponents([.day], from: latest)
guard
let earlierDay = earlierComponents.day,
let laterDay = laterComponents.day,
laterDay >= earlierDay
else {
return 0
}
return laterDay - earlierDay
}
func dateForDaysFromNow(_ days: Int) -> Date? {
var dayComponent = DateComponents()
dayComponent.day = days
return Calendar.current.date(byAdding: dayComponent, to: self)
}
}
Related
Just a question in regards to the times in Swift. I have a timestamp, which I want to validate whether it is 24 hours past the current time at the moment. I tried a few options and this is where my code is at the moment however I am stuck. Any tips will be highly appreciated!
let timeStampsOfUser = timeStampsOfUser.value as Any as! Double
print(timeStampsOfUser)
let timeInMiliSecDate = Date()
let timeInMiliSec = Double (timeInMiliSecDate.timeIntervalSince1970 * 1000)
print(timeInMiliSec)
if timeInMiliSec - timeStampsOfUser > 86400000 {
// how do I get the total count of timestamps which have been more than 24 hours and vice versa?
}
Update: However, how do I found the count? As in, how many of the timestamps are more and less than 24 hours? Thanks
let timeStampsOfUser = timeStampsOfUser.value as Any as! Double
print(timeStampsOfUser)
let timeInMiliSecDate = Date()
let timeInMiliSec = Double (timeInMiliSecDate.timeIntervalSince1970 * 1000)
print(timeInMiliSec)
let timeDone = timeInMiliSec - timeStampsOfUser > 86400000
if timeDone == true {
print("more than 24 hours")
} else {
print("less than 24 hours")
}
86400000 is the wrong approach. Never use 86400-math. Days could have 23 or 25 hours.
Create an extension of Date with 2 functions, an init method which takes a timestamp in milliseconds and a function to check if a given date is in the last 24 hours (actually the difference is less than one day). Calendar can do this with DateComponents pretty reliably.
extension Date {
init(timestampInMilliseconds: Double) {
self.init(timeIntervalSince1970: timestampInMilliseconds / 1000.0)
}
func isDateInLast24Hours() -> Bool {
Calendar.current.dateComponents([.day], from: self, to: .now).day! == 0
}
}
And use it
let timeStamp = 1665584000000.0 // 12. Oct 2022, 16:13
let date = Date(timestampInMilliseconds: timeStamp)
let isInLast24Hours = date.isDateInLast24Hours()
To check multiple timestamps use a loop or map
I have 2 dates. I don't care about the date portion, just the time.
How can I compare 2 dates and get the timeinterval between 2 dates?
Should I set the dates to 01-01-2000 and leave the time alone to compare?
Use DateComponents and get the hour, minute, and second of the two dates. At this point you have to assume a full 24 hour, 86400 seconds per day. There's no need to worry about daylight saving or leap seconds or anything since you are doing date independent calculations.
Convert the hours, minutes, and seconds into total seconds of the day for the two dates. Then simply subtract the two totals and you have the difference.
Here's a helpful Date extension:
extension Date {
func secondsSinceMidnight() -> TimeInterval {
let comps = Calendar.current.dateComponents([.hour,.minute,.second], from: self)
return TimeInterval(comps.hour! * 3600 + comps.minute! * 60 + comps.second!)
}
func timeDifference(to date: Date) -> TimeInterval {
return date.secondsSinceMidnight() - self.secondsSinceMidnight()
}
}
Call timeDifference(to:) using your two dates and you will get the difference in seconds ignoring the date portion of the dates.
A negative result means that the to date is closer to midnight.
This is an alternative to rmaddy's solution completely based on DateComponents
extension Date {
func timeComponents() -> DateComponents {
return Calendar.current.dateComponents([.hour,.minute,.second], from: self)
}
func timeDifference(to date: Date) -> Int {
return Calendar.current.dateComponents([.second], from: date.timeComponents(), to: self.timeComponents()).second!
}
}
If you have two dates you can use the method timeIntervalSince(Date).
For instance:
func calculateElapsedTime(from someTime: Date) -> TimeInterval {
let currentTime = Date()
var elapsedTime = currentTime.timeIntervalSince(someTime)
return elapsedTime
}
If you only want to consider the time difference between the two dates, you first have to normalize the date. This can be done in the following cumbersome way:
let currentDate = Date()
let anotherDate = Date(timeInterval: 60, since: currentDate)
let formatter = DateFormatter()
formatter.timeStyle = .short
let currentTime = formatter.string(from: currentDate)
let anotherTime = formatter.string(from: anotherDate)
let currentIntervalTime = formatter.date(from: currentTime)
let anotherIntervalTime = formatter.date(from: anotherTime)
let elapsedTime = anotherIntervalTime?.timeIntervalSince(currentIntervalTime!)
I've looked at this question, but a lot of it doesn't make sense nor work:
How to determine if a business is open given the hours of operation (Swift-iOS)
Some places on my list open at like 7:30 am and close the next day at 4 am. I have the times on my Parse-server listed as such:
openTime (Number): 7.5 (for 7:30 am)
closeTime (Number): and 4 for (4 am)
However, when I use the logic from the linked questions,
if now.hour! > Int(openTime) && now.hour! < Int(closeTime) {}
it keeps saying that business is closed. How could I adjust the numbers or the logic, in order to make it work for places that close late night / early morning the next day?
You can consider that closeTime must be superior to openTime, otherwise its the day after.
so it will become something like:
let realCloseTime = closeTime < openTime ? closeTime + 24 : closeTime
if now.hour! > Int(openTime) && now.hour! < Int(realCloseTime) {}
You will have a problem with minutes. Stores that open at 7:30 will report being open at 7:00. Stores that close at 11:30 will report being closed at 11:00.
Assuming that open and close are doubles.
var openTime: Double
var closeTime: Double
then
func isOpen(at date: Date = Date()) -> Bool {
guard let openDate = createDate(bySettingHours: openTime, of: date) else { return false }
guard let closeDate = createDate(bySettingHours: closeTime, of: date) else { return false }
guard let adjustedCloseDate = add24Hours(to: closeDate) else { return false }
let realCloseDate = openDate < closeDate ? closeDate : adjustedCloseDate
return openDate <= date && date <= realCloseDate
}
private func createDate(bySettingHours double: Double, of date: Date) -> Date? {
let hour = Int(floor(double)) % 24
let minute = Int(double * 30) % 30
return Calendar.current.date(bySettingHour: hour, minute: minute, second: 0, of: date)
}
private func add24Hours(to date: Date) -> Date? {
return Calendar.current.date(byAdding: .hour, value: 24, to: date)
}
I was assuming you have some kind of model like
class Business {
var openTime: Double
var closeTime: Double
}
My suggestion was to add the isOpen(at:) method here.
class Business {
var openTime: Double
var closeTime: Double
func isOpen(at date: Date = Date()) -> Bool {
// implmentation
}
}
It would be used something like this
var business = Business()
// Setup `business`
business.isOpen()
// or
let now = Date()
business.isOpen(at: now)
I have an app that presents data that expires every 30 seconds (precisely, at h/m/s 11:30:00, 11:30:30, 11:31:00, etc).
I can get the current time, but I am unsure on how to calculate the time between now and the nearest thirty seconds.
Anything I've found is in Objective-C, and I've been unable to convert it.
Here's what I tried:
func nearestThirtySeconds() -> Date? {
var components = NSCalendar.current.dateComponents([.second], from: self)
let second = components.second ?? 30
components.second = second >= 30 ? 60 - second : -second
return Calendar.current.date(byAdding: components, to: self)
}
But this returns the nearest minute (I think, it always returns a definite minute)
Any ideas?
You can round the seconds to the nearest multiple of 30,
and then add the difference between the rounded and the original
value to the date:
extension Date {
func nearestThirtySeconds() -> Date {
let cal = Calendar.current
let seconds = cal.component(.second, from: self)
// Compute nearest multiple of 30:
let roundedSeconds = lrint(Double(seconds) / 30) * 30
return cal.date(byAdding: .second, value: roundedSeconds - seconds, to: self)!
}
}
That should be good enough to display the rounded time, however it
is not exact: A Date includes also fractional seconds, so
for example "11:30:10.123" would become "11:30:00.123" and not "11:30:00.000". Here is another approach which solves that problem:
extension Date {
func nearestThirtySeconds() -> Date {
let cal = Calendar.current
let startOfMinute = cal.dateInterval(of: .minute, for: self)!.start
var seconds = self.timeIntervalSince(startOfMinute)
seconds = (seconds / 30).rounded() * 30
return startOfMinute.addingTimeInterval(seconds)
}
}
Now seconds is the time interval since the start of the current minute
(including fractional seconds). That interval is rounded to the nearest
multiple of 30 and added to the start of the minute.
I used the answer by Martin R to write a more generic version to round by any time period.
Answer is outdated and only works with time, check gist for the latest version.
https://gist.github.com/casperzandbergenyaacomm/83c6a585073fd7da2e1fbb97c9bcd38a
extension Date {
func rounded(on amount: Int, _ component: Calendar.Component) -> Date {
let cal = Calendar.current
let value = cal.component(component, from: self)
// Compute nearest multiple of amount:
let roundedValue = lrint(Double(value) / Double(amount)) * amount
let newDate = cal.date(byAdding: component, value: roundedValue - value, to: self)!
return newDate.floorAllComponents(before: component)
}
func floorAllComponents(before component: Calendar.Component) -> Date {
// All components to round ordered by length
let components = [Calendar.Component.year, .month, .day, .hour, .minute, .second, .nanosecond]
guard let index = components.index(of: component) else {
fatalError("Wrong component")
}
let cal = Calendar.current
var date = self
components.suffix(from: index + 1).forEach { roundComponent in
let value = cal.component(roundComponent, from: date) * -1
date = cal.date(byAdding: roundComponent, value: value, to: date)!
}
return date
}
}
To round to x minutes you need to also floor the seconds so this also contains the floor method I wrote.
How to use:
let date: Date = Date() // 10:16:34
let roundedDate0 = date.rounded(on: 30, .second) // 10:16:30
let roundedDate1 = date.rounded(on: 15, .minute) // 10:15:00
let roundedDate2 = date.rounded(on: 1, .hour) // 10:00:00
There's a Cocoapod called 'SwiftDate' that is great for date formatting and manipulation that can applied to your Date() instance.
Example of how to use the date rounding method:
let date = Date() // 2018-10-01 23:05:29
date.dateRoundedAt(at: .toFloor5Mins) // 2018-10-01 23:05:00
date.dateRoundedAt(at: .toCeil5Mins) // 2018-10-01 23:10:00
date.dateRoundedAt(at: .toFloorMins(1)) // 2018-10-01 23:05:00
date.dateRoundedAt(at: .toCeilMins(1)) // 2018-10-01 23:06:00
(For reference, check out the documentation at https://cocoapods.org/pods/SwiftDate)
let now = Date()
var timeInterval = now.timeIntervalSinceReferenceDate
timeInterval += 30 - timeInterval.truncatingRemainder(dividingBy: 30)
let rounded = Date(timeIntervalSinceReferenceDate: timeInterval)
print("\(now) rounded to nearest 30 seconds is \(rounded)")
i have a date say 2 March 2016 stored as NSUserDefaults and i want to add a new row in TableView every time a new Month is about to come , so what should i do for accomplishing this , IMO comparing the stored Date and Current Date and if
in Curent Date a new Month is about to come in next 7 days then add the
row into table but i don't know where to start, anyone can give me some hint for checking current date's next 7 days for if a new months is about to come
and if my approach is not good enough then please correct me it'll be so appreciated by me and helpful for me
please see example for better understanding :
storedDate = 2 March 2016
currentDate = 26 March 2016
if CurrentDate + 1 Week == newMonth {
//add the Row into TableView
}
You can add an Extension to NSDate and then do all sorts of day/month addition
This method you can use to add 7 days to the current date...
func dateByAddingDays(daysToAdd: Int)-> NSDate {
let dateComponents = NSDateComponents()
dateComponents.day = daysToAdd
let newDate = NSCalendar.currentCalendar().dateByAddingComponents(dateComponents, toDate: self, options: .MatchFirst)
return newDate!
}
This method to add months to current date
func dateByAddingMonths(monthsToAdd: Int)-> NSDate {
let dateComponents = NSDateComponents()
dateComponents.month = monthsToAdd
let newDate = NSCalendar.currentCalendar().dateByAddingComponents(dateComponents, toDate: self, options: .MatchFirst)
return newDate!
}
Then you need to check that date you created and see if it its a different month than the one that is stored..
func compareMonths(newDate:NSDate)-> Bool {
let today = NSDate()
let todayPlusSeven = today.dateByAddingDays(7)
return todayPlusSeven.isNextMonth(storedDate)
}
Using this method to check if the months of 2 dates are the same
func isNextMonth(storedDate: NSDate)-> Bool {
return isSameMonthAsDate(storedDate.dateByAddingMonth(1))
}
func isSameMonthAsDate(compareDate: NSDate)-> Bool {
let comp1 = NSCalendar.currentCalendar().components([NSCalendarUnit.Year, NSCalendarUnit.Month], fromDate: self)
let comp2 = NSCalendar.currentCalendar().components([NSCalendarUnit.Year, NSCalendarUnit.Month], fromDate: compareDate)
return ((comp1.month == comp2.month) && (comp1.year == comp2.year))
}
An oldie but still goodie, is this page of Date helpers from Erica Sadun's github page here They are all in Obj-c but can be converted to swift easily enough. I still reference it when i need help doing date math