I was wondering how I could use NSDate within an if statement - I want to update a UILabel depending what the date is, currently I have the following code to determine the date but don't know how to actually get this within an if statement.
NSDate* date = [NSDate date];
NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"dd/MM/yyyy"];
NSString *dateString = [formatter stringFromDate:date];
dateLabel.text = dateString;
if (dateString == #"25/05/2012") {
NSLog(#"It's the 25th!");
}
else {
NSLog(#"Not it's not...");
}
Thanks a lot!
If you want to compare two strings use isEqualToString:
if ([dateString isEqualToString:#"25/05/2012"]) {
NSLog(#"It's the 25th!");
}
else {
NSLog(#"Not it's not...");
}
isEqualToString: in NSString class reference
if you want to compare two NSDate use isEqualToDate:
[date1 isEqualToDate:date2]
isEqualToDate: in NSDate class reference
To compare strings you can use isEqualToString: not == (with this you're comparing the pointers).
To compare dates you can use isEqualToDate:.
Depending on what you actually want to achieve you can use next calls:
NSDate:
- (BOOL)isEqualToDate:(NSDate *)anotherDate
- (NSComparisonResult)compare:(NSDate *)anotherDate
NSString:
- (BOOL)isEqualToString:(NSString *)aString
As has been noted, == checks for pointer equality. If you want to match the contents of an NSString or NSDate, or any other class of object, check that class' documentation for isEqual... and compare... methods. (NSDate has isEqualToDate: and compare:; NSString has isEqualToString: and compare: as well as several more specialized comparison methods.
However, depending on just what your aim is in matching dates, comparing NSDates with isEqualToDate: or checking the result of compare: against NSOrderedSame might not do what you want. An NSDate doesn't represent a whole calendar day but rather a specific moment in time. So if you have an NSDate representing 5/25/2012 1:53:13 AM PDT and one representing 5/25/2012 1:55:01 AM PDT, they will be equal or compare as NSOrderedSame.
What you're going for with the NSDateFormatter and string comparison will sort of work for checking whether two NSDates represent the same calendar day (presuming you compare string contents with isEqualToString: instead of using ==), but it's sort of kludgy and will fail in certain edge cases that are more common than you think. Apple provides APIs specifically for such comparisons; there's a section in the Date and Time Programming Guide that explains them well.
NSDate provides
- (BOOL)isEqualToDate:(NSDate *)anotherDate
- (NSComparisonResult)compare:(NSDate *)anotherDate
Here is a category (found here http://webd.fr/637-comparer-deux-nsdate) that offers a neat way to compare NSDates:
#import <Foundation/Foundation.h>
#interface NSDate (Compare)
-(BOOL) isLaterThanOrEqualTo:(NSDate*)date;
-(BOOL) isEarlierThanOrEqualTo:(NSDate*)date;
-(BOOL) isLaterThan:(NSDate*)date;
-(BOOL) isEarlierThan:(NSDate*)date;
//- (BOOL)isEqualToDate:(NSDate *)date; already part of the NSDate API
#end
And the implementation:
#import "NSDate+Compare.h"
#implementation NSDate (Compare)
-(BOOL) isLaterThanOrEqualTo:(NSDate*)date {
return !([self compare:date] == NSOrderedAscending);
}
-(BOOL) isEarlierThanOrEqualTo:(NSDate*)date {
return !([self compare:date] == NSOrderedDescending);
}
-(BOOL) isLaterThan:(NSDate*)date {
return ([self compare:date] == NSOrderedDescending);
}
-(BOOL) isEarlierThan:(NSDate*)date {
return ([self compare:date] == NSOrderedAscending);
}
#end
Simple to use:
if([aDateYouWant ToCompare isEarlierThanOrEqualTo:[NSDate date]]) // [NSDate date] is now
{
// do your thing ...
}
Related
I have one method that I use in many places throughout my project that looks like the following:
-(void)showSignInView
{
if(check for time interval)
[[self superview] addSubview:loginController.view];
}
I'd like to note the first time that this method is called, then on every subsequent call of this method check to make sure that the interval has been more than 15 minutes from the original call. Only then will it execute the rest of its code.
I know that you can use NSDate to measure time intervals using code like the following:
NSDate *firstTime = [[NSDate date] retain];
NSDate *SecondTime = [NSDate date];
NSLog(#"Time elapsed: %f", [SecondTime timeIntervalSinceDate:firstTime]);
but I'm not sure how to implement the initial time check, then subsequent comparisons to that time. How can I do this?
Create a property named previousTime.
#property(nonatomic, retain) NSDate *previousTime;
And create a method to find the time difference.
- (NSTimeInterval)timeDifferenceSinceLastOpen {
if (!previousTime) self.previousTime = [NSDate date];
NSDate *currentTime = [NSDate date];
NSTimeInterval timeDifference = [currentTime timeIntervalSinceDate:previousTime];
self.previousTime = currentTime;
return timeDifference;
}
You could use GCD to achieve this. The dispatch_once() function can arrange that a block is only executed once in the lifetime of your app.
NSDate *firstTime = nil;
- (void)loadView {
[self calculateTime:[NSDate dateWithTimeIntervalSince1970:1312996898]];
}
- (void)calculateTime:(NSDate*)secondTime
{
double offset = [secondTime timeIntervalSinceDate:[self getFirstTime]];
if (offset >= 900.0) {
NSLog(#"15 min gone");
}
}
- (NSDate *)getFirstTime
{
if (!firstTime) {
firstTime = [[NSDate date] retain];
}
return firstTime;
}
I know that there is a data type called NSTimeInterval, but this is in seconds. I want to have an object representation that would be able to represent a time range, say Thursday 21 June 8:00 - Thursday 21 June 9:00. Later on I want to compare the current date/time and check whether it fits within this range. What is the best way to do this?
I would suggest using two NSDate objects to store the start and end dates. You can easily determine if a date is between them using the timeIntervalSinceDate: method:
- (BOOL)dateInInterval:(NSDate *)testDate {
// date1 is the instance variable containing the starting date
// date2 is the instance variable containing the ending date
return ([testDate timeIntervalSinceDate:date1] > 0 &&
[testDate timeIntervalSinceDate:date2] < 0);
}
You just need to make a class which holds two NSDate objects, making sure the first is before the second, and including this method.
FYI, NSTimeInterval is not a class, its a typedef of double.
Edit
Since you want use these as keys for a dictionary, you could use something similar to this to store and search your data:
#protocol IntervalDictionaryKey <NSObject>
// The class you use as keys for your dictionary must implement this method to determine if a object is in the interval
- (BOOL)intervalContains:(id)object;
#end
#interface IntervalDictionary : NSObject {
NSMutableArray *keys, *values;
}
- (void)addInterval:(id<IntervalDictionaryKey>)interval withObject:(id)object;
- (void)setObject:(id)object forIntervalOf:(id)intervalObject;
- (id)objectForIntervalOf:(id)object;
#end
#implementation IntervalDictionary
- (id)init {
if(self = [super init]) {
keys = [NSMutableArray new];
values = [NSMutableArray new];
}
return self;
}
- (void)dealloc {
[keys release];
[values release];
[super dealloc];
}
- (void)addInterval:(id<IntervalDictionaryKey>)interval withObject:(id)object {
[keys addObject:interval];
[values addObject:object];
}
- (void)setObject:(id)object forIntervalOf:(id)intervalObject {
id<IntervalDictionaryKey> key;
NSUInteger i = 0;
for(key in keys) {
if([key intervalContains:intervalObject]) {
[values replaceObjectAtIndex:i withObject:object];
break;
}
++i;
}
}
- (id)objectForIntervalOf:(id)object {
id<IntervalDictionaryKey> key;
NSUInteger i = 0;
for(key in keys) {
if([key intervalContains:object]) {
return [values objectAtIndex:i];
}
++i;
}
}
#end
Usage:
Example interval class:
#interface DateInterval : NSObject <IntervalDictionaryKey> {
NSDate *date1, *date2;
}
- (BOOL)intervalContains:(NSDate *)date; // this is the same as the dateInInterval method above
#end
#implementation DateInterval
// initializer which sets date1 and date2
- (BOOL)intervalContains:(NSDate *)date {
return ([date timeIntervalSinceDate:date1] > 0 &&
[date timeIntervalSinceDate:date2] < 0);
}
#end
Example usage code:
//intervalX is a DateInterval object, created elsewhere
//objectX is any object, created elsewhere
//objectInX is a NSDate which is part of intervalX, created elsewhere
IntervalDictionary *dict = [IntervalDictionary new];
[dict addInterval:interval0 withObject:object0];
[dict addInterval:interval1 withObject:object1];
[dict objectForIntervalOf:objectIn0]; // returns object0
[dict objectForIntervalOf:objectIn1]; // returns object1
[dict setObject:object2 forIntervalOf:objectIn1]; // changes the object for interval1 to object2
[dict objectForIntervalOf:objectIn1]; // now returns object2
NSDateComponents can be used to store components of time intervals as well as date components. You can add such an object to an NSDate using NSCalendar's dateByAddingComponents:toDate:options:.
From iOS10+ you have NSDateInterval (See https://developer.apple.com/documentation/foundation/nsdateinterval?language=objc).
Is the best data structure to represent a time interval.
I am trying to compare two NSDates one is created by the viewDidLoad method and the other by clicking a button. I want to be able to click the button and find the time difference since the viewDidLoad was ran. I keep getting a difference of nil. Any Ideas?
#import "TimeViewController.h"
id startTime;
#implementation TimeViewController
- (void)viewDidLoad {
NSDate *startTime = [NSDate date];
NSLog(#"startTime = %#",startTime);
}
- (IBAction)buttonPressed{
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
double timeInterval = [now timeIntervalSinceDate:startTime];
NSLog(#"time difference = %#",[NSString stringWithFormat:#"%g",timeInterval]);
}
You have
id startTime;
in the global scope, and also
NSDate *startTime = [NSDate date];
inside viewDidLoad. The second statement creates a local variable called startTime, which hides the global variable. Use
startTime=[[NSDate date] retain];
instead.
That said, I'd suggest you not to create the global variable. Instead, make it an instance variable and a property:
#interface TimeViewController :NSObject{
....
NSDate*startDate;
}
...
#end
and as Kubi said, don't forget
-(void)dealloc{
[startDate release];
[super dealloc];
}
I'd also suggest not to use id to hold a known object. Who told you that? That's a very bad practice. Even when you declare a global variable, you should use
NSDate*startDate;
so that the compiler can warn you against non-defined methods.
This is more a community sharing post than a real question.
In my iPhone OS projects I'm always importing a helper class with helpful methods which I can use for about every project.
So I thought it might be a good idea, if everyone shares some of their favorite methods, which should have been in everyones toolcase.
I'll start with an extension of the NSString class, so I can make strings with dates on the fly providing format and locale. Maybe someone can find some need in this.
#implementation NSString (DateHelper)
+(NSString *) stringWithDate:(NSDate*)date withFormat:(NSString *)format withLocaleIdent:(NSString*)localeString{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
//For example #"de-DE", or #"en-US"
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:localeString];
[dateFormatter setLocale:locale];
// For example #"HH:mm"
[dateFormatter setDateFormat:format];
NSString *string = [dateFormatter stringFromDate:date];
[dateFormatter release];
[locale release];
return string;
}
#end
I'd love to see some of your tools.
#implementation UIDevice (OrientationAddition)
- (UIInterfaceOrientation)vagueOrientation {
if ([[UIApplication sharedApplication] statusBarFrame].size.height > 40){
return UIInterfaceOrientationLandscapeLeft;
}
return UIInterfaceOrientationPortrait;
}
#end
I use it for determining orientation before anything else is called, also in this:
#implementation UIScreen (BoundsAddition)
- (CGRect)actualBounds {
CGRect fakeBounds = self.bounds;
if (UIInterfaceOrientationIsPortrait([[UIDevice currentDevice] vagueOrientation])){
return fakeBounds;
}
return CGRectMake(0, 0, fakeBounds.size.height, fakeBounds.size.width);
}
#end
Which returns bounds based on orientation.
#implementation NSTimer (ECC)
-(void) suspend:(BOOL)inSuspend {
[self setFireDate:inSuspend ? [NSDate distantFuture] : [NSDate dateWithTimeIntervalSinceNow:[self timeInterval]]];
}
#end
How do you get a datetime column in SQLite with Objective C?
I have a table with 4 fields: pk, datetime, value1 and value2. pk (primary key), value1 and value2 are integers so I am using:
int value1 = sqlite3_column_int(statement, 2);
int value1 = sqlite3_column_int(statement, 3);
But what should I use for datetime?
In SQLite, there is no date/time column type per se, so one ends up representing dates either as Julian date values (real columns) or in strings (text columns). SQLite is also very particular in how dates are represented in strings, yyyy-MM-dd HH:mm:ss (only).
These are some methods that I wrote for working with SQLite dates from Objective-C. These methods are implemented in a category on NSDate.
Be sure to check out the functionality that SQLite offers for working with Julian dates. I have found these to be quite useful (http://www.sqlite.org/lang_datefunc.html). A function for deriving an NSDate's julianDay is included in the code example.
It looks like this subject was also covered here.
Persisting Dates to SQLite3 in an iPhone Application
+ (NSDate *) dateWithSQLiteRepresentation: (NSString *) myString;
{
NSAssert3(myString, #"%s: %d; %s; Invalid argument. myString == nil", __FILE__, __LINE__, __PRETTY_FUNCTION__);
return [[self sqlLiteDateFormatter] dateFromString: myString];
}
+ (NSDate *) dateWithSQLiteRepresentation: (NSString *) myString timeZone: (NSString *) myTimeZone;
{
NSString * dateWithTimezone = nil;
NSDate * result = nil;
NSAssert3(myString, #"%s: %d; %s; Invalid argument. myString == nil", __FILE__, __LINE__, __PRETTY_FUNCTION__);
NSAssert3(myTimeZone, #"%s: %d; %s; Invalid argument. myTimeZone == nil", __FILE__, __LINE__, __PRETTY_FUNCTION__);
dateWithTimezone = [[NSString alloc] initWithFormat: #"%# %#", myString, myTimeZone];
result = [[self sqlLiteDateFormatterWithTimezone] dateFromString: dateWithTimezone];
[dateWithTimezone release];
return result;
}
+ (NSString *) sqlLiteDateFormat;
{
return #"yyyy-MM-dd HH:mm:ss";
}
+ (NSString *) sqlLiteDateFormatWithTimeZone;
{
static NSString * result = nil;
if (!result) {
result = [[NSString alloc] initWithFormat: #"%# zzz", [self sqlLiteDateFormat]];
}
return result;
}
+ (NSDateFormatter *) sqlLiteDateFormatter;
{
static NSDateFormatter * _result = nil;
if (!_result) {
_result = [[NSDateFormatter alloc] init];
[_result setDateFormat: [self sqlLiteDateFormat]];
}
return _result;
}
+ (NSDateFormatter *) sqlLiteDateFormatterWithTimezone;
{
static NSDateFormatter * _result = nil;
if (!_result) {
_result = [[NSDateFormatter alloc] init];
[_result setDateFormat: [self sqlLiteDateFormatWithTimeZone]];
}
return _result;
}
- (NSString *) sqlLiteDateRepresentation;
{
NSString * result = nil;
result = [[NSDate sqlLiteDateFormatter] stringFromDate: self];
return result;
}
- (NSTimeInterval) unixTime;
{
NSTimeInterval result = [self timeIntervalSince1970];
return result;
}
#define SECONDS_PER_DAY 86400
#define JULIAN_DAY_OF_ZERO_UNIX_TIME 2440587.5
- (NSTimeInterval) julianDay;
{
return [self unixTime]/SECONDS_PER_DAY + JULIAN_DAY_OF_ZERO_UNIX_TIME;
}
+ (NSDate *) dateWithJulianDay: (NSTimeInterval) myTimeInterval
{
NSDate *result = [self dateWithTimeIntervalSince1970: (myTimeInterval - JULIAN_DAY_OF_ZERO_UNIX_TIME) * SECONDS_PER_DAY];
return result;
}
If you can define the database, then ou could also use REAL (SQLite data type) as the type for the datetime, then load it with sqlite3_column_double(). This will return a variable of the type double.
Then you can use [NSDate dateWithTimeIntervalSince1970:double_value] to get an NSDate object.
Please note that the category solution above has a problem in that it is subject to the locale settings on the user's device. For example, for midnight April 5th 2010 sqlLiteDateRepresentation above would return 2010/04/05 00:00:00 for most people's machines, however I have encountered a scenario where a user's locale settings caused the same function to produce "2010/04/05 12:00:00 a.m." which when used in my query does not return any rows. This seems to follow from the documentation of NSDateFormatter: "In general, you are encouraged to use format styles (see timeStyle, dateStyle, and NSDateFormatterStyle) rather than using custom format strings, since the format for a given style reflects a user’s preferences. Format styles also reflect the locale setting." Although I didn't see a good way to use the timeStyle/dateStyle to get the same format as yyyy/MM/dd HH:mm:ss that SQLite seems to need. I fear your best bet is likely a custom solution where you ensure that the time is definitely written in 24H format, don't allow locale settings to cause bugs.
If you just want to get an NSDate from an SQLite datetime field, where dateTime is the field returned from the database:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *date = [dateFormatter dateFromString:dateTime];
Now Work!
NSString *dateValueS = [[NSString alloc]
initWithUTF8String:(char*) sqlite3_column_text(statement,2)];