How to get the number of days of current/last year - ios

I want to find out the number of days from current and last year. I have managed to come up with this code which will give me a starting and ending date.
Then I use NSDateComponents to get the number of days for this current year. But what I get is 365, which means is the number of days from last year.
How can I corectly get the number of days for last and current year ?
func numOfDaysInYear() {
let calendar = NSCalendar.currentCalendar()
var interval = NSTimeInterval(0)
var startOfYear: NSDate?
var endOfYear: NSDate!
calendar.rangeOfUnit(.Year, startDate: &startOfYear, interval: &interval, forDate: NSDate())
endOfYear = startOfYear?.dateByAddingTimeInterval(interval - 1)
let comp = calendar.components(.Day, fromDate: startOfYear!, toDate: endOfYear, options: .MatchFirst)
print(comp.day)
}

endOfYear = startOfYear?.dateByAddingTimeInterval(interval - 1)
gives a date within the current year, and its difference to
startOfYear is 365 days, 23 hours and 59 seconds, and therefore the result is 365.
What you want is
let startOfNextYear = startOfYear!.dateByAddingTimeInterval(interval)
let comp = calendar.components(.Day, fromDate: startOfYear!, toDate: startOfNextYear, options: .MatchFirst)
print(comp.day)
which gives the correct output 366 for the leap year 2016.
To get results for the previous year, just replace the current data
NSDate() by a date "one year ago":
calendar.dateByAddingUnit(.Year, value: -1, toDate: NSDate(), options: [])

Alternative approach
let calendar = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.day = 1
components.month = 1
let nextFirstJanuar = calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)!
let thisFirstJanuar = calendar.nextDateAfterDate(nextFirstJanuar, matchingComponents: components, options: [.MatchNextTime, .SearchBackwards])!
let lastFirstJanuar = calendar.nextDateAfterDate(thisFirstJanuar, matchingComponents: components, options: [.MatchNextTime, .SearchBackwards])!
let daysInThisYear = calendar.components(.Day, fromDate: thisFirstJanuar, toDate: nextFirstJanuar, options: []).day
let daysInLastYear = calendar.components(.Day, fromDate: lastFirstJanuar, toDate: thisFirstJanuar, options: []).day

Related

Strange date result calculating start of next month using NSDateComponents in Swift 2

I have a function:
internal func startOfNextMonth() -> NSDate? {
guard
let cal: NSCalendar = NSCalendar.currentCalendar(),
let comp: NSDateComponents = NSDateComponents() else { return nil }
comp.month = 1
comp.day = 0
comp.to12pm()
let date = cal.dateByAddingComponents(comp, toDate: self.startOfMonth()!, options: [])!
return date
}
This should calculate the first day of next month, however it's returning 2016-03-02 08:00:00 UTC.
Is this something to do with it being a leap year (Feb. 29 messing it up?)
Here's my startOfMonth function for reference, and the to12pm() extension:
internal func startOfMonth() -> NSDate? {
guard
let cal: NSCalendar = NSCalendar.autoupdatingCurrentCalendar(),
let comp: NSDateComponents = cal.components([.Year, .Month], fromDate: self.at12pm()!) else { return nil }
comp.to12pm()
return cal.dateFromComponents(comp)!
}
internal extension NSDateComponents {
func to12pm() {
self.hour = 12
self.minute = 0
self.second = 0
}
}
internal func at12pm() -> NSDate? {
let cal = NSCalendar.currentCalendar()
return cal.dateBySettingHour(12, minute: 0, second: 0, ofDate: self, options: [])
}
startOfMonth() returns first of next month at 12 pm (12:00) then you add another 12 hours in startOfNextMonth(), this results one day ahead.
NSCalendar has a smart method to calculate the start of next month regardless of daylight saving changes or other irregularity
let calendar = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.day = 1
let startOfNextMonth = calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchNextTime)
If you just want to calculate the first day of the next month, all you have to do is:
let calendar = NSCalendar.autoupdatingCurrentCalendar()
let todayComponents = calendar.components([.Year, .Month], fromDate: NSDate())
todayComponents.month += 1
let firstDayOfMonth = calendar.dateFromComponents(todayComponents)
Test:
print(firstDayOfMonth)
Prints in the console:
Optional(2016-03-01 08:00:00 +0000)
Also, if it is December, the date will overflow to January of the next year.

Date that is one year from another date

I'm trying to calculate the date that is a year from the current date:
let calendar = NSCalendar.currentCalendar()
let currDate = NSDate()
let newDate = calendar.dateBySettingUnit(.Year, value: 2017, ofDate: currDate, options: NSCalendarOptions(rawValue: 0))!
But newDate is always January 1, 2017.
You got it almost
let calendar = NSCalendar.currentCalendar()
let currDate = NSDate()
let newDate = calendar.dateByAddingUnit(.Year, value: 1, toDate: currDate, options: [])

How to calculate the number of days in the current month/year

Is there any methods available for NSDate/NSCalendar to calculate the number of days in the current month and current year ?
The only thing I saw is to get the number of days from a given date or between two dates.
Is this the only way ?
Here is a method which works for both months and years:
let calendar = NSCalendar.currentCalendar()
let date = NSDate()
// Calculate start and end of the current year (or month with `.Month`):
var startOfInterval : NSDate?
var lengthOfInterval = NSTimeInterval()
calendar.rangeOfUnit(.Year, startDate: &startOfInterval, interval: &lengthOfInterval, forDate: date)
let endOfInterval = startOfInterval!.dateByAddingTimeInterval(lengthOfInterval)
// Compute difference in days:
let days = calendar.components(.Day, fromDate: startOfInterval!, toDate: endOfInterval, options: [])
print(days)
(You may want to add some error checking instead of forcibly unwrapping
optionals.)
Update for Swift 3:
let calendar = Calendar.current
let date = Date()
// Calculate start and end of the current year (or month with `.month`):
let interval = calendar.dateInterval(of: .year, for: date)!
// Compute difference in days:
let days = calendar.dateComponents([.day], from: interval.start, to: interval.end).day!
print(days)
let date = NSDate()
let cal = NSCalendar(calendarIdentifier:NSCalendarIdentifierGregorian)!
let days = cal.rangeOfUnit(.Day, inUnit: .Month, forDate: date)
print("\(days) in the month of \(date)")

calculating duration between NSDate swift 2

In notifications, you see activities that shows "posted 5 minutes ago",
That is what I'm trying to achieve here. The functions below are functioning properly.
func daysBetweenDate(startDate: NSDate, endDate: NSDate) -> Int
{
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day], fromDate: startDate, toDate: endDate, options: [])
// print(components.second)
return components.day
}
daysBetweenDate(postedDate, endDate: NSDate())
func hoursBetweenDate(startDate: NSDate, endDate: NSDate) -> Int
{
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Hour], fromDate: startDate, toDate: endDate, options: [])
// print(components.second)
return components.hour
}
hoursBetweenDate(postedDate, endDate: NSDate())
How do I recognize if the postedDate already second, minute, hour, or day old. And return whichever is necessary?
if the duration between the two NSDates are over a minute I want to output "34minutes ago" and when it is over an hour, "2 hours ago" and so on. How can I do that conditionally.
Give two dates, you can do this quite nicely with NSDateComponentsFormatter:
let form = NSDateComponentsFormatter()
form.maximumUnitCount = 1
form.unitsStyle = .SpellOut // or .Full, whatever you prefer
let s = form.stringFromTimeInterval(
d1.timeIntervalSinceReferenceDate - d2.timeIntervalSinceReferenceDate)

Days between 2 NSDates in a calendar year swift

I'm building a birthday reminder app. I want the user to be able to see how many days it is until somebody's next birthday.
Let's say I have an NSDate() = 2015-06-30 07:21:47 +0000
And the NSDate for the birthday = 1985-08-29 12:00:00 +0000
How would I get the number of days until the next birthday? I've used something like this which gives a negative number of days since the date's actual beginning (which are in the thousands with birthdates). Then with that I would add 365 to the numerical difference until it was positive but it still returns a wonky number. I'm assuming due to leap years or something.
Is there a method to this I can implement? Somehow equalize the year components so that it's always comparing from the next birthday and not the original birthdate?
Edit:
Here is the function I am using:
func daysBetween(date1: NSDate, date2: NSDate) -> Int {
var calendar: NSCalendar = NSCalendar.currentCalendar()
let date1 = calendar.startOfDayForDate(date1)
let date2 = calendar.startOfDayForDate(date2)
let flags = NSCalendarUnit.CalendarUnitDay
let components = calendar.components(flags, fromDate: date1, toDate: date2, options: nil)
return components.day
}
With the example dates I posted I would use it like this:
// sampleDate = 1985-08-29 12:00:00 +0000
daysBetween(sampleDate, date2: NSDate())
// --> 10897
Whether positive or negative, the number is in the thousands. I'm looking to equalize that into number of days to the next calendar birthday.
What you need is to compute the next occurrence of the (day and month
component of the) birthday after today:
let cal = NSCalendar.currentCalendar()
let today = cal.startOfDayForDate(NSDate())
let dayAndMonth = cal.components(.CalendarUnitDay | .CalendarUnitMonth,
fromDate: birthday)
let nextBirthDay = cal.nextDateAfterDate(today,
matchingComponents: dayAndMonth,
options: .MatchNextTimePreservingSmallerUnits)!
Remarks:
The purpose of the MatchNextTimePreservingSmallerUnits option is
that if the birthday is on February 29 (in the Gregorian calendar), its next occurrence will be
computed as March 1 if the year is not a leap year.
You might need to check first if the birthday is today, as it seems
that nextDateAfterDate() would return the next birthday in that case.
Then you can compute the difference in days as usual:
let diff = cal.components(.CalendarUnitDay,
fromDate: today,
toDate: nextBirthDay,
options: nil)
println(diff.day)
Update for Swift 2.2 (Xcode 7.3):
let cal = NSCalendar.currentCalendar()
let today = cal.startOfDayForDate(NSDate())
let dayAndMonth = cal.components([.Day, .Month],
fromDate: birthday)
let nextBirthDay = cal.nextDateAfterDate(today,
matchingComponents: dayAndMonth,
options: .MatchNextTimePreservingSmallerUnits)!
let diff = cal.components(.Day,
fromDate: today,
toDate: nextBirthDay,
options: [])
print(diff.day)
Update for Swift 3 (Xcode 8 GM):
let cal = Calendar.current
let today = cal.startOfDay(for: Date())
let dayAndMonth = cal.dateComponents([.day, .month], from: birthday)
let nextBirthDay = cal.nextDate(after: today, matching: dayAndMonth,
matchingPolicy: .nextTimePreservingSmallerComponents)!
let diff = cal.dateComponents([.day], from: today, to: nextBirthDay)
print(diff.day!)
Btw, in case anyone needs here is the #Aaron function in Swift 3:
func daysBetween(date1: Date, date2: Date) -> Int {
let calendar = Calendar.current
let date1 = calendar.startOfDay(for: date1)
let date2 = calendar.startOfDay(for: date2)
let components = calendar.dateComponents([Calendar.Component.day], from: date1, to: date2)
return components.day ?? 0
}

Resources