How to Use Nscalendar Range Function Within Calendar

How can I use NSCalendar range function within Calendar?

The equivalent of range(of:start:interval:for:) in Calendar is dateInterval(of:start:interval:for:)

Don't use NSDate in Swift

extension Calendar {
/**
Returns a tuple containing the start and end dates for the week that the
specified date falls in.
*/
func weekDatesForDate(date: Date) -> (start: Date, end: Date) {
var interval: TimeInterval = 0
var start = Date()
dateInterval(of: .weekOfYear, start: &start, interval: &interval, for: date)
let end = start.addingTimeInterval(interval)

return (start, end)
}
}

I recommend to use dedicated DateInterval as return value rather than a tuple:

extension Calendar {
/**
Returns a tuple containing the start and end dates for the week that the
specified date falls in.
*/
func weekDatesForDate(date: Date) -> DateInterval {
var interval: TimeInterval = 0
var start = Date()
dateInterval(of: .weekOfYear, start: &start, interval: &interval, for: date)
let end = start.addingTimeInterval(interval)
return DateInterval(start: start, end: end)
}
}

Swift 3 Calendar.range - migrate function

See for example this answer How to get the 'n' weekday of a Date on how to use rangeOfUnit in Swift 3.

However, there is an easier method to get the start of the day
for a date which translates directly to Swift 3:

extension Date {
func numberOfDaysUntilDateTime(toDateTime: Date, calendar: Calendar) -> Int {
let fromDate = calendar.startOfDay(for: self)
let toDate = calendar.startOfDay(for: toDateTime)
let difference = calendar.dateComponents([.day], from: fromDate, to: toDate)
return difference.day!
}
}

how to check if time is within a specific range in swift

There are lots of ways to do this. Personally, I don't like working with strings if I can avoid it. I'd rather deal with date components.

Below is code that creates dates for 8:00 and 16:30, and then compares the dates to see if the current date/time falls in that range.

It's longer than other people's code, but I think it's worth learning how to do calculations with dates using a Calendar:

EDIT #3:

This answer is from a long time ago. I'll leave the old answer below, but here is the current solution:

@CodenameDuchess' answer uses a system function, date(bySettingHour:minute:second:of:matchingPolicy:repeatedTimePolicy:direction:)

Using that function, the code can be simplified to this:

import UIKit

// The function `Calendar.date(bySettingHour:minute:second)` lets you
// create date objects for a given time in the same day of given date
// For example, 8:00 today

let calendar = Calendar.current
let now = Date()
let eight_today = calendar.date(
bySettingHour: 8,
minute: 0,
second: 0,
of: now)!

let four_thirty_today = calendar.date(
bySettingHour: 16,
minute: 30,
second: 0,
of: now)!

// In recent versions of Swift Date objectst are comparable, so you can
// do greater than, less than, or equal to comparisons on dates without
// needing a date extension

if now >= eight_today &&
now <= four_thirty_today
{
print("The time is between 8:00 and 16:30")
}

The old (Swift 2) answer follows, for historical completeness:

This code uses a Calendar object to get the day/month/year of the current date, and adds the desired hour/minute components, and then generates a date for those components.

import UIKit
//-------------------------------------------------------------
//NSDate extensions.
extension NSDate
{
/**
This adds a new method dateAt to NSDate.

It returns a new date at the specified hours and minutes of the receiver

:param: hours: The hours value
:param: minutes: The new minutes

:returns: a new NSDate with the same year/month/day as the receiver, but with the specified hours/minutes values
*/
func dateAt(#hours: Int, minutes: Int) -> NSDate
{
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!

//get the month/day/year componentsfor today's date.

println("Now = \(self)")

let date_components = calendar.components(
NSCalendarUnit.CalendarUnitYear |
NSCalendarUnit.CalendarUnitMonth |
NSCalendarUnit.CalendarUnitDay,
fromDate: self)

//Create an NSDate for 8:00 AM today.
date_components.hour = hours
date_components.minute = minutes
date_components.second = 0

let newDate = calendar.dateFromComponents(date_components)!
return newDate
}
}
//-------------------------------------------------------------
//Tell the system that NSDates can be compared with ==, >, >=, <, and <= operators
extension NSDate: Equatable {}
extension NSDate: Comparable {}

//-------------------------------------------------------------
//Define the global operators for the
//Equatable and Comparable protocols for comparing NSDates

public func ==(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.timeIntervalSince1970 == rhs.timeIntervalSince1970
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.timeIntervalSince1970 < rhs.timeIntervalSince1970
}
public func >(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.timeIntervalSince1970 > rhs.timeIntervalSince1970
}
public func <=(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.timeIntervalSince1970 <= rhs.timeIntervalSince1970
}
public func >=(lhs: NSDate, rhs: NSDate) -> Bool
{
return lhs.timeIntervalSince1970 >= rhs.timeIntervalSince1970
}
//-------------------------------------------------------------

let now = NSDate()
let eight_today = now.dateAt(hours: 8, minutes: 0)
let four_thirty_today = now.dateAt(hours:16, minutes: 30)

if now >= eight_today &&
now <= four_thirty_today
{
println("The time is between 8:00 and 16:30")
}

EDIT:

The code in this answer has changed a LOT for Swift 3.

Instead of using NSDate, it makes more sense to us the native Date object, and Date objects are Equatable and Comparable "out of the box".

Thus we can get rid of the Equatable and Comparable extensions and the definitions for the <, > and = operators.

Then we need to do a fair amount of tweaking of the syntax in the dateAt function to follow Swift 3 syntax. The new extension looks like this in Swift 3:

Swift 3 version:

import Foundation

extension Date
{

func dateAt(hours: Int, minutes: Int) -> Date
{
let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!

//get the month/day/year componentsfor today's date.

var date_components = calendar.components(
[NSCalendar.Unit.year,
NSCalendar.Unit.month,
NSCalendar.Unit.day],
from: self)

//Create an NSDate for the specified time today.
date_components.hour = hours
date_components.minute = minutes
date_components.second = 0

let newDate = calendar.date(from: date_components)!
return newDate
}
}

let now = Date()
let eight_today = now.dateAt(hours: 8, minutes: 0)
let four_thirty_today = now.dateAt(hours: 16, minutes: 30)

if now >= eight_today &&
now <= four_thirty_today
{
print("The time is between 8:00 and 16:30")
}

Get weekdays within a month

With the help of this answer, I was able to accomplish this.

let calendar = NSCalendar.currentCalendar()
let normalizedStartDate = calendar.startOfDayForDate(NSDate().startOfMonth)
let normalizedEndDate = calendar.startOfDayForDate(NSDate().endOfMonth)

var dates = [normalizedStartDate]
var currentDate = normalizedStartDate
repeat {
currentDate = calendar.dateByAddingUnit(NSCalendarUnit.Day, value: 1, toDate: currentDate, options: .MatchNextTime)!
dates.append(currentDate)
} while !calendar.isDate(currentDate, inSameDayAsDate: normalizedEndDate)

let weekdays = dates.filter { !calendar.isDateInWeekend($0) }
weekdays.forEach { date in
print(NSDateFormatter.localizedStringFromDate(date, dateStyle: .FullStyle, timeStyle: .NoStyle))
}

And it works!

Monday, February 1, 2016
Tuesday, February 2, 2016
Wednesday, February 3, 2016
Thursday, February 4, 2016
Friday, February 5, 2016
Monday, February 8, 2016
Tuesday, February 9, 2016
Wednesday, February 10, 2016
Thursday, February 11, 2016
Friday, February 12, 2016
Monday, February 15, 2016
Tuesday, February 16, 2016
Wednesday, February 17, 2016
Thursday, February 18, 2016
Friday, February 19, 2016
Monday, February 22, 2016
Tuesday, February 23, 2016
Wednesday, February 24, 2016
Thursday, February 25, 2016
Friday, February 26, 2016
Monday, February 29, 2016

Objective-C: Getting the number of days in a calendar year

This calculates the number of days of a year of a given date:

NSDate *someDate = [NSDate date];

NSDate *beginningOfYear;
NSTimeInterval lengthOfYear;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian rangeOfUnit:NSYearCalendarUnit
startDate:&beginningOfYear
interval:&lengthOfYear
forDate:someDate];
NSDate *nextYear = [beginningOfYear dateByAddingTimeInterval:lengthOfYear];
NSInteger startDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSEraCalendarUnit
forDate:beginningOfYear];
NSInteger endDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSEraCalendarUnit
forDate:nextYear];
NSInteger daysInYear = endDay - startDay;

Sad caveat: This does not work correctly for year 1582.

The year 1582, the year when Gregor introduced the currently widespread used calendar, needed a fix to align solar with calendar years. So they went with the pragmatic solution: They just dropped October 5-14. (They were not crazy enough to change weekdays, too). As a result the year 1582 only has 355 days.

Addendum: The code above only works correctly for years after 1582. It returns 365 days for the year 1500, for example, even though this year was a leap year in the then used julian calendar. The gregorian calendar starts at October 15, 1582. Computations made on the gregorian calendar are just not defined before that date. So in this way Apple's implementation is correct. I'm not aware of a correct implementation for years before 1583 on Cocoa.

All dates between two Date objects (Swift)

Just add one day unit to the date until it reaches
the current date (Swift 2 code):

var date = startDateNSDate // first date
let endDate = NSDate() // last date

// Formatter for printing the date, adjust it according to your needs:
let fmt = NSDateFormatter()
fmt.dateFormat = "dd/MM/yyyy"

// While date <= endDate ...
while date.compare(endDate) != .OrderedDescending {
print(fmt.stringFromDate(date))
// Advance by one day:
date = calendar.dateByAddingUnit(.Day, value: 1, toDate: date, options: [])!
}

Update for Swift 3:

var date = startDate // first date
let endDate = Date() // last date

// Formatter for printing the date, adjust it according to your needs:
let fmt = DateFormatter()
fmt.dateFormat = "dd/MM/yyyy"

while date <= endDate {
print(fmt.string(from: date))
date = Calendar.current.date(byAdding: .day, value: 1, to: date)!
}

Get all days of any month with objective-c

Carl's answer works on Mac. The following works on Mac or iPhone (no dateWithNaturalLanguageString: available there).

NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];

// Set your year and month here
[components setYear:2015];
[components setMonth:1];

NSDate *date = [calendar dateFromComponents:components];
NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];

NSLog(@"%d", (int)range.length);

How do I calculate the number of days in this year in Objective C

I finally came up with a solution that works. What I do is first calculate the number of months in the year and then for each month calculate the number of days for that month.

The code looks like this:

NSUInteger days = 0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *today = [NSDate date];
NSDateComponents *components = [calendar components:NSYearCalendarUnit fromDate:today];
NSUInteger months = [calendar rangeOfUnit:NSMonthCalendarUnit
inUnit:NSYearCalendarUnit
forDate:today].length;
for (int i = 1; i <= months; i++) {
components.month = i;
NSDate *month = [calendar dateFromComponents:components];
days += [calendar rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:month].length;
}

return days;

It is not as neat as I would have hoped for but it will work for any calendar such as the ordinary gregorian one or the islamic one.



Related Topics



Leave a reply



Submit