Permalink
Browse files

Merged in Chris Wright's date format additions to FMDatabase.

  • Loading branch information...
1 parent 6740330 commit 61b6e19436c388b3556236c7f1a226b465bceedf @ccgus committed May 24, 2013
Showing with 144 additions and 3 deletions.
  1. +1 −0 .gitignore
  2. +3 −0 CHANGES_AND_TODO_LIST.txt
  3. +62 −1 src/FMDatabase.h
  4. +34 −1 src/FMDatabase.m
  5. +1 −1 src/FMResultSet.m
  6. +43 −0 src/fmdb.m
View
@@ -5,3 +5,4 @@ build
*.xcodeproj/xcuserdata
fmdb.xcodeproj/*.mode1v3
*.xcodeproj/project.xcworkspace/xcuserdata/
+fmdb.xcodeproj/project.xcworkspace
@@ -3,6 +3,9 @@ Zip, nada, zilch. Got any ideas?
If you would like to contribute some code- awesome! I just ask that you make it conform to the coding conventions already set in here, and to add a couple of tests for your new code to fmdb.m. And of course, the code should be of general use to more than just a couple of folks. Send your patches to gus@flyingmeat.com.
+2013.05.24
+ Merged in Chris Wright's date format additions to FMDatabase.
+
2013.04.17
Added two new methods to FMDatabase for setting crypto keys, which take NSData objects. Thanks to Phillip Kast for the patch! <https://github.com/ccgus/fmdb/pull/135>
View
@@ -63,6 +63,7 @@
NSMutableSet *_openResultSets;
NSMutableSet *_openFunctions;
+ NSDateFormatter *_dateFormat;
}
@@ -73,7 +74,6 @@
@property (atomic, assign) BOOL logsErrors;
@property (atomic, retain) NSMutableDictionary *cachedStatements;
-
+ (id)databaseWithPath:(NSString*)inPath;
- (id)initWithPath:(NSString*)inPath;
@@ -138,6 +138,67 @@
- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block;
+
+
+/** Generate an NSDateFormat that won't be broken by timezone or locale changes.
+
+ Use this method to generate values to set the dateFormat property.
+
+ @param dateFormat A valid NSDateFormatter format string.
+
+ Example:
+
+ myDB.dateFormat = [FMDatabase storeableDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+
+ Note that NSDateFormatter is not thread-safe, so the formatter generated by this method should be assigned to only one FMDB instance and should not be used for other purposes.
+
+ */
++ (NSDateFormatter *)storeableDateFormat:(NSString *)format;
+
+
+/** Test whether the database has a date formatter assigned.
+
+ */
+- (BOOL)hasDateFormatter;
+
+
+/** Set to a date formatter to use string dates with sqlite instead of the default UNIX timestamps.
+
+ Set to nil to use UNIX timestamps.
+
+ Defaults to nil.
+
+ Should be set using a formatter generated using FMDatabase::storeableDateFormat.
+
+ Note there is no direct getter for the NSDateFormatter, and you should not use the formatter you pass to FMDB for other purposes, as NSDateFormatter is not thread-safe.
+
+ */
+- (void)setDateFormat:(NSDateFormatter *)format;
+
+
+/** Convert the supplied NSString to NSDate, using the current database formatter.
+
+ Returns nil if no formatter is set.
+
+ */
+- (NSDate *)dateFromString:(NSString *)s;
+
+/** Convert the supplied NSDate to NSString, using the current database formatter.
+
+ Returns nil if no formatter is set.
+
+ */
+- (NSString *)stringFromDate:(NSDate *)date;
+
+
+
+
+
+
+
+
+
+
@end
@interface FMStatement : NSObject {
View
@@ -56,8 +56,10 @@ - (void)dealloc {
[self close];
FMDBRelease(_openResultSets);
FMDBRelease(_cachedStatements);
+ FMDBRelease(_dateFormat);
FMDBRelease(_databasePath);
FMDBRelease(_openFunctions);
+ FMDBRelease(_dateFormatLock);
#if ! __has_feature(objc_arc)
[super dealloc];
@@ -254,6 +256,34 @@ - (BOOL)setKeyWithData:(NSData *)keyData {
#endif
}
++ (NSDateFormatter *)storeableDateFormat:(NSString *)format {
+
+ NSDateFormatter *result = FMDBReturnAutoreleased([[NSDateFormatter alloc] init]);
+ result.dateFormat = format;
+ result.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
+ result.locale = FMDBReturnAutoreleased([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
+ return result;
+}
+
+
+- (BOOL)hasDateFormatter {
+ return _dateFormat != nil;
+}
+
+- (void)setDateFormat:(NSDateFormatter *)format {
+ FMDBAutorelease(_dateFormat);
+ _dateFormat = FMDBReturnRetained(format);
+}
+
+- (NSDate *)dateFromString:(NSString *)s {
+ return [_dateFormat dateFromString:s];
+}
+
+- (NSString *)stringFromDate:(NSDate *)date {
+ return [_dateFormat stringFromDate:date];
+}
+
+
- (BOOL)goodConnection {
if (!_db) {
@@ -373,7 +403,10 @@ - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC);
}
else if ([obj isKindOfClass:[NSDate class]]) {
- sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
+ if (self.hasDateFormatter)
+ sqlite3_bind_text(pStmt, idx, [[self stringFromDate:obj] UTF8String], -1, SQLITE_STATIC);
+ else
+ sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
}
else if ([obj isKindOfClass:[NSNumber class]]) {
View
@@ -299,7 +299,7 @@ - (NSDate*)dateForColumnIndex:(int)columnIdx {
return nil;
}
- return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]];
+ return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]];
}
View
@@ -7,6 +7,7 @@
#define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); abort(); } }
void testPool(NSString *dbPath);
+void testDateFormat();
void FMDBReportABugFunction();
int main (int argc, const char * argv[]) {
@@ -809,6 +810,7 @@ int main (int argc, const char * argv[]) {
testPool(dbPath);
+ testDateFormat();
@@ -1320,6 +1322,47 @@ void testPool(NSString *dbPath) {
/*
+ Test the date format
+ */
+
+void testOneDateFormat( FMDatabase *db, NSDate *testDate ) {
+ [db executeUpdate:@"DROP TABLE IF EXISTS test_format"];
+ [db executeUpdate:@"CREATE TABLE test_format ( test TEXT )"];
+ [db executeUpdate:@"INSERT INTO test_format(test) VALUES (?)", testDate];
+ FMResultSet *rs = [db executeQuery:@"SELECT test FROM test_format"];
+ if ([rs next]) {
+ NSDate *found = [rs dateForColumnIndex:0];
+ if (NSOrderedSame != [testDate compare:found]) {
+ NSLog(@"Did not get back what we stored.");
+ }
+ }
+ else {
+ NSLog(@"Insertion borked");
+ }
+ [rs close];
+}
+
+void testDateFormat() {
+
+ FMDatabase *db = [FMDatabase databaseWithPath:nil]; // use in-memory DB
+ [db open];
+
+ NSDateFormatter *fmt = [FMDatabase storeableDateFormat:@"yyyy-MM-dd HH:mm:ss"];
+
+ NSDate *testDate = [fmt dateFromString:@"2013-02-20 12:00:00"];
+
+ // test timestamp dates (ensuring our change does not break those)
+ testOneDateFormat(db,testDate);
+
+ // now test the string-based timestamp
+ [db setDateFormat:fmt];
+ testOneDateFormat(db, testDate);
+
+ [db close];
+}
+
+
+/*
What is this function for? Think of it as a template which a developer can use
to report bugs.

0 comments on commit 61b6e19

Please sign in to comment.