4

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?

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Undolog
  • 562
  • 2
  • 9
  • 20
  • 1
    Use a wrapper. There are some here: http://cocoaheads.byu.edu/resources/sqlite – Dave DeLong Nov 10 '09 at 22:01
  • This might be a duplicate of these questions: http://stackoverflow.com/questions/251155/persisting-dates-to-sqlite3-in-an-iphone-application , http://stackoverflow.com/questions/942188/sqlite-datetime-data-type-with-iphone-nsdate – Brad Larson Nov 11 '09 at 04:03

5 Answers5

13

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;
}
Community
  • 1
  • 1
xyzzycoder
  • 1,831
  • 13
  • 19
  • This is very usefull! However my problem is that when i try to use: NSString *dateValue = [NSString stringWithUTF8String:(char*) sqlite3_column_text(statement,1)]; iPhone goes in crash ! :( What wrong? – Undolog Nov 10 '09 at 22:13
  • Why are you casting to (char)? – xyzzycoder Nov 10 '09 at 22:19
  • +1, I wound up doing this. I did have to write my own user functions for adding/summing time periods. – J. Polfer Dec 03 '09 at 15:54
  • 1
    Just thought I should point out that `[NSDate dateWithNaturalLanguageString:]` wasn't provided, and you can actually use `[[NSDate date] timeIntervalSince1970]` instead of this unixTime method. – Matt Rix Jun 04 '11 at 17:37
  • @Matt - Thanks. Made the change. – xyzzycoder Jun 06 '11 at 21:33
  • @xyzzycoder I have saved the date value in DATETIME format,but as you mentioned,there is nothing like DATETIME in sqlite,I have made the change from date DATETIME to date VARCHAR(64).I would like to know the query for retrieving data from a particular month,say data from january/february/etc.. I have saved the format as @"@"yyyy-MM-dd HH:mm:ss"" only!Please suggest me query for retrieving data month wise,thanks :) – Eshwar Chaitanya Jan 04 '12 at 09:37
  • @EshwarChaitanya I'm not certain if I understand the question, but this is an example query that uses a subquery as an example table and extracts out the year, month, and day (and sorts them) from the returned datetime column. select strftime('%Y', faux_table.timestamp) as "Year", strftime('%m', faux_table.timestamp) as "Month", strftime('%d', faux_table.timestamp) as "Day" from (select datetime() as "timestamp") faux_table order by 1 asc, 2 asc, 3 asc – xyzzycoder Jan 08 '12 at 05:51
  • @xyzzycoder Yeah I found out the answer for the query anyway,here is the link http://stackoverflow.com/questions/8725685/retrieve-month-part-from-date-saved-in-sqlite-database-table-for-displaying-mont ,anyway thanks for the concern and answer :) – Eshwar Chaitanya Jan 08 '12 at 08:02
  • + (NSDate *) dateWithJulianDay: (NSTimeInterval) myTimeInterval { NSDate * result = [[NSDate date] timeIntervalSince1970]; return result; } //hadn't compiled – Applicasa iOS developer Jul 23 '12 at 12:03
3

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.

Kobski
  • 1,636
  • 15
  • 27
  • This is strange, because the official SQLite documentation says the following about using REAL numbers to store dates: _REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar._ [http://www.sqlite.org/datatype3.html] – Peter Boné Nov 20 '13 at 11:41
0

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];
AndyDunn
  • 1,074
  • 1
  • 12
  • 19
0

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.

Robert Hawkey
  • 761
  • 7
  • 12
  • What changes do you recommend to fix the category? – xyzzycoder Jun 01 '11 at 00:03
  • Sorry I didn't see this for so long, I actually was able to cheat in my circumstance because for all of my queries the time was irrelevant, only the date mattered. So I was able to hardcode my strings to end with 00:00:00. Obviously this is no good for a generic category. The category would need to parse the string, check for the presence of a.m. or p.m., if found separate the string by ' ', then manipulate the time string to convert from 12H to 24H format and rebuild the string. – Robert Hawkey Sep 14 '11 at 18:20
-5

Now Work!

NSString *dateValueS = [[NSString alloc] 
     initWithUTF8String:(char*) sqlite3_column_text(statement,2)];
Undolog
  • 562
  • 2
  • 9
  • 20
  • 2
    Why do you accept your own answer as an answer when it's not really the answer? What you have pasted here is just a line to read a string value from a db column. – jbasko Feb 25 '10 at 01:14