This code will return number of days between two days.
How to determine when time will pass and day become hours like (20 hours) or (40 minutes), then how do I check that it's a day or less than a day.
-(NSInteger)numberOfDaysUntilDay:(NSDate *)aDate
{
NSCalendar *calendar = [[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [calendar components:NSCalendarUnitDay
fromDate:self
toDate:aDate options:kNilOptions];
return [components day];
}
Use NSCalendarUnitSecond instead.
NSDateComponents *components = [calendar components:NSCalendarUnitSecond
fromDate:date
toDate:aDate options:kNilOptions];
Later you can convert seconds to any unit (hour, day, year).
Related
I have to find next Sunday date (NSDate) from device's current date.
I have used below logic:
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [calendar components:NSCalendarUnitWeekday | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:self];
NSUInteger weekdayToday = [components weekday];
NSInteger daysToMonday = (8 - weekdayToday) % 7;
NSDate *weekEndPrev = [self dateByAddingTimeInterval:60*60*24*daysToMonday];
Here, in EST if time is near to 11 PM, I'm getting Monday as Weekend.
Let me know feasible solution. I have tried many options. Nothings works with 2 different timezones.
Thanks in Advance.
You need just to get the number of days you need and then just add them to your current date:
NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate * newDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:value toDate:date options:0];
where value - is the number of days to add
Find current week Sunday Date first then add 7 days using NSDateComponents.
NSDate *today = [[NSDate alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *weekdayComponents = [gregorian components:NSCalendarUnitWeekday fromDate:today];
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay:(0 - ([weekdayComponents weekday] - 1))];
NSDate *sudayCurWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:today options:0];
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setDay:7];
NSDate *nextSunday = [gregorian dateByAddingComponents:offsetComponents toDate:sudayCurWeek options:0];
NSLog(#"%#",nextSunday);
You can do this without any math, which is best left to NSCalendar as it handles daylight savings etc. First you need a calendar with the correct time zone. For example for IST:
NSCalendar *gc = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
gc.timeZone = [NSTimeZone timeZoneWithName:#"IST"];
Then you can use nextDateAfterDate:matchingUnit:value:options: to find the start of the next Sunday after a given time. For example:
NSDate *start = ... // the time you wish to start from
NSDate *nextSunday =
[gc nextDateAfterDate:start
matching:UnitNSCalendarUnitWeekday // match based on weekday number
value:1 // Sunday = weekday 1
options:NSCalendarMatchNextTime // read the docs!
];
The returned date, nextSunday, will be the next Sunday at 00:00 in the timezone of the calendar, gc.
HTH
Finally, I got solution..
NSDateComponents *components = [[NSCalendar currentCalendar] componentsInTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"] fromDate:[NSDate date]]; // Pass date for which you want to find next Sunday
NSUInteger weekdayToday = [components weekday];
NSInteger daysToMonday = (8 - weekdayToday) % 7;
NSDate *weekEndPrev = [[NSDate date] dateByAddingTimeInterval:60*60*24*daysToMonday];
This question already has answers here:
How to calculate time in hours between two dates in iOS
(5 answers)
Closed 6 years ago.
i am using following method to get date time difference in minutes it giving me negative value .
I am using following code :
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSUInteger unitFlags = NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitMinute;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:dt2
toDate:dt1 options:0];
NSInteger months = [components month];
NSInteger days = [components day];
NSInteger minutes = [components minute];
NSLog(#"months %ld",(long)months);
NSLog(#"days %ld",(long)days);
NSLog(#"minutes %ld",(long)minutes);
int res = (int)minutes;
NSLog(#"int minutes %d",res);
return res;
I want to calculate difference between these two below date in minutes:
date1:2016-11-23 07:39:44 +0000
date2:2016-11-23 08:13:44 +0000
As a result I'm getting -34.
You need to replace with this :
NSDateComponents *components = [gregorian components:unitFlags
fromDate:dt1
toDate:dt2 options:0];
fromDate will contain an earlier date and toDate will contains later date in NSDateComponents you are passing it wrongly change this
NSDateComponents *components = [gregorian components:unitFlags
fromDate:dt2
toDate:dt1 options:0];
to this
NSDateComponents *components = [gregorian components:unitFlags
fromDate:dt1
toDate:dt2 options:0];
If you don't know which date is former, just return absolute value of difference. That will always give you positive value.
return abs(res);
I'm trying to work out the number of days between two dates. Here is how I am doing it:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
unsigned int calendarFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDate *dateToCheck = (self.subscriptionEnd ?: self.trialEnd);
NSLog(#"dateToCheck: %#", dateToCheck);
NSLog(#"current date: %#", [self systemTimeZoneDate]);
NSDateComponents *components = [gregorian components:calendarFlags
fromDate:[self systemTimeZoneDate]
toDate:dateToCheck
options:0];
return [components day] >= 0 ?: 0;
At the time of this writing, NSLog outputted the following:
2014-09-05 22:56:20.054 tweepy[9635:60b] dateToCheck: 2014-10-05 08:02:51 PM +0000
2014-09-05 22:56:20.057 tweepy[9635:60b] current date: 2014-09-05 10:56:20 PM +0000
It is returning a difference of one day because iOS thinks that the day is in YDM format.
Should I be indicating the date format somewhere?
Here is the code of how self.subscriptionEnd is setup:
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.day = 30;
self.subscriptionEnd = [gregorianCalendar dateByAddingComponents:dateComponents toDate:[self systemTimeZoneDate] options:0];
Here is the code of how self.trialEnd is setup:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.day = 2;
self.trialEnd = [gregorian dateByAddingComponents:dateComponents toDate:[self systemTimeZoneDate] options:0];
The date format doesn't have anything to do with it. What is happening is the NSDateComponents are giving you what you are asking for, the total difference in years, months, and days. If you want just the days, you need to only provide NSDayCalendarUnit. The docs make this clear:
Some operations can be ambiguous, and the behavior of the computation is calendar-specific, but generally larger components will be computed before smaller components; for example, in the Gregorian calendar a result might be 1 month and 5 days instead of, for example, 0 months and 35 days.
I am trying to determine if the current date is in fact three days or less from the end of the month. In other words, if I am in August, then I would like to be alerted if it is the 28,29,30, or 31st. If I am in February, then I would like to be notified when it is the 25,26,27, or 28 (or even 29). In the case of a leap year, I would be alerted from 26th onwards.
My problem is that I am not sure how to perform such a check so that it works for any month. Here is my code that I have thus far:
-(BOOL)monthEndCheck {
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
if (month is 3 days or less from the end of the month for any month) {
return YES;
} else {
return NO;
}
}
Because there are months with 28, 30, and 31 days, I would like a dynamic solution, rather than creating a whole series of if/else statements for each and every condition. Is there a way to do this?
This is how you get the last day of the month:
NSDate *curDate = [NSDate date];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* comps = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:curDate]; // Get necessary date components
// set last of month
[comps setMonth:[comps month]+1];
[comps setDay:0];
NSDate *tDateMonth = [calendar dateFromComponents:comps];
NSLog(#"%#", tDateMonth);
Source: Getting the last day of a month
EDIT (another source): How to retrive Last date of month give month as parameter in iphone
Now you can simply count from the current date.
If < 3 do whatever you wanted to do.
Maybe something like this:
NSTimeInterval distanceBetweenDates = [date1 timeIntervalSinceDate:date2];
double timeInSecondsFor3Days = 280000; //Better use NSDateComponents here!
NSInteger hoursBetweenDates = distanceBetweenDates / timeInSecondsFor3Days;
However I did not test that^^
EDIT: Thanks to Aaron. Do NSDateComponents to calculate the time for three days instead!
First you have to compute the start of the current day (i.e. today at 00.00).
Otherwise, the current day will not count as a full day when computing the
difference between today and the start of the next month.
NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *startOfToday;
[cal rangeOfUnit:NSCalendarUnitDay startDate:&startOfToday interval:NULL forDate:now];
Computing the start of the next month can be done with rangeOfUnit:...
(using a "statement expression" to be fancy :)
NSDate *startOfNextMonth = ({
NSDate *startOfThisMonth;
NSTimeInterval lengthOfThisMonth;
[cal rangeOfUnit:NSCalendarUnitMonth startDate:&startOfThisMonth interval:&lengthOfThisMonth forDate:now];
[startOfThisMonth dateByAddingTimeInterval:lengthOfThisMonth];
});
And finally the difference in days:
NSDateComponents *comp = [cal components:NSCalendarUnitDay fromDate:startOfToday toDate:startOfNextMonth options:0];
if (comp.day < 4) {
// ...
}
I am looking to display the amount of months from an NSDate object.
//Make Date Six Months In The Future
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *sixMonthsFromNow = [[NSDateComponents alloc] init];
[sixMonthsFromNow setMonth:6];
NSDate *finishDate = [calendar dateByAddingComponents:sixMonthsFromNow toDate:[NSDate date] options:0]; //Six Months Time
//Display
NSCalendarUnit requiredFormat = NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *dateComponents = [calendar components:requiredFormat fromDate:[NSDate date] toDate:finishDate options:0];
NSLog(#"%d months %d days %d hours %d minutes %d seconds", [dateComponents month], [dateComponents day], [dateComponents hour], [dateComponents minute], [dateComponents second]);
This outputs: 5 months 29 days 23 hours 59 minutes 59 seconds
Which is great, but I only wish to display the amount of months.
If I limit to only months:
//Make Date Six Months In The Future
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *sixMonthsFromNow = [[NSDateComponents alloc] init];
[sixMonthsFromNow setMonth:6];
NSDate *finishDate = [calendar dateByAddingComponents:sixMonthsFromNow toDate:[NSDate date] options:0]; //Six Months Time
//Display
NSCalendarUnit requiredFormat = NSMonthCalendarUnit;
NSDateComponents *dateComponents = [calendar components:requiredFormat fromDate:[NSDate date] toDate:finishDate options:0];
NSLog(#"%d months", [dateComponents month]);
This outputs: 5 months
Although this is technically correct, I would like to round the amount of days to make it output six months.
Is there an easy way to achieve this effect? I noticed there wasn't a rounding property on NSDateComponents. Will I have to manually check the amount of days and decide to round up?
My end goal is to not only limit the rounding effect to months, this should be able to round hours to days if i only supplied: NSDayCalendarUnit
The following method could to what you want.
The idea is that after computing the (rounded down) number of calendar units
between start date and end date, add both that amount and one more to the start date
and check which one is closer to the end date:
#interface NSCalendar (MyCategory)
-(NSInteger)roundedUnit:(NSCalendarUnit)unit fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
#end
#implementation NSCalendar (MyCategory)
-(NSInteger)roundedUnit:(NSCalendarUnit)unit fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
// Number of units between the two dates:
NSDateComponents *comps = [self components:unit fromDate:fromDate toDate:toDate options:0];
NSInteger value = [comps valueForComponent:unit];
// Add (value) units to fromDate:
NSDate *date1 = [self dateByAddingComponents:comps toDate:fromDate options:0];
// Add (value + 1) units to fromDate:
[comps setValue:(value + 1) forComponent:unit];
NSDate *date2 = [self dateByAddingComponents:comps toDate:fromDate options:0];
// Now date1 <= toDate < date2. Check which one is closer,
// and return the corresponding value:
NSTimeInterval diff1 = [toDate timeIntervalSinceDate:date1];
NSTimeInterval diff2 = [date2 timeIntervalSinceDate:toDate];
return (diff1 <= diff2 ? value : value + 1);
}
#end
And you would use it as
NSInteger months = [calendar roundedUnit:NSMonthCalendarUnit fromDate:[NSDate date] toDate:finishDate];
The code uses utility methods for NSDateComponents from
https://github.com/henrinormak/NSDateComponents-HNExtensions/blob/master/README.md:
- (void)setValue:(NSInteger)value forComponent:(NSCalendarUnit)unit;
- (NSInteger)valueForComponent:(NSCalendarUnit)unit;
These methods are new in OS X 10.9, but not available in iOS 7.
As the other commenter pointed out, you're calling NSDate twice, and the second date is slightly later than the first. Use the same date object twice and you won't get rounding errors.
As for how to round:
I don't think there is any rounding built into NSCalendars calendrical calculation methods.
You need to decide what that means to you. You might round at the halfway point. In that case, you could ask the calendar how many days there are in the current month, using the rangeOfUnit:inUnit:forDate method, and then add half that many days to the end date, then ask for the month. If you only want to "round up within a week, then only add a week to the end date before asking for the number of months difference.
I've got a similar problem:
let fmt1 = NSDateComponentsFormatter()
fmt1.allowedUnits = .CalendarUnitYear |
.CalendarUnitMonth |
.CalendarUnitDay
let dc1 = NSDateComponents()
dc1.month = 1
dc1.day = 15
// This occasionally outputs "1 month, 14 days" instead of
// "1 month, 15 days" when the formatter is constrained to
// years, months and days. If formatter is allowed to use
// all units, the output is "1 month, 14 days, 23 hours,
// 59 minutes, 59 seconds".
fmt1.stringFromDateComponents(dc1)
This can be fixed by specifying a positive amount of seconds and combining it with
setting maximumUnitCount formatter parameter to a fixed value:
let fmt2 = NSDateComponentsFormatter()
fmt2.allowedUnits = .CalendarUnitYear |
.CalendarUnitMonth |
.CalendarUnitDay
fmt2.maximumUnitCount = 2
fmt2.collapsesLargestUnit = true
let dc2 = NSDateComponents()
dc2.month = 1
dc2.day = 15
dc2.second = 10
// This always outputs "1 month, 15 days"
fmt2.stringFromDateComponents(dc2)
This fix will probably work in your case too: just assign some positive amount of seconds to NSDateComponents before calling dateByAddingComponents:
[sixMonthsFromNow setMonth: 6];
[sixMonthsFromNow setSecond: 10];