0

I'm working on an app in which users can log in and select a site which may be in a different time zone. Because the app is showing data that is relevant to that site I've decided to set the default timezone for the app to be the site timezone via NSTimeZone.setDefaultTimeZone. This works great except when I select data out of our sqlite db via FMDB (I don't think FMDB has anything to do with it) and use strftime with 'localtime'. While our data is stored by the minute in UTC using epochs, we often need to show summations by day, weekday or month in the local time zone. For example to get averages by weekday:

select strftime('%w',datetime(epoch,'localtime')),avg(value) 
    from values 
    where siteId = 'x'  
    group by 1 
    order by 2 desc

The 'localtime' that it's using is the system local time and not the default time zone for the app where all NSDate calls respect the default time zone. There does not to be any other timezone options for strftime other than localtime and UTC and the current work arounds are pretty slow requiring multiple SQL roundtrips where this should easily be handled in 1 query as above.

Can anyone tell me how sqlite on iOS determines 'localtime'? Is there a way to force it to use a different i.e. defaultTimeZone?

1 Answer 1

0

As you have seen, SQLite doesn't use NSDate or the app's local timezone. The datetime function converts with a Modifier. Say you had a DB that stores as GMT (I think this is the case for your app):

sqlite> create table mytable (id int, time datetime);
sqlite> insert into mytable values (1, CURRENT_TIMESTAMP);
sqlite> select time from mytable;
2016-06-24 19:05:36  <-  THIS IS GMT
sqlite> select datetime(time, 'localtime') from mytable;
2016-06-24 15:05:36  <- THIS IS LOCAL TIME

In this example (and yours) 'localtime' is the Modifier. From the sqlite docs:

Modifiers

The time string can be followed by zero or more modifiers that alter date and/or time. Each modifier is a transformation that is applied to the time value to its left. Modifiers are applied from left to right; order is important. The available modifiers are as follows.

  1. NNN days
  2. NNN hours
  3. NNN minutes
  4. NNN.NNNN seconds
  5. NNN months
  6. NNN years
  7. start of month
  8. start of year
  9. start of day
  10. weekday N
  11. unixepoch
  12. localtime
  13. utc

So you cannot directly convert to the local value. However, because you can use these modifers your app can get your local GMT offset from NSDate:

if let myZone = NSTimeZone(abbreviation: "EST")
{
    NSTimeZone.setDefaultTimeZone(myZone)
    var offset = (myZone.secondsFromGMT)/3600 as Int
    var offsetModifer = "\(offset) hours"
}

Then you can execute your sqlite query as so (building the query using offsetModifer which translates to -4 hours in the example here:

sqlite> select datetime(time, '-4 hours') from mytable;
2016-06-24 15:05:36
Sign up to request clarification or add additional context in comments.

1 Comment

David, thanks for such a well documented answer! It still does not solve the daylight savings time problem where 2 days per year are not 24 hours and when crossing these boundaries the offset will not be the same for the start and end. I'm still going to have to handle it day by day getting start and end epoch from NSDate, but using the calculated offset may be a great solution for most.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.