UIDatePicker restrict hours but not date - ios

I came across a situation where I needed to restrict a UIDatePicker's selected hour, but still allow free selection of the day. This would be useful if you wanted to allow a user to select a date/time during set business hours. I found something that was close to what I was wanting to do by alerting the user that their selection was bad, but didn't actually change the date on the picker, so I wanted to share my solution Q&A-style.

This particular example will not allow selection of times before 7:00am or after 9:59pm. Selection of an "invalid" time will immediately slide the UIDatePicker back to the closest valid time on the respective end of the spectrum (for example, selection of 10:02pm will immediately slide back to 9:59pm)
- (void)datePickerChanged
{
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:datePicker.date];
if([components hour] < 7)
{
[components setHour:7];
[components setMinute:0];
[datePicker setDate:[[NSCalendar currentCalendar] dateFromComponents:components]];
}
else if([components hour] > 21)
{
[components setHour:21];
[components setMinute:59];
[datePicker setDate:[[NSCalendar currentCalendar] dateFromComponents:components]];
}
}
Edit: As #DuncanC suggested in the comments, feedback to the user should probably be included, such as a label saying "Only times between 7:00am and 9:59pm can be used"

In Swift2.0:
func datePickerChanged() {
let components = NSCalendar.currentCalendar().components(
[NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.WeekOfYear, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second, NSCalendarUnit.Weekday, NSCalendarUnit.WeekdayOrdinal, NSCalendarUnit.WeekOfYear],
fromDate: datePickerInstance.date)
if components.hour < 7 {
components.hour = 7
components.minute = 0
datePickerInstance.setDate(NSCalendar.currentCalendar().dateFromComponents(components)!, animated: true)
}
else if components.hour > 21 {
components.hour = 21
components.minute = 59
datePickerInstance.setDate(NSCalendar.currentCalendar().dateFromComponents(components)!, animated: true)
}
else {
print("Everything is good.")
}
}

If you want to actually limit the hours that are displayed, I built a custom picker for that purpose. It's a subclass of UIPickerView and it replicates the functionality of UIDatePicker in countDownTimer mode, while adding support to set maxTimeInterval.
You use it like this:
GSTimeIntervalPicker *picker = [[GSTimeIntervalPicker alloc] init];
picker.maxTimeInterval = (3600 * 3); // set the limit
picker.minuteInterval = 5; // the step. Default is 1 min.
picker.timeInterval = (3600 * 1.5); // 1 h 30 minutes
picker.onTimeIntervalChanged = ^(NSTimeInterval newTimeInterval) {
// Use the value
};
Available on GitHub under MIT license. Blog post here.

In Swift 4:
func datePickerChanged() {
var components = Calendar.current.dateComponents([.hour, .minute, .month, .year, .day], from: datePicker.date)
if components.hour! < 7 {
components.hour = 7
components.minute = 0
datePicker.setDate(Calendar.current.date(from: components)!, animated: true)
}
else if components.hour! > 21 {
components.hour = 21
components.minute = 59
datePicker.setDate(Calendar.current.date(from: components)!, animated: true)
}
else {
print("Everything is fine!")
}
}

Related

How to get all days in a month that are a specific weekday?

How do I get the actual date of the month based on a given a day ? For example, I would like to retrieve all the dates in June 2017 which are Saturday. How can I achieve that ? Sample code will be very much appreciated as I have struggled for days on this.
A DateComponents has a weekday property, representing the day of the week. The weekdays are (in Foundation's Gregorian calendar) numbered 1 for Sunday, 2 for Monday, …, and 7 for Saturday.
A DateComponents also has a weekdayOrdinal property, representing “the position of the weekday within the next larger calendar unit, such as the month. For example, 2 is the weekday ordinal unit for the second Friday of the month.”
So let's initialize a DateComponents for some Saturday in June 2017. It's generally a good idea to specify a time of noon if you don't care about the time, because midnight (the default time of day) can cause problems in some time zones on some days.
var components = DateComponents(era: 1, year: 2017, month: 06, hour: 12, weekday: 7)
And let's make a calendar.
var calendar = Calendar.autoupdatingCurrent
Now we can loop over all the possible weekday ordinals. For each, we'll ask the calendar to generate a date. Then we ask the calendar to convert the date back to year, month, and day components.
In the Gregorian calendar, some months have 5 Saturdays, but most have 4. So when we ask for the 5th Saturday, we'll probably get a date in the following month. When that happens, we want to suppress that date.
for i in 1 ... 5 {
components.weekdayOrdinal = i
let date = calendar.date(from: components)!
let ymd = calendar.dateComponents([.year, .month, .day], from: date)
guard ymd.month == components.month else { break }
print("\(ymd.year!)-\(ymd.month!)-\(ymd.day!)")
}
Output:
2017-6-3
2017-6-10
2017-6-17
2017-6-24
Objective-C version:
NSDateComponents *components = [NSDateComponents new];
components.era = 1;
components.year = 2017;
components.month = 6;
components.hour = 12;
components.weekday = 7;
NSCalendar *calendar = NSCalendar.autoupdatingCurrentCalendar;
for (NSInteger i = 1; i <= 5; ++i) {
components.weekdayOrdinal = i;
NSDate *date = [calendar dateFromComponents:components];
NSDateComponents *ymd = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date];
if (ymd.month != components.month) { break; }
NSLog(#"%ld-%ld-%ld", (long)ymd.year, (long)ymd.month, (long)ymd.day);
}
This is another solution for your problem using calendar method called enumerateDates and using a Date extension
//month in MM format, year in yyyy format and dayNumber as Int 1 for sunday, 7 for saturday
func datesWith(dayNumber:Int,month:String,year:String) -> [Date]
{
assert(dayNumber >= 1 && dayNumber <= 7, "Day number is wrong")
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.date(from: year + "-" + month + "-" + "01")
guard date != nil else {
return []
}
var resultDates : [Date] = []
//check if firstDay of month is desired weekday
if(Calendar.current.component(.weekday, from: date!) == dayNumber)
{
resultDates.append(date!)
}
Calendar.current.enumerateDates(startingAfter: date!, matching: DateComponents(weekday: dayNumber), matchingPolicy: Calendar.MatchingPolicy.nextTimePreservingSmallerComponents) { (currentDate, result, stop) in
if(currentDate! > date!.endOfMonth())
{
stop = true
return
}
resultDates.append(currentDate!)
}
return resultDates
}
Extension
extension Date {
func startOfMonth() -> Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))!
}
func endOfMonth() -> Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
}
}
Using it
override func viewDidLoad() {
super.viewDidLoad()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
// Do any additional setup after loading the view, typically from a nib.
let datesArray = self.datesWith(dayNumber: 5, month: "06", year: "2017")
for currDate in datesArray {
debugPrint(dateFormatter.string(from: currDate))
}
}
Output
"2017-06-01"
"2017-06-08"
"2017-06-15"
"2017-06-22"
"2017-06-29"
Hope this helps

How to trigger local notification fortnightly starting from a specific future date

I am trying to trigger UNUserNotification fortnightly which will start from a specific future day. I have triggered successfully with UNTimeIntervalNotificationTrigger. But my problem is I cannot set a specific start date here.
UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:(14*24*3600) repeats: YES];
request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
I've tried with WeekdayOrdinal for UNCalendarNotificationTrigger but that does not work exactly with fortnight duration always.
Is there any way I can schedule local UNUserNotification fortnightly from a specific day in future?
UNNotificationRequest does not provide anything to trigger fortnight recurring.
With UNCalendarNotificationTrigger you can only trigger following types of recurring:
weekly, daily, monthly, yearly
switch repeatType {
case .daily:
newComponents.hour = components.hour
newComponents.minute = components.minute
break
case .weekly:
newComponents.hour = components.hour
newComponents.minute = components.minute
newComponents.weekday = components.weekday
break
case .monthly:
newComponents.hour = components.hour
newComponents.minute = components.minute
newComponents.day = components.day
break
case .none:
newComponents.hour = components.hour
newComponents.minute = components.minute
newComponents.day = components.day
newComponents.month = components.month
newComponents.year = components.year
break
case .annually:
newComponents.hour = components.hour
newComponents.minute = components.minute
newComponents.day = components.day
newComponents.month = components.month
break
}
Although you can easily use EventKit to create events and alarms within those events with all such recurring options you want like the fortnight, quarterly, half-yearly etc.
I have created this code to create events with multiple recurring types:
let eventStore = EKEventStore()
eventStore.requestAccess(to: .event, completion: { (granted, error) in
if (granted) && (error == nil) {
let event = EKEvent(eventStore: eventStore)
event.title = "Countdown title"
event.startDate = Date()
event.notes = "Countdown is complete!"
event.isAllDay = false
event.calendar = eventStore.defaultCalendarForNewEvents
var frequency : EKRecurrenceFrequency = EKRecurrenceFrequency.daily
var interval = 1
switch repeatType {
case .daily:
//Repeat every day
frequency = EKRecurrenceFrequency.daily
interval = 1
break
case .weekly:
//Repeat every week
frequency = EKRecurrenceFrequency.weekly
interval = 1
break
case .biweekly:
//Repeat every 2 weeks
frequency = EKRecurrenceFrequency.weekly
interval = 2
break
case .monthly:
//Repeat every month
frequency = EKRecurrenceFrequency.monthly
interval = 1
break
case .quarterly:
//Repeat every 3 months
frequency = EKRecurrenceFrequency.monthly
interval = 3
break
case .halfYearly:
//Repeat every 6 months
frequency = EKRecurrenceFrequency.monthly
interval = 6
break
default:
// Repeat every year
frequency = EKRecurrenceFrequency.yearly
interval = 1
}
let alarm : EKAlarm = EKAlarm(relativeOffset: TimeInterval(exactly: 0)!)
event.addAlarm(alarm)
if interval > 0 {
event.addRecurrenceRule(EKRecurrenceRule(recurrenceWith: frequency, interval: interval, end: nil))
}
do {
try eventStore.save(event, span: .thisEvent)
} catch let error as NSError {
print(error.localizedDescription)
return
}
} else {
print(error?.localizedDescription ?? "no error")
}
})
Please don't forget to import EventKit.
Try with triggerWithDateMatchingComponents method
Objective-C Example Code
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:86400]; // You can set your date here.
NSDateComponents *triggerDate = [[NSCalendar currentCalendar]
components:NSCalendarUnitYear +
NSCalendarUnitMonth + NSCalendarUnitDay +
NSCalendarUnitHour + NSCalendarUnitMinute +
NSCalendarUnitSecond fromDate:date];
UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDate repeats:NO];
Swift Example
let triggerDate = Calendar.current.date(byAdding: .day, value: 1, to: self)!
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)

while selecting sunday date it select date of next week [duplicate]

I want to get the current week start and end date and I also want to use the previous
week start and end date and next week of the start and end date in current month.
Thanks in Advance.
rangeOfUnit:startDate:interval:forDate:. It gives you the start and the interval for a certain time unit. With it it is easy to find the start of the week in the used calendar and add the range-1 to get the latest second in that week.
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfTheWeek
interval:&interval
forDate:now];
//startOfWeek holds now the first day of the week, according to locale (monday vs. sunday)
endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval-1];
// holds 23:59:59 of last day in week.
I solve the problem thanks for Support
Code :- it give the current week start and end date.
NSDate *today = [NSDate date];
NSLog(#"Today date is %#",today);
dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd"];// you can use your format.
//Week Start Date
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int dayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[components setDay:([components day] - ((dayofweek) - 2))];// for beginning of the week.
NSDate *beginningOfWeek = [gregorian dateFromComponents:components];
NSDateFormatter *dateFormat_first = [[NSDateFormatter alloc] init];
[dateFormat_first setDateFormat:#"yyyy-MM-dd"];
dateString2Prev = [dateFormat stringFromDate:beginningOfWeek];
weekstartPrev = [[dateFormat_first dateFromString:dateString2Prev] retain];
NSLog(#"%#",weekstartPrev);
//Week End Date
NSCalendar *gregorianEnd = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *componentsEnd = [gregorianEnd components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int Enddayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[componentsEnd setDay:([componentsEnd day]+(7-Enddayofweek)+1)];// for end day of the week
NSDate *EndOfWeek = [gregorianEnd dateFromComponents:componentsEnd];
NSDateFormatter *dateFormat_End = [[NSDateFormatter alloc] init];
[dateFormat_End setDateFormat:#"yyyy-MM-dd"];
dateEndPrev = [dateFormat stringFromDate:EndOfWeek];
weekEndPrev = [[dateFormat_End dateFromString:dateEndPrev] retain];
NSLog(#"%#",weekEndPrev);
Here is Swift 3 Version:-
extension Date {
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}
var endOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 7, to: sunday)
}
}
You can get the week start date and end date like this :
let startWeek = Date().startOfWeek
let endWeek = Date().endOfWeek
print(startWeek ?? "not found start date")
print(endWeek ?? "not found end date")
A Swift version
of vikingosegundo's answer:
let calendar = NSCalendar.currentCalendar()
var startOfTheWeek: NSDate?
var endOfWeek: NSDate!
var interval = NSTimeInterval(0)
calendar.rangeOfUnit(.WeekOfMonth, startDate: &startOfTheWeek, interval: &interval, forDate: NSDate())
endOfWeek = startOfTheWeek!.dateByAddingTimeInterval(interval - 1)
Here's an elegant way for Swift 3 (Xcode 8+):
extension Date {
var startOfWeek: Date {
let date = Calendar.current.date(from: Calendar.current.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))!
let dslTimeOffset = NSTimeZone.local.daylightSavingTimeOffset(for: date)
return date.addingTimeInterval(dslTimeOffset)
}
var endOfWeek: Date {
return Calendar.current.date(byAdding: .second, value: 604799, to: self.startOfWeek)!
}
}
And we can use this extension like this:
print(Date().startOfWeek)
print(Date().endOfWeek)
Swift 4 Solution
I have figured out according to my requirement, where I have find out dates for following.
1. Today
2. Tomorrow
3. This Week
4. This Weekend
5. Next Week
6. Next Weekend
So, I have created Date Extension to get Dates of Current Week and Next Week.
CODE
extension Date {
func getWeekDates() -> (thisWeek:[Date],nextWeek:[Date]) {
var tuple: (thisWeek:[Date],nextWeek:[Date])
var arrThisWeek: [Date] = []
for i in 0..<7 {
arrThisWeek.append(Calendar.current.date(byAdding: .day, value: i, to: startOfWeek)!)
}
var arrNextWeek: [Date] = []
for i in 1...7 {
arrNextWeek.append(Calendar.current.date(byAdding: .day, value: i, to: arrThisWeek.last!)!)
}
tuple = (thisWeek: arrThisWeek,nextWeek: arrNextWeek)
return tuple
}
var tomorrow: Date {
return Calendar.current.date(byAdding: .day, value: 1, to: noon)!
}
var noon: Date {
return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
var startOfWeek: Date {
let gregorian = Calendar(identifier: .gregorian)
let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
return gregorian.date(byAdding: .day, value: 1, to: sunday!)!
}
func toDate(format: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = format
return formatter.string(from: self)
}
}
USAGE:
let arrWeekDates = Date().getWeekDates() // Get dates of Current and Next week.
let dateFormat = "MMM dd" // Date format
let thisMon = arrWeekDates.thisWeek.first!.toDate(format: dateFormat)
let thisSat = arrWeekDates.thisWeek[arrWeekDates.thisWeek.count - 2].toDate(format: dateFormat)
let thisSun = arrWeekDates.thisWeek[arrWeekDates.thisWeek.count - 1].toDate(format: dateFormat)
let nextMon = arrWeekDates.nextWeek.first!.toDate(format: dateFormat)
let nextSat = arrWeekDates.nextWeek[arrWeekDates.nextWeek.count - 2].toDate(format: dateFormat)
let nextSun = arrWeekDates.nextWeek[arrWeekDates.nextWeek.count - 1].toDate(format: dateFormat)
print("Today: \(Date().toDate(format: dateFormat))") // Sep 26
print("Tomorrow: \(Date().tomorrow.toDate(format: dateFormat))") // Sep 27
print("This Week: \(thisMon) - \(thisSun)") // Sep 24 - Sep 30
print("This Weekend: \(thisSat) - \(thisSun)") // Sep 29 - Sep 30
print("Next Week: \(nextMon) - \(nextSun)") // Oct 01 - Oct 07
print("Next Weekend: \(nextSat) - \(nextSun)") // Oct 06 - Oct 07
You can modify Extension according to your need.
Thanks!
Solution for Swift 5.x
Create Date extension:
func weekPeriod() -> (startDate: Date, endDate: Date) {
let calendar = Calendar(identifier: .gregorian)
guard let sundayDate = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return (startDate: Date(), endDate: Date()) }
if calendar.isDateInToday(sundayDate) {
let startDate = calendar.date(byAdding: .day, value: -6, to: sundayDate) ?? Date()
let endDate = sundayDate
return (startDate: startDate, endDate: endDate)
}
let startDate = calendar.date(byAdding: .day, value: 1, to: sundayDate) ?? Date()
let endDate = calendar.date(byAdding: .day, value: 6, to: startDate) ?? Date()
return (startDate: startDate, endDate: endDate)
}
First find the current date...
NSDate *today = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:today];
Calcuate number of days to substract from today, in order to get the first day of the week. In this case, the first day of the week is monday. This is represented by first subtracting 0 with the weekday integer followed by adding 2 to the setDay.
Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4, Thursday = 5, Friday = 6 and Saturday = 7. By adding more to this integers, you will go into the next week.
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: (0 - [weekdayComponents weekday]) + 2];
[componentsToSubtract setHour: 0 - [weekdayComponents hour]];
[componentsToSubtract setMinute: 0 - [weekdayComponents minute]];
[componentsToSubtract setSecond: 0 - [weekdayComponents second]];
Create date for first day in week
NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:today options:0];
By adding 6 to the date of the first day, we can get the last day, in our example Sunday.
NSDateComponents *componentsToAdd = [gregorian components:NSDayCalendarUnit fromDate:beginningOfWeek];
[componentsToAdd setDay:6];
NSDate *endOfWeek = [gregorian dateByAddingComponents:componentsToAdd toDate:beginningOfWeek options:0];
for next and previous ....
-(IBAction)Week_CalendarActionEvents:(id)sender{
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *offsetComponents = [[[NSDateComponents alloc] init] autorelease];
NSDate *nextDate;
if(sender==Week_prevBarBtn) // Previous button events
[offsetComponents setDay:-7];
else if(sender==Week_nextBarBtn) // next button events
[offsetComponents setDay:7];
nextDate = [gregorian dateByAddingComponents:offsetComponents toDate:selectedDate options:0];
selectedDate = nextDate;
[selectedDate retain];
NSDateComponents *components = [gregorian components:NSWeekCalendarUnit fromDate:selectedDate];
NSInteger week = [components week];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMMM YYYY"];
NSString *stringFromDate = [formatter stringFromDate:selectedDate];
[formatter release];
[Week_weekBarBtn setTitle:[NSString stringWithFormat:#"%#,Week %d",stringFromDate,week]];
}
Here's some code and it also checks an edge case where the beginning of the week starts in the prior month. You can get end of week by setting setWeekday to 7 and you can get the prior week by subtracting 1 from [components week]
// Finds the date for the first day of the week
- (NSDate *)getFirstDayOfTheWeekFromDate:(NSDate *)givenDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
// Edge case where beginning of week starts in the prior month
NSDateComponents *edgeCase = [[NSDateComponents alloc] init];
[edgeCase setMonth:2];
[edgeCase setDay:1];
[edgeCase setYear:2013];
NSDate *edgeCaseDate = [calendar dateFromComponents:edgeCase];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:edgeCaseDate];
[components setWeekday:1]; // 1 == Sunday, 7 == Saturday
[components setWeek:[components week]];
NSLog(#"Edge case date is %# and beginning of that week is %#", edgeCaseDate , [calendar dateFromComponents:components]);
// Find Sunday for the given date
components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:givenDate];
[components setWeekday:1]; // 1 == Sunday, 7 == Saturday
[components setWeek:[components week]];
NSLog(#"Original date is %# and beginning of week is %#", givenDate , [calendar dateFromComponents:components]);
return [calendar dateFromComponents:components];
}
Here is a solution for the swift4,
we can get all the days for the current week.
var calendar = Calendar(identifier: Calendar.Identifier.gregorian)
let today = calendar.startOfDay(for: Date())
let dayOfWeek = calendar.component(.weekday, from: today) - calendar.firstWeekday
let weekdays = calendar.range(of: .weekday, in: .weekOfYear, for: today)!
let days = (weekdays.lowerBound ..< weekdays.upperBound)
.flatMap { calendar.date(byAdding: .day, value: $0 - dayOfWeek, to: today) }
//Begining of Week Date
- (NSDate*) beginingOfWeekOfDate{
NSCalendar *tmpCalendar = [NSCalendar currentCalendar];
NSDateComponents *components = [tmpCalendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitWeekday fromDate:self];//get the required calendar units
NSInteger weekday = tmpCalendar.firstWeekday;
components.weekday = weekday; //weekday
components.hour = 0;
components.minute = 0;
components.second = 0;
NSDate *fireDate = [tmpCalendar dateFromComponents:components];
return fireDate;
}
//End of Week Date
-(NSDate *)endOfWeekFromDate{
NSCalendar *tmpCalendar = [NSCalendar currentCalendar];
NSDateComponents *components = [tmpCalendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitWeekday fromDate:self];//get the required calendar units
int weekday = 7; //Saturday
if (tmpCalendar.firstWeekday != 1) {
weekday = 1;
}
components.weekday = weekday;//weekday
components.hour = 23;
components.minute = 59;
components.second = 59;
NSDate *fireDate = [tmpCalendar dateFromComponents:components];
return fireDate;
}
You can get current day and date by following code:
NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
[dateFormat setDateFormat:#"EEEE"];
NSString *weekDay = [dateFormat stringFromDate:today];
[dateFormat setDateFormat:#"dd"];
NSString *thedate=[dateFormat stringFromDate:today];
//[dateFormat release];
NSLog(#"%# %#", weekDay,thedate);
Now, You need to put some logic in it to calculate starting date and end date of the week. The logic will be such kind,
if the week day is Monday
then
starting date = current date - 0
end date = current date + 6
and so on
I think you can get the idea of it.
By take advantage of the method rangeOfUnit:startDate:interval:forDate: of NSDate, there is a simpler way to achieve this:
- (void)startDate:(NSDate **)start andEndDate:(NSDate **)end ofWeekOn:(NSDate *)date{
NSDate *startDate = nil;
NSTimeInterval duration = 0;
BOOL b = [[NSCalendar currentCalendar] rangeOfUnit:NSWeekCalendarUnit startDate:&startDate interval:&duration forDate:date];
if(! b){
*start = nil;
*end = nil;
return;
}
NSDate *endDate = [startDate dateByAddingTimeInterval:duration-1];
*start = startDate;
*end = endDate;
}
NSDate *this_start = nil, *this_end = nil;
[self startDate:&this_start andEndDate:&this_end ofWeekOn:[NSDate date]];
So now you have the start date and end date of this week. Then last week:
NSDate *lastWeekDate = [this_start dateByAddingTimeInterval:-10];
NSDate *last_start = nil, *last_end = nil;
[self startDate:&last_start andEndDate:&last_end ofWeekOn:lastWeekDate];
Next week:
NSDate *nextWeekDate = [this_end dateByAddingTimeInterval:10];
NSDate *next_start = nil, *next_end = nil;
[self startDate:&next_start andEndDate:&next_end ofWeekOn:nextWeekDate];
Now you have them all.
Swift 3
First find the current date...
let today = Date()
let gregorian = Calendar(identifier: .gregorian)
var weekdayComponents: DateComponents? = gregorian.dateComponents([.weekday], from: today)
Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4, Thursday = 5, Friday = 6 and Saturday = 7.
var componentsToSubtract = DateComponents()
componentsToSubtract.day = (0 - (weekdayComponents?.weekday!)!) + 2
beginningOfWeek = gregorian.date(byAdding: componentsToSubtract, to: today)
var componentsToAdd: DateComponents? = gregorian.dateComponents([.day], from: beginningOfWeek!)
componentsToAdd?.day = 6
endOfWeek = gregorian.date(byAdding: componentsToAdd!, to: beginningOfWeek!)
let components: DateComponents? = gregorian.dateComponents([.month], from: beginningOfWeek!)
let month: Int? = components?.month
let components1: DateComponents? = gregorian.dateComponents([.month], from: endOfWeek!)
let month1: Int? = components1?.month
print("\(month) - \(month1)")
showDate(start:beginningOfWeek!, end:endOfWeek!, strtMn:month!, endMn:month1!)
func showDate(start:Date, end:Date, strtMn:Int, endMn:Int) {
if strtMn == endMn{
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
let stringFromDate: String = formatter.string(from: start)
let formatter1 = DateFormatter()
formatter1.dateFormat = "dd"
let stringFromDate1: String = formatter1.string(from: end)
print("\(stringFromDate) - \(stringFromDate1)")
lblDate.text = "\(stringFromDate) - \(stringFromDate1)"
}
else{
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
let stringFromDate: String = formatter.string(from: start)
let formatter1 = DateFormatter()
formatter1.dateFormat = "MMM dd"
let stringFromDate1: String = formatter1.string(from: end)
print("\(stringFromDate) - \(stringFromDate1)")
lblDate.text = "\(stringFromDate) - \(stringFromDate1)"
}
}
for next and previous ....
#IBAction func week_CalendarActionEvents(_ sender: UIButton) {
let gregorian = Calendar(identifier: .gregorian)
var offsetComponents = DateComponents()
var nextStrtDate: Date?
var nextEndDate: Date?
var startDate: Date?
var endDate: Date?
startDate = beginningOfWeek
endDate = endOfWeek
if sender.tag == 1 {
offsetComponents.day = -7
}
else if sender.tag == 2 {
offsetComponents.day = 7
}
nextStrtDate = gregorian.date(byAdding: offsetComponents, to:startDate!)
startDate = nextStrtDate
beginningOfWeek = startDate
nextEndDate = gregorian.date(byAdding: offsetComponents, to: endDate!)
endDate = nextEndDate
endOfWeek = endDate
let components: DateComponents? = gregorian.dateComponents([.month], from: startDate!)
let month: Int? = components?.month
let components1: DateComponents? = gregorian.dateComponents([.month], from: endDate!)
let month1: Int? = components1?.month
print("\(month)- \(month1)")
showDate(start:startDate!, end:endDate!, strtMn:month!, endMn:month1!)
}
Swift 3+: Simple solution with extension
extension Date {
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}
var endOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 7, to: sunday)
}
var yesterdayDate: Date? {
return NSCalendar.current.date(byAdding: .day, value: -1, to: noon)!
}
var tommorowDate: Date? {
return NSCalendar.current.date(byAdding: .day, value: 1, to: noon)!
}
var previousDate: Date? {
let oneDay:Double = 60 * 60 * 24
return self.addingTimeInterval(-(Double(oneDay)))
}
var nextDate: Date? {
let oneDay:Double = 60 * 60 * 24
return self.addingTimeInterval(oneDay)
}
var noon: Date {
return NSCalendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
var month: Int {
return Calendar.current.component(.month, from: self)
}
}
Swift 4.2
If you want to show the current week dates, here is the code.
override func viewDidLoad() {
super.viewDidLoad()
for dateIndex in 0..<7 {
guard let startWeek = Date().startOfWeek else { return }
let date = Calendar.current.date(byAdding: .day, value: dateIndex + 1, to: startWeek)
print(date)
}
}
extension Date {
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}
}
In swift 3.0
let cal = NSCalendar.current
//weekday
let weekday = cal.component(.weekday, from: Date())
var dateComp = cal.dateComponents([.hour, .minute, .second, .day, .month, .year], from: Date())
print(dateComp.day!)
//Start Date of the week - Sunday
dateComp.day = dateComp.day! - (weekday - 1)// start date of week
print(cal.date(from: dateComp)!)
//End Date of the Week - Saturday
dateComp = cal.dateComponents([.hour, .minute, .second, .day, .month, .year], from: Date())
dateComp.day = dateComp.day! + (7 - weekday)
print(cal.date(from: dateComp)!)
There are 3 steps to get Start date and End date of week from any region.
Find week day of current day.
func getTodayWeekDay() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let weekDay = dateFormatter.string(from: Date())
return weekDay
}
Make emun class for subtraction and addition of days.
i.e. if current week day is Wednesday then for getting Monday of current week we have to subtract -2 from current day.
And for getting Sunday of current week we have to add 4 into current day.
enum WeekDays: String {
case monday = "Monday"
case tuesday = "Tuesday"
case wednesday = "Wednesday"
case thursday = "Thursday"
case friday = "Friday"
case saturday = "Saturday"
case sunday = "Sunday"
var daysToSubstract: Int {
switch self {
case .monday: return 0
case .tuesday: return -1
case .wednesday: return -2
case .thursday: return -3
case .friday: return -4
case .saturday: return -5
case .sunday: return -6
}
}
var daysToAdd: Int {
switch self {
case .monday: return 6
case .tuesday: return 5
case .wednesday: return 4
case .thursday: return 3
case .friday: return 2
case .saturday: return 1
case .sunday: return 0
}
}
}
Get Start-week and End-week by subtraction and addition from current day.
extension Date {
var startOfWeek: String? {
let gregorian = Calendar(identifier: .gregorian)
let startWeek = dateFormatter.string(from:gregorian.date(byAdding: .day, value: WeekDays(rawValue: getTodayWeekDay())?.daysToSubstract ?? 0, to: self)!)
print("start-week--- \(startWeek)")
return startWeek
}
var endOfWeek: String? {
let gregorian = Calendar(identifier: .gregorian)
let endWeek = dateFormatter.string(from:gregorian.date(byAdding: .day, value: WeekDays(rawValue: getTodayWeekDay())?.daysToAdd ?? 6, to: self)!)
print("end-week--- \(endWeek)")
return endWeek
}
}
You can use functions like below:
Date().startOfWeek
Date().endOfWeek

Iterating over a year to get each days NSDate object in Swift

Hello I have a method that returns an array of times for each day.
prayTimesDate(date: NSDate, latitide : Double, longitude : Double, timeZone : Double) -> NSMutableArray
I need to iterate through a whole year or maybe a date range to get an array of times for each day in a whole year. I found alot of references in ruby and python on how to do this but I couldn't find anything for swift or objective-c. Is there any built in methods in swift that will accomplish this? If not can someone help me out as I am still new in programming. Any input is greatly appreciated.
This is the objective-c code for the method I'm linking to my swift project
- (NSMutableArray *)prayerTimesDate:(NSDate *)date latitude:(double)latitude longitude:(double)longitude andTimezone:(double)timezone
{
unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:unitFlags fromDate:date];
NSInteger year = [components year];
NSInteger month = [components month];
NSInteger day = [components day];
return [self getDatePrayerTimesForYear:year month:month day:day latitude:latitude longitude:longitude andtimeZone:timezone];
}
Assuming your prayerTimesDate: method is already returning the expected result, you can loop through each day of the year while repeatedly call prayerTimesDate: to get an array containing the prayer times for each day, ex:
func yearlyPrayerDatesFromCurrentDate (latitude:Double, longitude:Double, timezone:Double) -> NSMutableArray {
// Set "date" to equal the current day
var date:NSDate! = NSDate()
// Increment "date" by one year to calculate the ending
// date for the loop
let gregorian:NSCalendar! = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let dateComponents = NSDateComponents()
dateComponents.year = 1
let endingDate:NSDate! = gregorian.dateByAddingComponents(dateComponents, toDate: date, options: nil)
// Create an array to hold *all* the returned
// results for the year
var datesArray = NSMutableArray()
// Loop through each date until the ending date is
// reached
while date.compare(endingDate) != NSComparisonResult.OrderedDescending {
// Call your prayerTimesDate: method on the current
// date to get that date's prayer times and add the
// times from the returned array to the datesArray
datesArray.addObjectsFromArray(prayerTimesDate(date, latitude: latitude, longitude: longitude, andTimezone: timezone))
// increment the date by 1 day
let dateComponents = NSDateComponents()
dateComponents.day = 1
date = gregorian.dateByAddingComponents(dateComponents, toDate: date, options: nil)
}
return datesArray
}
Here is another example for a period over 14 days (without NSCalendar):
let ti:NSTimeInterval = 24*60*60 //one day
let dateFrom = NSDate() //Now
let dateTo = dateFrom.dateByAddingTimeInterval(24*60*60*14) //14 Days later
var nextDate = NSDate()
var endDate = dateTo.dateByAddingTimeInterval(ti)
while nextDate.compare(endDate) == NSComparisonResult.OrderedAscending
{
print("nextDate:", nextDate)
nextDate = nextDate.dateByAddingTimeInterval(ti)
}
Create an NSDateComponents instance for 1 day and NSDate objects for each time on the first day. Now you can iterate over the number of days you want (or until you hit then end date) and then you can use dateByAddingComponents:toDate:options: of the calendar to get the new date for each day.
From Apple doc: To compute a sequence of dates, use the enumerateDatesStartingAfterDate:matchingComponents:options:usingBlock: method instead of calling this method ( - nextDateAfterDate:matchingComponents:options: ) in a loop with the previous loop iteration's result.
As I got, it will iterate all dates that matched with "matchingComponents" till you finish iteration with "stop.memory = true"
//: Playground - noun: a place where people can play
import UIKit
let calendar = NSCalendar.currentCalendar()
let startDate = calendar.startOfDayForDate(NSDate())
let finishDate = calendar.dateByAddingUnit(.Day, value: 10, toDate: startDate, options: [])
let dayComponent = NSDateComponents()
dayComponent.hour = 1
calendar.enumerateDatesStartingAfterDate(startDate, matchingComponents: dayComponent, options: [.MatchStrictly]) { (date, exactMatch, stop) in
print(date)
if date!.compare(finishDate!) == NSComparisonResult.OrderedDescending {
// .memory gets at the value of an UnsafeMutablePointer
stop.memory = true
}
}

Getting date from weekday

I'm doing this app where the user inputs some days (Using UITableView, monday - sunday).
I then need the app to figure out which dates this matches with. Say it's the user sits on sunday the 15th and chooses monday and tuesday. The app will figure out the dates are monday 16th and tuesday 17th.
How would one go about that using NSDate and such? I know how to find a weekday using the date, but I want the opposite.
Of course it has to be the closest days, like not finding monday the 23rd, but finding 16th.
Hope that makes sense. :-)
A direct method, without using a loop:
NSUInteger targetWeekday = ...; // 1 = Sunday, 2 = Monday, ...
// Date components for today:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *comp = [cal components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit
fromDate:now];
// Adjust components for target weekday:
if (targetWeekday >= comp.weekday) {
comp.day += (targetWeekday - comp.weekday);
} else {
comp.day += (targetWeekday + 7 - comp.weekday); // Assuming 7 days per week.
}
comp.weekday = targetWeekday;
// And back to NSDate:
NSDate *targetDate = [cal dateFromComponents:comp];
Remark:
if (targetWeekday >= comp.weekday) {
comp.day += (targetWeekday - comp.weekday);
} else {
comp.day += (targetWeekday + 7 - comp.weekday); // Assuming 7 days per week.
}
can be replaced by the shorter, equivalent code
comp.day += (targetWeekday + 7 - comp.weekday) % 7;
You can do it by following a simple procedure:
Start with an NSDate that represents today
Get the day of the week from it (here is how it is done)
If the day of the week matches what's in the selected UITableViewCell, you are done.
Otherwise, add one day to NSDate (here is how it is done), and go back to step 2.

Resources