How can I find out whether an NSDate is a business day? - ios

How can I find out whether an NSDate is a business day? That is to say, whether or not it is a weekend according to the user's current locale and calendar settings - so not hardcoded to just be Monday to Friday?
NSCalendar has a firstWeekday property, but that just seems to be a presentational thing; it's Sunday in the US and Monday in the UK.
EDIT: I'm not worried about holidays, I just want to be able to shade the weekends of a calendar in the same way as the built-in calendar app.

Update for iOS 8 and higher:
NSCalendar now contains method - (BOOL)isDateInWeekend:(NSDate *)date
which checks whether a date falls under weekend in a particular calendar & locale.
Old answer
If you only need to shade the weekends, [NSDateComponents weekday] is your friend. This method will return 1 for Sunday and 7 for Saturday.
Fact: Most applications assume weekend = Saturday & Sunday. AFAIK this is correct for all countries using Gregorian Calendar. The only possibility covering all other calendars is to have a local database per calendar/locale and watch for changes. If such a change happens an application update is needed or new data has to be downloaded.
If we are talking about business days (not only weekends), we are talking about something that
has no clear definition (what is a business day changes between companies)
has no specification (for example - date formatting, time zones etc. has internationally accepted specifications)
is often changed by local law (e.g. holidays added)
What you need is to let the users set up your applications for their individual needs, depending on their nationality and employer.

You can get if the day is Monday to Friday, but as far as I know, there's no such things as business day for NSDate depending on the location, as for example firstWeekday, so you will need to implement it yourself.

Use NSDateComponents.
NSDate *date = [NSDate date];
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:date];
NSLog(#"Weekday: %d", dateComponents.weekday);
This will also differentiate whether the device is in a territory where Sunday or Monday are considered to be the first day of the week.

Related

NSCalendar firstWeekday always 1 regardless of user preference

I am attempting to get the firstWeekday as set in the user's system preferences, using [[NSCalendar currentCalendar] firstWeekday], however the value returned is always 1 (Sunday) regardless of what is set in the system preferences. Also tried the autoupdatingCurrentCalendar with the same results.
From the docs, it states [NSCalendar currentCalendar] returns a calendar formed from the settings for the current user’s chosen system locale overlaid with any custom settings the user has specified in System Preferences. From that description it sounds like I should be able to access the firstWeekday set in the user settings.
Anyone have any ideas as to why this is not returning the correct firstWeekday?
Here's the deal…
Changing the first weekday in Settings > Calendar > First Weekday ONLY adjusts the day weeks start on in Apple's Calendar app. It does NOT adjust underlying NSCalendar objects. To do that you'll have to change your phone's region under Settings > General > Date & Time > Language & Region > Region to something like Germany to test a calendar with the start of the week as Monday, or US for Sunday, etc.
(To anyone setting out to work on calendar based code: thank you for caring about localization and also here be dragons. Good luck!)

Best way to store the day of the week using NSDate and NSDateFormatter

I'm trying to reproduce some of the functionality of the default Clock where I let users select a repeat frequency for an alarm. The problem is that different NSCalendar settings will give you different names for the days of the week. How do I store the selected days of the week in such a way that if the user changes their calendar the frequency always falls on the right day of the week?
One solution would be to check if the calendar has changed every time the app comes into foreground, and if so, make appropriate changes from there. Probably not the most elegant solution, nor the best practice, but it could get the job done.
Take (long) [dateYouWantToRemember timeIntervalSince1970] and store that in a property file or some such. (You could use timeIntervalSinceReferenceDate, but the 1970 number is the "UNIX epoch date" and more standardized -- you can find converters online to check it.)
Later, under the (potentially) different calendar, do [NSDate dateWithTimeIntervalSince1970:theLongYouStored] and you will reconstruct the date, in the new calendar.
Now use NSCalendar/NSDateComponents to find out the new ordinal day of week of that date, and use [NSDateFormatter weekdaySymbols] to fetch a list of the weekday names. Index the list with the ordinal to fetch the new day name. Use NSDateComponents to do arithmetic on you dates to select the next date that is that day of the week, as needed for your app.

Converting time strings to dates and finding the upcoming one

In my program, I have an NSArray of various dates and times, stored as strings and formatted like this: #[#"07:23",#"18:09",#"13:55"];
When I use an NSDateFormatter to convert these to NSDates, the times are correct, but year/month/day information is added.
The arrays that I have created are columns of a bus schedule. Each entry is one timeslot for whatever stop the array represents. My application needs to take the current time: [NSDate date] and see which time from the array is next in sequence. I'm just trying to display when the very next bus will arrive.
I have thought of comparing each element of the array with the current date and time using -[NSDate's laterDate:], but the problem is that when I convert the strings to NSDate objects, it gives them some random day-month-year like 13:55:00 January 1st, 2001 which will always be before the current date, so my test won't work.
I can find some workarounds that are really tragically McGuyvered but I would prefer something clean.
What I want to know are these things:
Can I remove the day/month/year portion from the NSDate?
Is it possible to easily set the day/month/year of each object in my array to today without using NSDateComponents and NSCalendar? I can manipulate them as they enter the array.
Would it be easier to reformat the current date/time to match the day/month/year of the array?
Otherwise, is there a better, cleaner solution to find the next upcoming timeslot? I am open to changing the entire format from arrays if necessary.
Can I remove the day/month/year portion from the NSDate?
No. An NSDate is merely an instant in time that is some number of seconds since some reference date. Describing that instant in time as some year/month/day depends on the local calendar. For example, the "day of month" of [NSDate date] as I type this is 28 where I live but 29 for the same NSDate value in Japan.
Is it possible to easily set the day/month/year of each object in my
NSMutableArray to today? without using NSDateComponents and
NSCalendar?
No. That's what NSDateComponents is for.
Otherwise, is there a better, cleaner solution to find
the next upcoming timeslot? I am open to changing the entire format
from arrays if necessary.
Use NSCalendar's -components:fromDate: to get an NSDateComponents object that matches [NSDate date]. Replace the hour/minute/second components with an arrival time's hour/minute/second: this is an arrival time today. Add one to the day component: this is an arrival time tomorrow. (Weekend and holiday schedules cause extra complication; the weekday component may be useful.) Convert back to NSDate using NSCalendar's -dateFromComponents: and perform your date comparisons there.

Compare iOS Time Zone Rules

If the user is in Eastern time, I don't want to display "EST" or "EDT" but if they are not, I do want to display it.
Is there an easy way to compare TimeZone rules in iOS?
[NSTimeZone localTimeZone] returns America/Indianapolis, but I just want to know if it is eastern.
Comparing data does not work, do I just compare secondsFromGMT?
EDIT
Comparing offsets does seem to work, but I don't know if it is going to cause problems later?
You can use [[NSTimeZone localTimeZone] abbreviation]; to get the abbreviation of whatever time zone the user is in.
This gives the abbreviation of the current date (EDT if not daylight savings and EST if daylight savings, in your case). If you want the abbreviation of a specific date, you can use abbreviationForDate: and insert any date.
Here is some more information about NSTimeZone
Edit:
If you want to actually compare multiple time zones (as in to check if it is the current time zone or not), you can use isEqualToTimeZone:. If that does not fit your needs, look at some of the other NSTimeZone methods in that link.
I've had lots of pain from dealing with time zone issues on our tools app, that includes event scheduling across time zones. The time zone names have not been friendly to use. Also, Daylight savings time is a great big pain.
What has worked consistently for me is calls like this using timeZoneForSecondsFromGMT
I also convert my stored dates to GMT, then I can set them to the local time on display.
NSTimeZone *zone = [NSTimeZone systemTimeZone];
NSInteger timeZoneOffset = [zone secondsFromGMT];
[self.dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:timeZoneOffset]];

How to create NSDates (begin and end of day) without using NSCalendar?

I have an iPad app, using XCode 4.5, iOS 6.0 and Storyboards. I need to create a NSCompoundPredicate using a NSDate, which I am given by the user selecting from UIDatePicker. The examples I have seen show the use of NSCalendar and NSCalendarComponents, both of which use the "default" calendar.
This is the code, using one NSPredicate, which works but gets all of the records for that date and all dates in the future, which is wrong.
NSMutableArray *apptDataArray = [NSMutableArray new];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(aStartTime = %#)", [dict objectForKey:#"selectedDate"]];
I need to use a NSCompoundPredicate because I need to get all of the records for a particular date (from 1 minute after midnight to midnight of the selected date).
Is there a way to create two NSDates (begin and end of the day) for the NSCompoundPredicate from a given NSDate, in this case: [dict objectForKey:#"selectedDate"] (which is a NSDate)? (This question is NOT a duplicate of this which uses the NSCalendar code mentioned above.)
UPDATE: updated question in bold.
While you say that you don't want to use a NSCalendar, you already are:
From the UIDatePicker Class Reference:
calendar
The calendar to use for the date picker.
#property(nonatomic, copy) NSCalendar *calendar
Discussion
The default value of this property corresponds to the user’s current calendar as configured in Settings. This is equivalent
to the value returned by calling the NSCalendar class method
currentCalendar. Setting calendar to nil is equivalent to setting it
to its default value.
Calendars specify the details of cultural systems used for reckoning
time; they identify the beginning, length, and divisions of a year.
Basically, the only way to get accurate values that you can use for a query is to use the same calendar that was used to create your NSDate. So, the short version is: Go ahead and use a calendar. In fact, use this one:
NSCalendar *cal = [NSCalendar currentCalendar];
You can't really create a meaningful NSDate object without referencing a calendar at some point. As others have pointed out, an NSDate is stored as the number of seconds since a particular date and time. Unless you feel like completely re-writing all calendar operations in order to account for things like leap years, leap seconds, etc you should use what Apple has already given us.
An NSDate represents the number of seconds since a fixed epoch. Internally, the epoch used is January 1, 2001 12:00:00AM UTC+0. Most APIs across platforms use another epoch, January 1, 1970 12:00:00AM UTC+0, called the "UNIX epoch" or "time_t epoch", so there are APIs to convert to/from that epoch as well. As a result, a date is just a single number, of type NSTimeInterval (a typedef of double.)
To get the first and last moments of a day, you need to define a "day." This is done with a calendar. Most calendars have 24-hour days, but I'm sure there's one out there that doesn't.
You say you cannot use NSCalendar because you need to work in a calendar that it does not support. Can you tell us which calendar you need to use?
To my mind its not possible (why you don't want to use NSCalendar?)

Resources