I want to create an array of NSDates starting from today to next month. This can easily be done in Ruby using Time.now..(Time.now + 30.days)
How can I create an array of dates just like in Ruby in Objective C?
Any ObjC solution is unfortunately going to be far more verbose than that Ruby code.
The correct way to make the calculation is with NSDateComponents:
NSMutableArray * dateArray = [NSMutableArray array];
NSCalendar * cal = [NSCalendar currentCalendar];
NSDateComponents * plusDays = [NSDateComponents new];
NSDate * now = [NSDate date];
for( NSUInteger day = 0; day < NUMDAYS; day++ ){
[plusDays setDay:day];
[dateArray addObject:[cal dateByAddingComponents:plusDays toDate:now options:0]];
}
To make the procedure more convenient (if you need to do it more than a few times), you could put this loop into a category method on NSCalendar, with NUMDAYS replaced with the argument and substituting self for cal.
There's nothing built in to do this quite as concisely as the Ruby you've posted. Breaking the problem down, you need a way to get the day after a particular date. Here's a function that will do that:
NSDate *CalendarDayAfterDate(NSDate *date)
{
NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = 1;
NSCalendar *calendar = [NSCalendar currentCalendar];
return [calendar dateByAddingComponents:components toDate:date options:0];
}
Next, you need to get an array of days one after the other:
NSDate *today = [NSDate date];
NSMutableArray *dates = [NSMutableArray arrayWithObject:today];
for (NSUInteger i=0; i<30; i++) {
NSDate *tomorrow = CalendarDayAfterDate(today);
[dates addObject:tomorrow];
today = tomorrow;
}
After much downvoting and commenting, here's my REVISED answer...
-(NSDate *)nextDayFromDate:(NSDate *)originalDate {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponent = [NSDateComponents new];
dateComponent.day = 1;
NSDate *tomorrow = [calendar dateByAddingComponents:dateComponent toDate:originalDate options:0];
return tomorrow;
}
NSMutableArray *dateArray = [NSMutableArray array];
NSDate *now = [NSDate date];
[dateArray addObject:now];
for (int i=0;i<31;i++) {
NSDate *firstDate = [dateArray objectAtIndex:i];
NSDate *newDate = [self nextDayFromDate:firstDate];
[dateArray addObject:newDate];
}
What this does is use the NSCalendar API to add a "day interval" to any given NSDate. Add "Now" to the array, then do a loop 30 times, each time using the previous NSDate object as input to the logic.
Related
i want to get all the dates from yesterday date to one month..
like today is 19 may, so i need all the date from 18 may to 18 April.
please help.
You can use this code.It works.
NSDate *currentDate = [NSDate date];
NSLog(#"Current Date = %#", currentDate);
NSDateComponents *dateComponents = [NSDateComponents new];
dateComponents.month = -1;
NSDate *currentDatePlus1Month = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponents toDate:currentDate options:0];
NSLog(#"Date = %#", currentDatePlus1Month );
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar];
NSDateComponents *days = [[NSDateComponents alloc] init];
NSMutableArray* arr =[[NSMutableArray alloc]init];
NSInteger dayCount = 0;
while ( TRUE ) {
[days setDay: ++dayCount];
NSDate *date = [gregorianCalendar dateByAddingComponents: days toDate: currentDatePlus1Month options: 0];
if ( [date compare: currentDate] == NSOrderedAscending ){
[arr addObject:date];
}
if([[arr lastObject] isEqual:[currentDate dateByAddingTimeInterval:-60*60*24*1]])
{
NSLog(#"%lu",(unsigned long)arr.count);
break;
}
// Do something with date like add it to an array, etc.
}
if you find all dates you can remove count and get all dates in array.
To achieve this, I think you should have an Array holding all those dates. I'll write pseudocode about the logic here.
INIT dateArray
NSDate pastDate = (today).yesterday
NSDate lastMonth = pastDate.lastMonth()
WHILE pastDate > lastMonth // pastDate is after lastMonth
dateArray.add(pastDate)
pastDate = pastDate.yesterday
END WHILE
About how to turn this pseudocode into real code is another story (this would be quite long). Hope this help.
PS: If you'd like Objective-C solution, please comment. I'll take my time write it for you ;)
Given some startDate, I would like to degenerate x number of consecutive days after this startDate. I'm attempting to use the following code:
// Generate dates
self.days = [[NSMutableArray alloc] init];
NSDate *startDate = [NSDate date];
NSCalendar *theCalendar = [NSCalendar currentCalendar];
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
dayComponent.day = 1;
for (int i = 1; i < self.mygoal.goalDays; i++) {
[self.days addObject:startDate];
startDate = [theCalendar dateByAddingComponents:dayComponent toDate:startDate options:0];
}
Question: Is the reassignment of startDate ok, given that I'm adding the same object to self.days?
Creating a sequence of dates is not as trivial as it sounds. Actually it is covered in the great WWDC2011 video «Performing Calendar Calculations».
You are adding in every loop a day to the last date. But actually this will fail in timezones with DST if the time is in the hour that is changed for the day of switching and for any following days as the dates will be nil.
If you instead change the date components i the loop and add it to the original satrtdate, it will only effect the day of DST-switching.
To also handle that you can set the hour of the start date to something safe — as noon — as all switches are performed at night hours.
With all this in mind I would use something like this to create a sequence of days with times set to start of day:
NSUInteger numberOfDays = 10;
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *startDate = [cal dateFromComponents:({
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.year = 2015;
comps.month = 1;
comps.day = 2;
comps.hour = 12; // for DST-safety
comps;
})];
NSMutableArray *dates = [#[] mutableCopy];
for (NSUInteger i =0 ; i < numberOfDays; ++i) {
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = i;
NSDate *date = [cal dateByAddingComponents:comps
toDate:startDate
options:0];
// set date to beginning of day
[cal rangeOfUnit:NSCalendarUnitDay
startDate:&date
interval:NULL
forDate:date];
[dates addObject:date];
}
So, yes, reassignment is technically OK, but in this particular case it is might cause unexpected trouble.
It's fine because you're not actually adding the same object. dateByAddingComponents: returns a new object, so when you assign it to startDate you are replacing the reference to your old object to a reference to the new one
I am creating a custom type calendar and I am trying to see if it is possible to store dates in an array without statically assigning each one. For example the 1st date in the array would be the day it was first created and it would save the next week lets say into the relevant indexes in the array.
NSMutableArray *thisWeek = [today, tomorrow, sunday(Feb 24), monday (Feb 25), etc];
What would be the best way to go about storing the future dates?
NSMutableArray *days = [[NSMutableArray alloc] init];
NSCalendar *cal = [NSCalendar autoupdatingCurrentCalendar];
NSDateComponents *tempCop = [cal components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:tempCop];
for (int i = 0; i < 8; i++)
{
NSDateComponents *comps = [[NSDateComponents alloc]init];
[comps setDay:i];
[days addObject:[cal dateByAddingComponents:comps toDate:today options:0]];
}
NSMutableArray *days;
days = [[NSMutableArray alloc] init];
NSDate *todayDate = [NSDate Date];
[days addObject:todayDate];
for (int i = 1; i <= 6; i++)
{
NSDate *newDate = [[NSDate date] dateByAddingTimeInterval:60*60*24*i];
[days addObject:newDate];
}
In this way, you can add days in array.
Take a look at dateByAddingTimeInterval: in the NSDate docs (link). It lets you add a given amount of seconds to a date.
I'm trying get only the Saturdays and Sundays between two dates, but I don't know why get me free days on a week.
Here is my code:
- (BOOL)checkForWeekend:(NSDate *)aDate {
BOOL isWeekendDate = NO;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSRange weekdayRange = [calendar maximumRangeOfUnit:NSWeekdayCalendarUnit];
NSDateComponents *components = [calendar components:NSWeekdayCalendarUnit fromDate:aDate];
NSUInteger weekdayOfDate = [components weekday];
if (weekdayOfDate == weekdayRange.location || weekdayOfDate == weekdayRange.length) {
// The date falls somewhere on the first or last days of the week.
isWeekendDate = YES;
}
return isWeekendDate;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSString *strDateIni = [NSString stringWithString:#"28-01-2012"];
NSString *strDateEnd = [NSString stringWithString:#"31-01-2012"];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"dd-MM-yyyy"];
NSDate *startDate = [df dateFromString:strDateIni];
NSDate *endDate = [df dateFromString:strDateEnd];
unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [gregorian components:unitFlags fromDate:startDate toDate:endDate options:0];
// int months = [comps month];
int days = [comps day];
for (int i=0; i<days; i++)
{
NSTimeInterval interval = i;
NSDate * futureDate = [startDate dateByAddingTimeInterval:interval];
BOOL isWeekend = [self checkForWeekend:futureDate]; // Any date can be passed here.
if (isWeekend) {
NSLog(#"Weekend date! Yay!");
}
else
{
NSLog(#"Not is Weekend");
}
}
}
The problem:
The issue was caused by NSTimeInterval interval = i; The logic of the for loop was to iterate by days. Setting the time interval to i was iterating by seconds.
From documentation on NSTimeInterval
NSTimeInterval is always specified in seconds;
The answer:
Changing the NSTimeInterval line to
NSTimeInterval interval = i*24*60*60;
Here is a link to another answer I posted on SO (shameless, I know). It has some code that may help you with dates in the future. The methods are implemented as categories of NSDate, meaning they become methods of NSDate.
There are several functions there that help with weekends. But these two might be most helpful:
- (NSDate*) theFollowingWeekend;
- (NSDate *) thePreviousWeekend;
They return the date of the weekend following and prior to the receiver (self).
Generally, you should not use the notion that a day is 86400 seconds, and should use NSDateComponents and NSCalendar. This works even when daylight savings time transitions occur between dates. Like this:
- (NSDate *) dateByAddingDays:(NSInteger) numberOfDays {
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
dayComponent.day = numberOfDays;
NSCalendar *theCalendar = [NSCalendar currentCalendar];
return [theCalendar dateByAddingComponents:dayComponent toDate:self options:0];
}
One very important thing to remember is that one day is not (necessarily) equal to 24*60*60 seconds. And you should not do date arithmetic yourself
What you really need to do might seem a little tedious but this is the correct thing to do: use NSCalendar and – dateByAddingComponents:toDate:options:
See Calendrical Calculations guide.
I want to set the NSDate time with my desired hours:minutes:seconds
currently im working with NSDate component but it is not giving the desired result
[comps setHour: -hours];
[comps setMinute:0];
[comps setSecond:0];
NSDate *minDate = [calendar_c dateFromComponents:comps];
This works great as an NSDate category.
/** Returns a new NSDate object with the time set to the indicated hour,
* minute, and second.
* #param hour The hour to use in the new date.
* #param minute The number of minutes to use in the new date.
* #param second The number of seconds to use in the new date.
*/
-(NSDate *) dateWithHour:(NSInteger)hour
minute:(NSInteger)minute
second:(NSInteger)second
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components: NSYearCalendarUnit|
NSMonthCalendarUnit|
NSDayCalendarUnit
fromDate:self];
[components setHour:hour];
[components setMinute:minute];
[components setSecond:second];
NSDate *newDate = [calendar dateFromComponents:components];
return newDate;
}
With the above category, if you have an existing date you want to change the time on, you do so like this:
NSDate *newDate = [someDate dateWithHour:10 minute:30 second:00];
If, however, you are trying to add or subtract hours from an existing date, a category method to do that is also straightforward:
/** Returns a new date with the given number of hours added or subtracted.
* #param hours The number of hours to add or subtract from the date.
*/
-(NSDate*)dateByAddingHours:(NSInteger)hours
{
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setHour:hours];
return [[NSCalendar currentCalendar]
dateByAddingComponents:components toDate:self options:0];
}
Your approach should work fine. I needed a solution for this type problem (setting the individual date components) and the following code works as expected for me. My situation: I wanted to create a date object that used the current date but had the time set to a value that was passed in as a string.
NSString *string = #"7:00";
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
NSDateFormatter *timeOnlyFormatter = [[NSDateFormatter alloc] init];
[timeOnlyFormatter setLocale:locale];
[timeOnlyFormatter setDateFormat:#"h:mm"];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *today = [NSDate date];
NSDateComponents *todayComps = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:today];
NSDateComponents *comps = [calendar components:(NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:[timeOnlyFormatter dateFromString:string]];
comps.day = todayComps.day;
comps.month = todayComps.month;
comps.year = todayComps.year;
NSDate *date = [calendar dateFromComponents:comps];
[calendar release];
[timeOnlyFormatter release];
[locale release];
One thing to note is that you really have to pay attention to time zones when you are judging whether a time appears to be accurate. For example, in my app, when you stop at a breakpoint, you will see the time in GMT (so it looks different than the input time, which is in my local time), but when the time is actually displayed on screen in the app, it is being formatted to display in the local timezone. You may need to take this into consideration to determine whether the result is actually different from what you would expect.
If this does not help, can you elaborate on "not giving the desired result"? What result is it giving and how does that compare to what you expected?
is Swift2
extension NSDate {
func dateWithHour (hour: Int, minute:Int, second:Int) ->NSDate?{
let calendar = NSCalendar.currentCalendar(),
components = calendar.components([.Day,.Month,.Year], fromDate: self)
components.hour = hour;
components.minute = minute;
components.second = second;
return calendar.dateFromComponents(components)
}
}
You can set 0 to hour, min, and second.
NSDateFormatter *tFmt = [[NSDateFormatter alloc] init];
tFmt.dateFormat = #"yyyy-MM-dd";
NSString *strNowDate = [NSString stringWithFormat:#"%# 00:00:00",[tFmt stringFromDate:[NSDate date]]];
NSDate *nowDate = [NSDate dateWithString:strNowDate formatString:#"yyyy-MM-dd HH:mm:ss"];
Swift 5 solution (based on #dattk answer) for those who fear Deprecation warnings :)
func date(withHour hour: Int, withMinute minute: Int, withSeconds second: Int) -> Date? {
let now = Date()
let calendar = NSCalendar.current
var components = calendar.dateComponents([.day,.month,.year], from: now)
components.hour = hour
components.minute = minute
components.second = second
return calendar.date(from: components)
}
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *comps = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:[NSDate date]];
comps.hour = 0;
comps.minute = 15;
NSDate *date = [calendar dateFromComponents:comps];
iOS 8