Swift rounding down Date seconds to zero - swift

I have a bunch of Date objects that I want to perform calculations on, for these calculations I dont need second precision so I am attempting to "zero out" the seconds on my Dates. This all works fine unless the Date already has zero seconds in which case Swift decrements the number of minutes by one, e.g 10:30:00 -> 10:29:00.
My code:
let calendar = Calendar.current
var minuteComponent = DateComponents()
minuteComponent.second = 0
let dateDelta = calendar.nextDate(after: date, matching: minuteComponent, matchingPolicy: .nextTime, direction: .backward)
I have tried all the matching policies only to get the same result.
This seems odd to me, as the target is already at the required value, though I suspect it is inline with the documentation.
Is this an appropriate way to zero out the seconds while preserving the higher magnitude components or is there a better way?

One option is to use a date formatter, since this cuts off the seconds it will be the same as rounding down.
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm"
let noSeconds = formatter.date(from: formatter.string(from: someDate))
And here is a similar solution using Calendar & DateComponents
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: someDate)
let noSeconds = calendar.date(from: components)

Related

Converting total minutes to "HH:mm" format

I'm currently saving some timestamped data in the minutes format, eg 550 is 9:10AM.
Is there a way to convert this into the string "09:10"? I'll be using 24hr format.
I'm using swift for an iOS app, but if there is logic that's non-language specific, that would be helpful too.
Cheers,
Josh
The question is what the 550 really represents:
If it represents an abstract time interval, measured in minutes, you would likely convert it to a TimeInterval and then use DateComponentsFormatter to prepare a string representation of hours and minutes:
let timeInterval = TimeInterval(minutes * 60)
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute]
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
let string = formatter.string(from: timeInterval)
This is a simple “hours and minutes” representation. This pattern is especially useful if the number of hours could exceed 24 (e.g. 1,550 minutes is 25 hours and 50 minutes, or 1 day, 1 hour, and 50 minutes, depending upon whether you add .day to allowedUnits or not).
If, however, you really mean that the 550 minutes is intended to literally represent the time of the day, then you might use calendrical date calculations and use DateFormatter for a string representation of the time:
let timeInterval = TimeInterval(minutes * 60)
let date = Calendar.current.startOfDay(for: Date()).addingTimeInterval(timeInterval)
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "HH:mm"
let string = formatter.string(from: date)
But, that having been said, if this really represented 9:10am in the morning (not an abstract time interval nine hours and ten minutes) and you wanted to show it in the UI, you would generally honor the device’s preferred time format (am/pm or 24-hour clock):
let timeInterval = TimeInterval(minutes * 60)
let date = Calendar.current.startOfDay(for: Date()).addingTimeInterval(timeInterval)
let formatter = DateFormatter()
formatter.timeStyle = .short
formatter.dateStyle = .none
let string = formatter.string(from: date)
Either way (forcing 24 hour clock or honor the user’s preferences), though, you really are displaying a time of day, which means that this while it will be 9:10am most days of the year, if you do this on the day that we spring forward to daylight savings, it will say 10:10am, but if on the day we fall backwards back to standard time on that day, it would be 8:10am.
Clearly, the syntax is different in Objective-C than it is in Swift, but the basic API is the same (though you obviously would be using the NS prefixes, e.g. NSDateComponentsFormatter and/or NSDateFormatter).

UIKit - Calendar components return absolute value for a negative year

I'm having trouble with extracting the year from a Date. Everything works fine until I use a date that has a negative year. Then UIKit's Calendar.componten(:from:) returns the absolute value instead and is even off by 1. Here's an example:
import UIKit
let calendar = Calendar.current
let date = Date(timeIntervalSince1970: -90000000000)
print(date) // -0882-01-14 08:00:00 +0000
print(calendar.component(.year, from: date)) // 883
Any idea why that happens and how to correctly get the date without manually trying to figure out the year's sign?
Thanks!
Negative years should be present as B.S 9000 not "- 9000". In Swift there is era instance property as well. You can create your calendar and play with the values For example :
var newCalendar = Calendar(identifier: .gregorian)
newCalendar.timeZone = newCalendarTimeZone
let newCalendarEra = newCalendar.ordinality(of: .day, in: .era, for: now)

How to create two variables with the current time and day of the week?

I want to create two variables - one with today's day of the week (Monday - Sunday) and one with the current time. I want to do more than just display it in a label, but am just trying that for now.
I tried this, but my app crashes:
let weekDay = NSCalendar.current.component(.weekday, from: Date())
self.weekDayLabel.text = "\(weekDay)"
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "EEEE"
let dayInWeek = formatter.string(from: date)

actual date plus 14 days

i working with swift 3 for osx.
i have this calendar:
this calendar should show the actual date plus 14 days.
Generally it is correct, because 30 - 16 = 14 days between.
this is my code for this:
datePicker.dateValue = Calendar.current.date(byAdding: .day, value: 14, to: Date())!
but at the bottom of my calendar should be visible the days between now and the selected date. but here will be shown 13 days.
why 13 and not 14?
this text label will calculate like this:
txtDiffDays.stringValue = "(\(Calendar.current.dateComponents([.day], from: Date(), to: datePicker.dateValue).day!) Tage)"
where is my mistake?
The problem is that you are calling Date() twice.
When calculating the difference the from date must be the exact same date used when setting the datePicker. Otherwise it's slightly later and the amount of days is less than 14.
Here are some experiments that I did in the playground:
let now = Date()
let date = Calendar.current.date(byAdding: .day, value: 14, to: Date())!
let nowDay = Calendar.current.dateComponents([.day], from: Date()).day
let dateDay = Calendar.current.dateComponents([.day], from: date).day
date.timeIntervalSince1970 - Date().timeIntervalSince1970
date.timeIntervalSince1970 - now.timeIntervalSince1970
let days = Calendar.current.dateComponents([.day], from: Date().addingTimeInterval(-1), to: date).day!
let days2 = Calendar.current.dateComponents([.day], from: now, to: date).day!
Base on the result, I think what happens is that some time has passed between "setting the date picker's value" and "calculating the date difference". During both of these actions you did Date(). Since some time has passed, the date that he second Date() creates is a tiny little bit later than the one created by the first Date() call, as you can see from line 8 and 9 in the playground.
Since the second date is a little later, that makes the date difference a little less than 14 whole days, so dateComponents method rounds it down and you get 13.
To fix this, avoid doing two Date() calls. You can just create a local variable called now and use it throughout the method.
let now = Date()
Alternatively, you can use Date().addingTimeInterval(-1) like I did in playground line 10, but I feel like this is a cheat more than a solution.

comparing time without date swift3

I am using an API to get a list of times which is five times a day as string in (AM,PM) format without date,
("4:30 AM","1:00 PM","3:20: PM","6:40 PM","9:10 PM")
what I am trying to do is to show the next time in the screen depends on the current time on the iPhone
so if the time now is 5:00 PM the time should display on the screen is 6:40 PM
how can I compare times in swift without including the dates because I tried to convert the list of times I have to Date type but unfortunately am getting dates and the dates shows in 2001 so even if I try to compare the date would give me wrong result
print(self._praytime.arraytime)
for Dates in self._praytime.arraytime {
let dateformatters = DateFormatter()
dateformatters.dateStyle = .none
dateformatters.timeStyle = .short
let xxbbs = dateformatters.date(from: Dates)
print(xxbbs!)
}
after I got the times I put it in Array and I tried to execute the code upper so I can get the times without the date but what shows in the output screen was this
Optional(2000-01-01 11:14:00 +0000)
Optional(2000-01-01 20:06:00 +0000)
Optional(1999-12-31 23:57:00 +0000)
Optional(2000-01-01 03:11:00 +0000)
Optional(2000-01-01 04:41:00 +0000)
any idea how to solve this problem and if is there any other easy way to do this task I'm exciting to hear it
thank you all
Suggestion using DateComponents
Assuming this given array
let times = ["4:30 AM","1:00 PM","3:20 PM","6:40 PM","9:10 PM"]
Create a date formatter matching the format
let formatter = DateFormatter()
formatter.dateFormat = "h:mm a"
Map the time strings to Date and immediately to DateComponents considering only hour and minute
let dateArray = times.map { Calendar.current.dateComponents([.hour, .minute], from:formatter.date(from:$0)!) }
Map this array to the next date from now matching the components respectively
let upcomingDates = dateArray.map { Calendar.current.nextDate(after: Date(), matching: $0, matchingPolicy: .nextTime)! }
Sort the array ascending, the first item is the date you are looking for
let nextDate = upcomingDates.sorted().first!
Use the formatter to convert the date back to a time string
print(formatter.string(from:nextDate))