I know there are some questions similar to this one, but I just can't find what's wrong with my code.
Basically what I want is if today is not sunday, currentDate is set to the last sunday.
If today is sunday I currentDate is set to the sunday before.
Here's how I'm trying to do it.
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSWeekCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:currentDate];
if (components.weekday == 1) { //Its sunday. We just need to subtract a week
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
}else{ //If its not sunday we just go to sunday
[components setWeekday: 1];
currentDate = [[NSCalendar currentCalendar] dateFromComponents:components];
}
The first time this part of the code is executed I get the right answer. After the first time I get weird dates, like 02 Dec. 4026, and the year keeps going up.
Here's the code that made it work:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components: ( NSWeekCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit) fromDate:currentDate];
int weekday = components.weekday;
if (weekday == 1) {
[components setWeek:components.week -1];
}else{
[components setWeekday:1];
}
currentDate = [calendar dateFromComponents:components];
Use nextDateAfterDate with the .SearchBackwards option if you are using iOS 8.0+
let calendar = NSCalendar.currentCalendar()
let options: NSCalendarOptions = [.MatchPreviousTimePreservingSmallerUnits, .SearchBackwards]
calendar.nextDateAfterDate(NSDate(), matchingUnit: .Weekday, value: 1, options: options)
Try this category on NSCalendar
#interface NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date;
#end
#implementation NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date
{
static NSUInteger SUNDAY = 1;
static NSUInteger MONDAY = 2;
NSDate *startOfWeek;
[self rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfWeek
interval:NULL
forDate:date];
if(self.firstWeekday == SUNDAY){
NSDate *beginningOfDate;
[self rangeOfUnit:NSDayCalendarUnit
startDate:&beginningOfDate
interval:NULL forDate:date];
if ([startOfWeek isEqualToDate:beginningOfDate]) {
startOfWeek = [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -7;
comps;
})
toDate:startOfWeek
options:0];
}
return startOfWeek;
}
if(self.firstWeekday == MONDAY)
return [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -1;
comps;
})
toDate:startOfWeek
options:0];
return nil;
}
#end
use it as:
NSDate *lastSunday = [calendar previousSundayForDate:[NSDate date]];
PS:
a version for "one-method-one-return-statement"- fetishists:
#implementation NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date
{
NSDate *resultDate = nil;
static NSUInteger SUNDAY = 1;
static NSUInteger MONDAY = 2;
NSDate *startOfWeek;
[self rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfWeek
interval:NULL
forDate:date];
if(self.firstWeekday == SUNDAY){
NSDate *beginningOfDate;
[self rangeOfUnit:NSDayCalendarUnit
startDate:&beginningOfDate
interval:NULL forDate:date];
if ([startOfWeek isEqualToDate:beginningOfDate]) {
startOfWeek = [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -7;
comps;
})
toDate:startOfWeek
options:0];
}
resultDate = startOfWeek;
}
if(self.firstWeekday == MONDAY)
resultDate = [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -1;
comps;
})
toDate:startOfWeek
options:0];
return resultDate;
}
#end
The reason you got 4026 is because of this line:
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSWeekCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:currentDate];
It sets the year component to the current year, 2013. It also initializes the week and weekday components. Then the components are added to the current date, which doubles the year to 4026, here:
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
If you want to subtract only one week, do it like this:
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
[components release];
Related
I have tried changing my functions that get the start of day/end of day NSDates several times and I know the loop is working bc its printing out the right days of the week, but the start_timestamp and end_timestamp for each day is showing up the same for some reason. It has to be something involving the int not changing or something not directly related to the logic im not seeing:
Here is what its printing out:
WEEK_ARRAY:{
Friday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
Monday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
Saturday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
Sunday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
Thursday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
Tuesday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
Wednesday = {
"DAY_END" = 1454389199;
"DAY_START" = 1454302800;
};
}
Here is the logic:
- (NSMutableDictionary *)lastSevenDays {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"EEEE"];
NSDate *date = [NSDate date];
NSMutableDictionary *weekDays = [[NSMutableDictionary alloc] init];
for (int i = 0; i <7; i++) {
NSString *weekDay = [formatter stringFromDate:date];
date = [self dateByAddingOneDayFromDate:date];
NSMutableDictionary *specificDayDict=[[NSMutableDictionary alloc]init];
NSDate *StartNSDate=[self beginningOfDay:date];
NSNumber *StartTstamp=[NSNumber numberWithInt:[self convertNSDateToTimestamp:StartNSDate]];
int endOfDay=[self convertNSDateToTimestamp:[self endOfDay:date]];
NSLog(#"DAY:%# | DAY_START:%# | DAY_END:%d",weekDay,StartTstamp,endOfDay);
[specificDayDict setValue:StartTstamp forKey:#"DAY_START"];
[specificDayDict setValue:[NSNumber numberWithInt:endOfDay] forKey:#"DAY_END"];
[weekDays setObject:specificDayDict forKey:weekDay];
}
return weekDays;
}
- (NSDate *)dateByAddingOneDayFromDate:(NSDate *)date {
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *plusOneDay = [[NSDateComponents alloc] init];
[plusOneDay setDay:+1];
NSDate *newDate = [cal dateByAddingComponents:plusOneDay
toDate:date
options:NSWrapCalendarComponents];
return newDate;
}
-(NSDate *)beginningOfDay:(NSDate *)date
{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
return [cal dateFromComponents:components];
}
-(NSDate *)endOfDay:(NSDate *)date
{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
[components setHour:23];
[components setMinute:59];
[components setSecond:59];
return [cal dateFromComponents:components];
}
-(NSDate *)convertTimestampToNSDate:(int)timestamp{
NSDate* date = [NSDate dateWithTimeIntervalSince1970:timestamp];
return date;
}
-(int )convertNSDateToTimestamp:(NSDate *)date{
int timestamp=[date timeIntervalSince1970];
return timestamp;
}
The reason is the missing NSDayCalendarUnit component in both beginningOfDay and endOfDay methods.
In beginningOfDay and endOfDay functions you have missed NSCalendarUnitDay. Also NSMonthCalendarUnit, NSYearCalendarUnit are deprecated in ios8.
-(NSDate *)beginningOfDay:(NSDate *)date{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitDay| NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond) fromDate:date];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
return [cal dateFromComponents:components];
}
-(NSDate *)endOfDay:(NSDate *)date{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitDay| NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond) fromDate:date];
[components setHour:23];
[components setMinute:59];
[components setSecond:59];
return [cal dateFromComponents:components];
}
I am using
- (NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSCalendarOptions)opts
to calculate a new date, like below:
NSDateComponents *quaterComps = [NSDateComponents new];
quaterComps.quarter = 1;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *nextDate = [gregorian dateByAddingComponents:quaterComps toDate:firstDate options:0];
When firstDate is 2013-12-31 16:00:00 +0000,
Above API keeps returning the same result, not the next quarter date
** current date and next date **
NSDate *currentDate = [NSDate date];
NSDate *futureTime = [currentDate dateByAddingTimeInterval:60*60*72];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
[calendar setTimeZone:timeZone];
NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit fromDate:futureTime];
if ([components hour] >= 19) { // make it the next day
[components setDay:[components day] + 1 ];
}
[components setHour:8];
[components setMinute:00];
NSDate *alertTime = [calendar dateFromComponents:components];
NSDate *date = [NSDate date];
NSDateComponents *comp = [NSDateComponents new];
comp.weekOfYear = 3;
NSDate *date1 = [[NSCalendar currentCalendar] dateByAddingComponents:comp toDate:date options:0];
NSLog(#"date: %#", date);
NSLog(#"date1: %#", date1)
Before giving downvote, comment the reason
I have created a UIDatePicker with minimum & maximum date values (i.e. 6 months of date picker) Now, i need to get 7 days from selected date. There i need to check the conditions,
If date is today date i need to get 7 days from today onwards
If date is last date (i.e. last date of picker) need to get last 7 days including last day
If date is middle of today's date & last date i need to get last 3 days, next 3 days including today date. And, also while getting last & next 3 days it shouldn't get exceed with picker's date limit.
Here's my code snippet:
- (void)addDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSDate *startDate = self.selectedDate;
for (int x = 0; x <= range; x++) {
NSLog(#"%#", [dateFormat stringFromDate:startDate]);
startDate = [startDate dateByAddingTimeInterval:(60 * 60 * 24)];
}
}
- (void)minusDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [NSDateComponents new];
for (NSInteger i=0; i<range; i++) {
comps.day += -1;
NSDate *date = [calendar dateByAddingComponents:comps toDate:self.selectedDate options:0];
NSDateComponents *components = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:date];
NSLog(#"%#", [dateFormat stringFromDate:[calendar dateFromComponents:components]]);
}
}
- (void)calculateDateRange {
if ([dateArray count] > 0) {
[dateArray removeAllObjects];
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastcomponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:[NSDate date]];
lastcomponents.month += 6;
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"yyyy/MM/dd"];
NSDate *currentDate = [NSDate date];
NSDate *selectedD = self.selectedDate;
NSDate *endDate = [calendar dateFromComponents:lastcomponents];
NSDate *fromDate;
NSDate *toDate;
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:currentDate];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:selectedD];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger first = [difference day];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:selectedD];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:endDate];
NSDateComponents *difference2 = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger second = [difference2 day];
if ((first == 0 || first < 3) && second > 7) {
[self addDays:7];
} else if (first >= 3 && second > 7) {
[self minusDays:3];
[self addDays:3];
}else if (second == 7 || second < 7) {
[self minusDays:7];
}
}
This is working fine. But, can't get exact last & previous days.
Anyone has idea on this?
My interpretation of your needs
You have a date picker. When a date is picked you need to create a 7 day range around that selected date.
So, if the selected date is 15/11/2014 then you want 3 days either side so...
12/11/2014 - 18/11/2014.
However, the date range cannot exceed the limits of the date picker. So if the minimum date on the date picker is set to 14/11/2014 then (in the above example) the date range would be...
14/11/2014 - 21/11/2014
Even after your additional explanation this is still my interpretation. And my code does exactly this.
Solution
You CANNOT use 60*60*24 to mean one day. This is just wrong. When dealing with dates you should always be using NSDateComponents and NSCalendar.
Also, break down your problem into small steps. There is no reason to do everything in one giant function.
OK I guess you have a datePicker action somewhere so I'd code it like this...
- (void)datePickerDateChanged
{
NSDate *minimumDate = self.datePicker.minimumDate;
NSDate *maximumDate = self.datePicker.maximumDate;
NSDate *selectedDate = self.datePicker.date;
NSDate *startDate;
NSDate *endDate;
if ([self numberOfDaysFromDate:minimumDate toDate:selectedDate] < 3) {
// get 7 days after minimumDate
startDate = minimumDate;
endDate = [self dateByAddingDays:6 toDate:minimumDate];
} else if ([self numberOfDaysFromDate:selectedDate toDate:maximumDate] < 3) {
// get 7 days before maximumDate
startDate = [self dateByAddingDays:-6 toDate:maximumDate];
endDate = maximumDate;
} else {
// get 3 days before and 3 days after selectedDate
startDate = [self dateByAddingDays:-3 toDate:selectedDate];
endDate = [self dateByAddingDays:3 toDate:selectedDate];
}
// Here startDate and endDate define your date range.
}
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [NSDateComponents new];
components.day = days;
return [calendar dateByAddingComponents:components toDate:date options:0];
}
- (NSInteger)numberOfDaysFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
// always return positive. We just want magnitude of days.
return components.day > 0 ? components.day : -components.day;
}
This is untested and just a first attempt.
I am using NSDateComponents and NSCalendar to get the dates between two dates.
// Get dates between before and after
NSDateComponents *betweenDateComponents = [calendar components:componentFlags
fromDate:afterDate
toDate:beforeDate
options:0];
I then have a simple for loop that is based on the components' day property:
for (int i = 1; i < betweenDateComponents.day; ++i) {
This works perfectly when I am working with all of the days for a week, or a month. If I filter by week, the day property logs as 6. If I filter by month the day property is 29, but for some reason when I filter by year, the day property logs as 29 instead of 364.
Why is the day property always wrong when I'm trying to work with a year?
Here is all of my code up until the for loop I included above:
// Set the date components according to the type of filter we're doing
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [NSDateComponents new];
if (filterType == CommentFilterTypeWeeklyByDay) {
[components setDay:-6];
} else if (filterType == CommentFilterTypeMonthlyByDay) {
[components setDay:-29];
} else if (filterType == CommentFilterTypeYearlyByMonth) {
[components setDay:-364];
}
NSUInteger componentFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
// Zero out before and after dates
NSDate *beforeDate = [NSDate date];
NSDateComponents *zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:beforeDate];
beforeDate = [calendar dateFromComponents:zeroComponents];
NSDate *afterDate = [calendar dateByAddingComponents:components toDate:beforeDate options:0];
zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:afterDate];
afterDate = [calendar dateFromComponents:zeroComponents];
NSMutableArray *dates = [NSMutableArray new];
[dates addObject:afterDate];
// Get dates between before and after
NSDateComponents *betweenDateComponents = [calendar components:componentFlags
fromDate:afterDate
toDate:beforeDate
options:0];
NSLog(#"%d", betweenDateComponents.day);
for (int i = 1; i < betweenDateComponents.day; ++i) {
Because your components include NSYearCalendarUnit and NSMonthCalendarUnit, I suspect you'll have to use month and year instead of just day. The components are cumulative, not independent.
You will always be able to get just the number of days (regardless of how many months or years are between your dates) by changing your components:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [NSDateComponents new];
switch (filterType) {
case CommentFilterTypeWeeklyByDay: [components setDay:-6]; break;
case CommentFilterTypeMonthlyByDay: [components setDay:-29]; break;
case CommentFilterTypeYearlyByMonth: [components setDay:-364]; break;
case CommentFilterTypeCustom: [components setDay:-590]; break;
default: break;
}
NSUInteger componentFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDate *beforeDate = [NSDate date];
NSDateComponents *zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:beforeDate];
beforeDate = [calendar dateFromComponents:zeroComponents];
NSDate *afterDate = [calendar dateByAddingComponents:components toDate:beforeDate options:0];
zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:afterDate];
afterDate = [calendar dateFromComponents:zeroComponents];
NSDateComponents *betweenDateComponents = [calendar components:NSDayCalendarUnit
fromDate:afterDate
toDate:beforeDate
options:0];
NSLog(#"%d", betweenDateComponents.day);
The NSLog here prints
6 for CommentFilterTypeWeeklyByDay
29 for CommentFilterTypeMonthlyByDay
364 for CommentFilterTypeYearlyByMonth
590 for CommentFilterTypeCustom
I want to get list of days in selected month.
For example, If user select feb then it show 1 to 28 0r 29 days. How can i do that?
Thanks
You can try the following code, I have given the today's date to find the list of dates in this month. You can give some other date to find the list of that given month.
NSDate *today = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSMutableArray *datesThisMonth = [NSMutableArray array];
NSRange rangeOfDaysThisMonth = [cal rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:today];
NSDateComponents *components = [cal components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSEraCalendarUnit) fromDate:today];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
for (NSInteger i = rangeOfDaysThisMonth.location; i < NSMaxRange(rangeOfDaysThisMonth); ++i) {
[components setDay:i];
NSDate *dayInMonth = [cal dateFromComponents:components];
[datesThisMonth addObject:dayInMonth];
}
NSLog(#"datesThisMonth: %#", datesThisMonth);
You can retrieve the number of days in specified month
NSCalendar* cal = [NSCalendar currentCalendar];
NSDateComponents* comps = [[NSDateComponents alloc] init];
// Set month here
[comps setMonth:2]; // 2 = February
NSRange range = [cal rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:[cal dateFromComponents:comps]];
NSLog(#"current day count in this month: %lu", (unsigned long)range.length);
I have an open source calendar event project. Maybe you can check it out if you need anything.
DPCalendar
I have simplied it to
- (NSInterger) numberOfDaysForMonth:(NSDate *)date {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate: date];
[components setDay:1];
//Get the first day of the month
NSDate *firstDay = [calendar dateFromComponents:components];
[components setDay:0];
[components setMonth:components.month + 1];
//Get the last day of the month
NSDate *lastDay = [calendar dateFromComponents:components];
components = [self.calendar components:NSDayCalendarUnit fromDate:firstDay toDate:lastDay options:0];
return components.day + 1;
}
Cheers
Generally, we only need to handle Feb depend on the year, here's my method implemented:
+ (NSUInteger)numberOfDaysForYear:(NSInteger)year
month:(NSInteger)month
{
if (2 == month) {
NSCalendar * gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents * dateComponents = [[NSDateComponents alloc] init];
[dateComponents setYear:year];
[dateComponents setMonth:month];
[dateComponents setDay:1];
NSDate * firstDayDate = [gregorianCalendar dateFromComponents:dateComponents];
NSRange days = [gregorianCalendar rangeOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitMonth
forDate:firstDayDate];
return days.length;
} else if (4 == month || 6 == month || 9 == month || 11 == month) {
return 30;
} else {
return 31;
}
}