Get Date by supplying a month and year - swift

I’m trying to create a Date extension static function that accepts two parameters: (month: Int, year: Int) and returns -> Date.
The month that is returned would be its .startOfMonth:
var startOfMonth: Date {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components)!
}
I know how to retrieve a month in the future by adding n-months to the current:
func addMonths(_ numMonths: Int) -> Date {
let cal = NSCalendar.current
return cal.date(byAdding: .month, value: numMonths, to: self)!
}
And I suppose this could be used in a roundabout way to determine what I’m looking for, by first determining how many months from the current is the month I’m interested in getting a Date returned for. But I would much prefer if I could retrieve it without needing to go through this step, especially if there’s a possibility that I’m not actually sure if the month is in fact in the future.

You can use DateComponents initializer to compose a new date just passing the month and year components. Just make sure to pass a calendar as well otherwise the result would be nil. Maybe something like:
extension Date {
static func startOfMonth(for month: Int, of year: Int, using calendar: Calendar = .current) -> Date? {
DateComponents(calendar: calendar, year: year, month: month).date
}
}
Date.startOfMonth(for: 2, of: 2021) // "Feb 1, 2021 at 12:00 AM"

Related

Swift Date: How to tell if a month can have a leap day?

I'm building a calendar view that I want to be agnostic of the year, just list all the possible dates that can occur in a month. I.e. to show the maximum number of days in a calendar, like February 29th. From this answer, I know there are other calendar systems that also have leap days, so I'm curious how I might be able to tell if a calendar has a leap day, regardless of the calendar system or year. Any help would be greatly appreciated! Here is what I have currently to get the number of days in a month:
func days(in month: Int) -> Int {
let components = DateComponents(month: month+1, day: -1)
let lastDay = Calendar.current.date(from: components)!
return Calendar.current.dateComponents([.day], from: lastDay).day!+1
}
This works great, but is based on the current year, which may not be a leap year.
I've seen isLeapMonth but this doesn't seem to work to query in the same way as .day.
edit/update:
You can get the next leap year for the current calendar and check the maximum number of days in month, maybe something like this:
extension Date {
var year: Int { Calendar.current.component(.year, from: self) }
var isLeapYear: Bool { Calendar.current.range(of: .day, in: .year, for: self)!.count == 366 }
// find the leap year
static var leapYear: Int {
var year = Date().year
while DateComponents(calendar: .current, year: year).date?.isLeapYear == false {
year += 1
}
return year
}
}
Date.leapYear // 2020
func maximumNumberOfDays(in month: Int) -> Int? {
Calendar.current.range(of: .day,
in: .month,
for: DateComponents(calendar: .current,
year: Date.leapYear,
month: month).date!)?.count
}
maximumNumberOfDays(in: 2) // 29

Current date brings back time as well

I'm trying to get the date only, I wrote this:
func getCurrentDate() -> Date {
dateFormatter.dateFormat = "dd.MM.yyyy"
let result = dateFormatter.string(from: date)
print(result)
let date = dateFormatter.date(from: result)
return date!
}
printing 'result' I get:
12.07.2019
which is what I need, but converting it to Date type and printing it results in:
2019-07-11 21:00:00 +0000
Why does that happen and how can I fix it ?
Date was very unfortunately named. It has nothing to do with dates. It represents an instant in time. An instant that all observers (ignoring details like Relativity) would agree on. It isn't "a day on the calendar." It's a point in time, independent of any calendar.
The fact that it prints out in a "calendar-like" way is just a convenience for debugging.
If you want "a specific day on a calendar" then the tools you want is DateComponents and Calendar, not Date:
func currentDay() -> DateComponents {
return Calendar.current.dateComponents([.year, .month, .day], from: Date())
}
Keep in mind that the user's current calendar may not be Gregorian. If you require the Gregorian calendar, you need to say so:
func currentDay() -> DateComponents {
return Calendar(identifier: .gregorian).dateComponents([.year, .month, .day],
from: Date())
}
The Gregorian calendar is the one where the current year is 2019. In the Hebrew calendar, it's 5779. In the Buddhist calendar, it's 2562. In the Islamic calendar, it's 1440. There is no one "correct" calendar, which is why you need to provide one.
Reading your comments, it's possible you mean "the start of 'today' using the current calendar." If that's what you want, then you would use this:
func currentDay() -> Date {
return Calendar.current.startOfDay(for: Date())
}
(Or use the Gregorian calendar if needed, though I don't believe any supported calendar disagrees about what the start of the day is.)

Swift - nextDate() search by day of year

Hoping for some help on this one. I'm trying to search (forwards and backwards) to find the next date that matches the day of the year
So, for instance I have a day value of 300, I know we are currently on day 237 of this year (as of writing this post, 25/08/18!), and I want to search backwards to find the previous occurrence of the 300th day of a year, and format a date from it.
I'm already extracting the 'day of year' from date using a small Date extension:
extension Date {
var dayOfYear: Int {
return Calendar.current.ordinality(of: .day, in: .year, for: self)!
}
}
Using the Calendar.nextDate() function I can search to match '.day' , but that's obviously day of month, not year:
let potentialSartDate = (Calendar.current as NSCalendar).nextDate(after: nowDate, matching: .day, value: dayOfYearValueToMatch, options: [.matchNextTime, .searchBackwards])!
Does anyone have any pointers, or approach, of how to perform a search like this build a date out of the result?
Thanks in advance!
Emile
You can get the current year component from today and use the Calendar date(from: DateComponents) method to get the 300th day of the year as follow:
extension Calendar {
static let iso8601 = Calendar(identifier: .iso8601)
}
extension Date {
var year: Int {
return Calendar.current.component(.year, from: self)
}
var last300ThDayOfYear: Date {
return Calendar.current.date(from: DateComponents(calendar: .iso8601, year: dayOfYear > 300 ? year : year - 1, day: 300))!
}
var dayOfYear: Int {
return Calendar.current.ordinality(of: .day, in: .year, for: self)!
}
}
let date = Date().last300ThDayOfYear // "Oct 27, 2017 at 12:00 AM"
print(date.dayOfYear) // 300

Get unavailable date in a datepicker (Swift)

I have a UIDatePicker which is available for days from today to the next week ( from Date() to Date().day + 7 ).
Suppose that today is 30th day of the month, so the available days to choose are 30th day of this month and first to 6th day of next month.
In this case, I need to change the datePicker month to next month as the user change day from 30 to 1.
But the problem is:
"When I change the day to an invalid one, the .change method give me the least valid day."
Example:
Assumptions:
Today = 28 July.
Choosable dates = 28 July to 3 August.
What I need:
User can not choose the days before 28 July and not after 3 August.
When user wants to change day to 1, 2 or 3 August, the datePicker itself change the month to August and make 1, 2 and 3 available to
be chosen by user.
Main Problem
When user change the day to 1, 2 or 3, I can not get the 1, 2 or 3! and it returns me "28"!
Just set maximumDate property of your UIDatePicker to desired date.
func setupDatePicker(){
var currDateComponents = getDateComponents(fromDate: Date())
var maxDateComponents = currDateComponents
if let date = currDateComponents?.day{
maxDateComponents?.day = date + 7
}
self.datePicker?.maximumDate = getDate(fromDateComponents: maxDateComponents)
}
func getDateComponents(fromDate date: Date?)-> DateComponents?{
let calendar = Calendar(identifier: .gregorian)
var dateComponents: DateComponents? = nil
if(date != nil){
dateComponents = calendar.dateComponents([.day, .month, .year], from: date!)
}
return dateComponents
}
func getDate(fromDateComponents dateComponents:DateComponents?)-> Date?{
let calendar = Calendar(identifier: .gregorian)
if let componentsBasedDate = calendar.date(from: dateComponents!) {
return componentsBasedDate
}
return nil
}
Call this function in viewDidLoad to setup calendar for setting maximum and minimum date:
fileprivate func setupCalendar() {
datePicker.minimumDate = Calendar.current.date(byAdding: .day, value: 0, to: ServerTime.sharedInstance.nowTime)
datePicker.maximumDate = Calendar.current.date(byAdding: .day, value: +7, to: ServerTime.sharedInstance.nowTime)
}
for calculate days between shown date and current date I've used this method:
extension Date {
//Calculate days between two day objects
func daysBetween(_ date: Date) -> Int {
let calendar = NSCalendar.current
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: date)
let date2 = calendar.startOfDay(for: self)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day ?? 0 // This will return the number of day(s) between dates
}
}}
In fact, it is impossible to get date before they chose. And according to unavailability of days in next month before changing the month, I can not use UIDatePicker to handle it.
So Finally I implemented my custom UIPickerView.
as #holex said.
Thank you all for answering.

Number to Date - Swift

Getting the day of year is straightforward, e.g.
func dayOfYear(inputDate:NSDate) -> (Int) {
let cal = NSCalendar.currentCalendar()
let returnDay = cal.ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: inputDate)
return returnDay
}
But how do you do the reverse? It would obviously return just the day/month. I can easily write a tedious routine back-calculating but is there a smart way?
Yes, NSCalendar provides a way to coalesce calendar components into a single date object. Take a look at this example I wrote in a Playground:
import UIKit
import Foundation
let inputDate: NSDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let day = calendar.ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: inputDate)
let components = NSDateComponents()
components.day = day
let date = calendar.dateFromComponents(components)
According to the documentation,
When there are insufficient components provided to completely specify an absolute time, a calendar uses default values of its choice. When there is inconsistent information, a calendar may ignore some of the components parameters or the method may return nil. Unnecessary components are ignored (for example, Day takes precedence over Weekday and Weekday ordinals).
Furthermore,
Note that some computations can take a relatively long time to perform.
See the NSCalendar Class Reference for more information.
func dayOfYear(inputDate: NSDate) -> Int {
return NSCalendar.currentCalendar().ordinalityOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitYear, forDate: inputDate)
}
func dateFromDayOfYear(day: Int) -> NSDate {
return NSCalendar.currentCalendar().dateWithEra(1, year: NSCalendar.currentCalendar().component(.CalendarUnitYear, fromDate: NSDate()), month: 1, day: day, hour: 0, minute: 0, second: 0, nanosecond: 0)!
}
let numberOfDays = dayOfYear(NSDate()) // 195
dateFromDayOfYear(numberOfDays) // "Jul 14, 2015, 12:00 AM"