How to get the difference between two dates? - cocoa-touch

I have two dates in format(MM/dd/yyyy hh:mm:ss:SS). For the both dates I have converted the two dates to strings by using (stringFromDate) method. But I could not get the difference between them and show them in my console. Please give me an idea how I should get it?
Thank you.

Example
NSDate *today = [NSDate date];
NSTimeInterval dateTime;
if ([visitDate isEqualToDate:today]) //visitDate is a NSDate
{
NSLog (#"Dates are equal");
}
dateTime = ([visitDate timeIntervalSinceDate:today] / 86400);
if(dateTime < 0) //Check if visit date is a past date, dateTime returns - val
{
NSLog (#"Past Date");
}
else
{
NSLog (#"Future Date");
}

Keep the dates as dates, get the difference between them, then print the difference.
From the docs on NSCalendar and assuming gregorian is an NSCalendar:
NSDate *startDate = ...;
NSDate *endDate = ...;
unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *comps = [gregorian components:unitFlags fromDate:startDate toDate:endDate options:0];
int months = [comps month];
int days = [comps day];

Generally I see day delta calculations handled by converting day/year values into flat days (usually days since some starting epoch, like 01/01/1970).
To aid in this, I've found it helpful to create a table of days into the year that each month starts. Here's a class I used for this recently.
namespace {
// Helper class for figuring out things like day of year
class month_database {
public:
month_database () {
days_into_year[0] = 0;
for (int i=0; i<11; i++) {
days_into_year[i+1] = days_into_year[i] + days_in_month[i];
}
};
// Return the start day of the year for the given month (January = month 1).
int start_day (int month, int year) const {
// Account for leap years. Actually, this doesn't get the year 1900 or 2100 right,
// but should be good enough for a while.
if ( (year % 4) == 0 && month > 2) {
return days_into_year[month-1] + 1;
} else {
return days_into_year[month-1];
}
}
private:
static int const days_in_month[12];
// # of days into the year the previous month ends
int days_into_year[12];
};
// 30 days has September, April, June, and November...
int const month_database::days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
month_database month;
}
As you can see from the start_day method, the main issue you are going to be wrestling with is how many leap days are contained in your range. Within our epoch the calculation I used there is good enough. The actual rule for which years contain leap days is discussed here.
February 29 in the Gregorian calendar,
the most widely used today, is a date
that occurs only once every four
years, in years evenly divisible by 4,
such as 1976, 1996, 2000, 2004, 2008,
2012 or 2016 (with the exception of
century years not divisible by 400,
such as 1900).

If you just want the difference in days, you can do this. (Based on mihir mehta's answer.)
const NSTimeInterval kSecondsPerDay = 60 * 60 * 24;
- (NSInteger)daysUntilDate:(NSDate *)anotherDate {
NSTimeInterval secondsUntilExpired = [self timeIntervalSinceDate:anotherDate];
NSTimeInterval days = secondsUntilExpired / kSecondsPerDay;
return (NSInteger)days;
}

Related

Why is Excel serial date-time to NSDate conversion two days, one hour and 40 minutes ahead?

I'm converting dates from an Excel spreadsheet to NSDate's, but for some reason they always come out two days ahead: Sundays come out as Tuesdays, etc.
My conversion method is based on the following info from cpearson.com:
Excel stores dates and times as a number representing the number of
days since 1900-Jan-0, plus a fractional portion of a 24 hour day:
ddddd.tttttt . This is called a serial date, or serial date-time.
(...) The integer portion of the number, ddddd, represents the number
of days since 1900-Jan-0. (...) The fractional portion of the number,
ttttt, represents the fractional portion of a 24 hour day. For
example, 6:00 AM is stored as 0.25, or 25% of a 24 hour day.
Similarly, 6PM is stored at 0.75, or 75% percent of a 24 hour day.
- (NSDate *)dateFromExcelSerialDate:(double)serialdate
{
if (serialdate == 0)
return nil;
NSTimeInterval theTimeInterval;
NSInteger numberOfSecondsInOneDay = 86400;
double integral;
double fractional = modf(serialdate, &integral);
NSLog(#"%# %# \r serialdate = %f, integral = %f, fractional = %f",
[self class], NSStringFromSelector(_cmd),
serialdate, integral, fractional);
theTimeInterval = integral * numberOfSecondsInOneDay; //number of days
if (fractional > 0) {
theTimeInterval += numberOfSecondsInOneDay / fractional; //portion of one day
}
NSCalendar *nl_gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSTimeZone *nl_timezone = [[NSTimeZone alloc] initWithName:#"Europe/Amsterdam"];
[nl_gregorianCalendar setTimeZone:nl_timezone];
NSDateComponents *excelBaseDateComps = [[NSDateComponents alloc] init];
[excelBaseDateComps setMonth:1];
[excelBaseDateComps setDay:1];
[excelBaseDateComps setHour:00];
[excelBaseDateComps setMinute:00];
[excelBaseDateComps setTimeZone:nl_timezone];
[excelBaseDateComps setYear:1900];
NSDate *excelBaseDate = [nl_gregorianCalendar dateFromComponents:excelBaseDateComps];
NSDate *inputDate = [NSDate dateWithTimeInterval:theTimeInterval sinceDate:excelBaseDate];
NSLog(#"%# %# \r serialdate %f, theTimeInterval = %f \r inputDate = %#",
[self class], NSStringFromSelector(_cmd),
serialdate, theTimeInterval,
[self.nl_dateFormatter stringFromDate:inputDate]);
return inputDate;
}
The spreadsheet was produced in the Netherlands, presumably on a Dutch version of Microsoft Excel.
Spreadsheet date Sunday July 6, 2014 00:00 yields the following results:
dateFromExcelSerialDate:
serialdate = 41826.000000, integral = 41826.000000, fractional =
0.000000 theTimeInterval = 3613766400.000000 inputDate = 08 jul. 2014 01:40
Similarly, Sunday July 13, 2014 00:00 yields:
serialdate = 41833.000000, integral = 41833.000000, fractional =
0.000000 theTimeInterval = 3614371200.000000 inputDate = 15 jul. 2014 01:40
I can correct the output by subtracting 2 days, one hour and 40 minutes:
theTimeInterval -= ((60 * 60 * 24 * 2) + (60*60) + (60*40));
but I have no idea how robust that is.
That difference of two days made me think it had something to do with leap year corrections, so I tried to let the calendar do the calculations by adding the NSTimeInterval seconds to the excelBaseDate, like so:
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setSecond:theInterval];
NSDate *inputDate = [nl_gregorianCalendar dateByAddingComponents:comps
toDate:excelBaseDate
options:0];
Strangely enough, that gave me dates somewhere in the 1870's. Who knows what is going on?
there are two things here:
your start date is 1900-Jan-1 but your referred description clearly says: the reference is 1900-Jan-0 – you may add an extra day here;
year 1900 was not a leap-year – you may add an extra day here;
I guess, this is pretty much the reason why you get two extra days every occasion.
Microsoft knows about that, see more about the topic here.

Is the iOS Do Not Disturb Bug Causing this Bug?

I am trying to find out if the recent Do Not Disturb bug (Ars Technica link) is affecting my code. I am trying to get the date and timestamp for midnight the previous Monday. Since I am writing this on a Monday, I would expect to get today's date (January 7, 2013), yet I am January 4, 2013.
I am following the guide posted by Apple, but I am trying to modify it to find the previous Monday, instead of Sunday.
+(NSTimeInterval)StartOfWeekMonday {
NSTimeInterval finalTime = -1;
NSInteger weekday;
NSDate *monday = 0;
NSCalendar *greg = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdays = [greg components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
NSDateComponents *subtract = [[NSDateComponents alloc] init];
NSDateComponents *final = 0;
weekday = ( weekdays.weekday == 1 ) ? 6 : weekdays.weekday;
[subtract setDay:(0 - weekday - 1)];
monday = [greg dateByAddingComponents:subtract toDate:[NSDate date] options:0];
final = [greg components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:monday];
monday = [greg dateFromComponents:final];
#ifdef DEBUG
NSLog( #"%s - %d -> Weekday: %d", __FILE__, __LINE__, weekday );
NSLog( #"%s - %d -> Monday: %#", __FILE__, __LINE__, [monday descriptionWithLocale:[NSLocale currentLocale]] );
#endif
finalTime = [monday timeIntervalSince1970];
return finalTime;
}
My log out put below, the weekday is correct (I am writing this on a Monday), yet the date is obviously wrong, it should be Monday: Monday, January 7, 2013, 12:00 AM
2013-01-07 15:07:33.792 MY-APP[5524:14c03] Weekday: 2
2013-01-07 15:07:36.757 MY-APP[5524:14c03] Monday: Friday, January 4, 2013, 12:00:00 AM Eastern Standard Time
This is being in the simulator right now, but all my other date calculations are giving the expected values.
Is it likely that I am being affected by the recent Do Not Disturb Bug and will have to wait for tomorrow (January 8, 2013) to see the expected results, or am I missing something entirely different?
The issue was with how to calculate the subtraction. Either I completely mis understood the example provided by Apple, or it was not clear enough. The solution for calculating the subtraction was as follows:
weekday = ( weekdays.weekday == 1 ) ? 8 : weekdays.weekday;
toSubtract = ( 2 - weekday );
[subtract setDay:toSubtract];
Running various tests produced the proper "Monday." In the end, this had nothing to do with the date bug affecting Do Not Disturb.

How to check if 2 Dates have a difference of 30days? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I calculate the difference between two dates?
NSDate, comparing two dates
I have got two dates. How to check if theres a difference of 30 days in between them?
I actually have an In-App purchase which needs to be disabled after every 30-days of purchase.
When the user buys the feature, the Date is saved and so I need to check the dates. If 30-days have passed, I need to disable the feature again.
You can convert both dates to seconds with timeIntervalSince1970 and then check if difference is bigger than 2592000 (30*24*60*60 which is 30 days * 24 hours * 60 minutes * 60 seconds).
NSTimeInterval difference = [date1 timeIntervalSince1970] - [date2 timeIntervalSince1970];
if(difference >2592000)
{
//do your stuff here
}
EDIT:
For more compact version you can use - (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate
NSTimeInterval difference = [date1 timeIntervalSinceDate:date2];
if(difference >2592000)
{
//do your stuff here
}
Provide the start and end NSDate in following manner:
NSDate *date_Start;
NSDate *date_End;
NSCalendar *cal=[NSCalendar currentCalendar];
NSDateComponents *components=[cal components:NSDayCalendarUnit fromDate:date_Start toDate:date_End options:0];
int days=[components day];
if(days>30){
//Your code here
}
You could create a date which is +30days from now :
NSDate *thrityDaysPlus = [[NSDate date] dateByAddingTimeInterval:3600*24*30]
and then simply compare it to your saved date
if ([date1 compare:date2] == NSOrderedDescending) {
NSLog(#"date1 is later than date2");
} else if ([date1 compare:date2] == NSOrderedAscending) {
NSLog(#"date1 is earlier than date2");
} else {
NSLog(#"dates are the same");
}

How to check if a date is into a range of dates with a specific repetition?

I'd like to create a simple pills reminder iOS application (let's leave notifications aside for the moment).
I want to create a single DB record for a pill that has repetitions and check if a specific day intersects date "generated" by the repetition.
Example:
I set a pills period that starts from April 12 and ends April 20, with repetition every 2 days , at 3.00 pm.
So this pills is valid for this dates :
April 12 at 3.00pm
April 14 at 3.00pm
April 16 at 3.00pm
April 18 at 3.00pm
April 20 at 3.00pm
Questions:
Which type of data can describe the information "Every 2 days"? NSDateInterval would be a good solution ?
How can i verify that a specific day agrees with my repetition scheme ? (i.e. Check if April 13 is a valid date for the previous example and get "NO" as answer)
Here is an example class which should meet your criteria:
main.m
#import <Foundation/Foundation.h>
#import "DateChecker.h"
int main(int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Specify a period in days
NSInteger period = 2;
// Set the start/end/current days. These can be retrieved from a database or elsewhere
NSDate *startDate = [NSDate dateWithString:#"2012-04-12 00:00:00 +0000"];
NSDate *endDate = [NSDate dateWithString:#"2012-04-20 00:00:00 +0000"];
NSDate *currentDate = [NSDate dateWithString:#"2012-04-14 00:00:00 +0000"];
// Actually do the checking. This could alternatively be offloaded to a function in DateChecker
if([DateChecker date:currentDate isBetweenDate:startDate andDate:endDate])
{
if([DateChecker date:currentDate fallsOnStartDate:startDate withInterval:period])
{
NSLog(#"The date %# falls in the specified date period & is on the interval date.", currentDate);
}
else
{
NSLog(#"The date %# falls in the specified date period & is NOT on the interval date.", currentDate);
}
}
else
{
NSLog(#"The date %# does not fall in the specified date period.", currentDate);
}
[pool release];
return 0;
}
DateChecker.h
#import <Foundation/Foundation.h>
#interface DateChecker : NSObject
{
}
+ (BOOL)date:(NSDate*)date fallsOnStartDate:(NSDate*)beginDate withInterval:(NSInteger)daysInterval;
+ (BOOL)date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
#end
DateChecker.m
#import "DateChecker.h"
#implementation DateChecker
+ (BOOL)date:(NSDate*)date fallsOnStartDate:(NSDate*)beginDate withInterval:(NSInteger)daysInterval
{
NSDateComponents *components;
NSInteger days;
// Get the number of days since the start date
components = [[NSCalendar currentCalendar] components: NSDayCalendarUnit fromDate: beginDate toDate: date options: 0];
days = [components day];
// If the number of days passed falls on the interval, then retrun YES
if(days % daysInterval == 0)
{
return YES;
}
else
{
return NO;
}
}
+ (BOOL)date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate
{
if ([date compare:beginDate] == NSOrderedAscending)
return NO;
if ([date compare:endDate] == NSOrderedDescending)
return NO;
return YES;
}
#end
That should give you an idea on how to write a class that can handle your requirements. I ran this as a command line app and it seemed to work alright. The function
+ (BOOL)date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate
was graciously obtained from How to Check if an NSDate occurs between two other NSDates.

Subtract 7 days from current date

It seems that I can't subtract 7 days from the current date. This is how i am doing it:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setDay:-7];
NSDate *sevenDaysAgo = [gregorian dateByAddingComponents:offsetComponents toDate:[NSDate date] options:0];
SevenDaysAgo gets the same value as the current date.
Please help.
EDIT: In my code I forgot to replace the variable which gets the current date with the right one. So above code is functional.
code:
NSDate *currentDate = [NSDate date];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
[dateComponents setDay:-7];
NSDate *sevenDaysAgo = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponents toDate:currentDate options:0];
NSLog(#"\ncurrentDate: %#\nseven days ago: %#", currentDate, sevenDaysAgo);
[dateComponents release];
output:
currentDate: 2012-04-22 12:53:45 +0000
seven days ago: 2012-04-15 12:53:45 +0000
And I'm fully agree with JeremyP.
BR.
Eugene
If you're running at least iOS 8 or OS X 10.9, there's an even cleaner way:
NSDate *sevenDaysAgo = [[NSCalendar currentCalendar] dateByAddingUnit:NSCalendarUnitDay
value:-7
toDate:[NSDate date]
options:0];
Or, with Swift 2:
let sevenDaysAgo = NSCalendar.currentCalendar().dateByAddingUnit(.Day, value: -7,
toDate: NSDate(), options: NSCalendarOptions(rawValue: 0))
And with Swift 3 and up it gets even more compact:
let sevenDaysAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date())
use dateByAddingTimeInterval method:
NSDate *now = [NSDate date];
NSDate *sevenDaysAgo = [now dateByAddingTimeInterval:-7*24*60*60];
NSLog(#"7 days ago: %#", sevenDaysAgo);
output:
7 days ago: 2012-04-11 11:35:38 +0000
Swift 3
Calendar.current.date(byAdding: .day, value: -7, to: Date())
Swift operator extension:
extension Date {
static func -(lhs: Date, rhs: Int) -> Date {
return Calendar.current.date(byAdding: .day, value: -rhs, to: lhs)!
}
}
Usage
let today = Date()
let sevenDayAgo = today - 7
Swift 4.2 - Mutate (Update) Self
Here is another way the original poster can get one week ago if he already has a date variable (updates/mutates itself).
extension Date {
mutating func changeDays(by days: Int) {
self = Calendar.current.date(byAdding: .day, value: days, to: self)!
}
}
Usage
var myDate = Date() // Jan 08, 2019
myDate.changeDays(by: 7) // Jan 15, 2019
myDate.changeDays(by: 7) // Jan 22, 2019
myDate.changeDays(by: -1) // Jan 21, 2019
or
// Iterate through one week
for i in 1...7 {
myDate.changeDays(by: i)
// Do something
}
dymv's answer work great. This you can use in swift
extension NSDate {
static func changeDaysBy(days : Int) -> NSDate {
let currentDate = NSDate()
let dateComponents = NSDateComponents()
dateComponents.day = days
return NSCalendar.currentCalendar().dateByAddingComponents(dateComponents, toDate: currentDate, options: NSCalendarOptions(rawValue: 0))!
}
}
You can call this with
NSDate.changeDaysBy(-7) // Date week earlier
NSDate.changeDaysBy(14) // Date in next two weeks
Hope it helps and thx to dymv
Swift 4.2 iOS 11.x Babec's solution, pure Swift
extension Date {
static func changeDaysBy(days : Int) -> Date {
let currentDate = Date()
var dateComponents = DateComponents()
dateComponents.day = days
return Calendar.current.date(byAdding: dateComponents, to: currentDate)!
}
}
Swift 3.0+ version of the original accepted answer
Date().addingTimeInterval(-7 * 24 * 60 * 60)
However, this uses absolute values only. Use calendar answers is probably more suitable in most cases.
Swift 5
Function to add or subtract day from current date.
func addOrSubtructDay(day:Int)->Date{
return Calendar.current.date(byAdding: .day, value: day, to: Date())!
}
Now calling the function
var dayAddedDate = addOrSubtructDay(7)
var daySubtractedDate = addOrSubtructDay(-7)
To Add date pass prospective day value
To Subtract pass negative day
value
Swift 3:
A modification to Dov's answer.
extension Date {
func dateBeforeOrAfterFromToday(numberOfDays :Int?) -> Date {
let resultDate = Calendar.current.date(byAdding: .day, value: numberOfDays!, to: Date())!
return resultDate
}
}
Usage:
let dateBefore = Date().dateBeforeOrAfterFromToday(numberOfDays : -7)
let dateAfter = Date().dateBeforeOrAfterFromToday(numberOfDays : 7)
print ("dateBefore : \(dateBefore), dateAfter :\(dateAfter)")
FOR SWIFT 3.0
here is fucntion , you can reduce days , month ,day by any count
like for example here , i have reduced the current system date's year by 100 year , you can do it for day , month also
just set the counter
and store it in an array , you can this array anywhere then
func currentTime()
{
let date = Date()
let calendar = Calendar.current
var year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let pastyear = year - 100
var someInts = [Int]()
printLog(msg: "\(day):\(month):\(year)" )
for _ in pastyear...year {
year -= 1
print("\(year) ")
someInts.append(year)
}
print(someInts)
}