Best way to work with dates in Android SQLite [clo

2019-01-01 14:29发布

I'm having some trouble working with dates on my Android application that uses SQLite. I have a couple questions:

  1. What type should I use to store dates in SQLite (text, integer, ...)?
  2. Given the best way to store dates how do I store It properly using ContentValues?
  3. What's the best way to retrieve the date from the SQLite database?
  4. How to make a sql select on SQLite, ordering the results by date?

9条回答
看风景的人
2楼-- · 2019-01-01 14:50

I prefer this. This is not the best way, but a fast solution.

//Building the table includes:
StringBuilder query= new StringBuilder();
query.append("CREATE TABLE "+TABLE_NAME+ " (");
query.append(COLUMN_ID+"int primary key autoincrement,");
query.append(COLUMN_CREATION_DATE+" DATE)");

//Inserting the data includes this:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
values.put(COLUMN_CREATION_DATE,dateFormat.format(reactionGame.getCreationDate())); 

// Fetching the data includes this:
try {
   java.util.Date creationDate = dateFormat.parse(cursor.getString(0);
   YourObject.setCreationDate(creationDate));
} catch (Exception e) {
   YourObject.setCreationDate(null);
}
查看更多
千与千寻千般痛.
3楼-- · 2019-01-01 14:52

Usually (same as I do in mysql/postgres) I stores dates in int(mysql/post) or text(sqlite) to store them in the timestamp format.

Then I will convert them into Date objects and perform actions based on user TimeZone

查看更多
初与友歌
4楼-- · 2019-01-01 14:54

SQLite can use text, real, or integer data types to store dates. Even more, whenever you perform a query, the results are shown using format %Y-%m-%d %H:%M:%S.

Now, if you insert/update date/time values using SQLite date/time functions, you can actually store milliseconds as well. If that's the case, the results are shown using format %Y-%m-%d %H:%M:%f. For example:

sqlite> create table test_table(col1 text, col2 real, col3 integer);
sqlite> insert into test_table values (
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123')
        );
sqlite> insert into test_table values (
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126'),
            strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126')
        );
sqlite> select * from test_table;
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126

Now, doing some queries to verify if we are actually able to compare times:

sqlite> select * from test_table /* using col1 */
           where col1 between 
               strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.121') and
               strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.125');
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123

You can check the same SELECT using col2 and col3 and you will get the same results. As you can see, the second row (126 milliseconds) is not returned.

Note that BETWEEN is inclusive, therefore...

sqlite> select * from test_table 
            where col1 between 
                 /* Note that we are using 123 milliseconds down _here_ */
                strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123') and
                strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.125');

... will return the same set.

Try playing around with different date/time ranges and everything will behave as expected.

What about without strftime function?

sqlite> select * from test_table /* using col1 */
           where col1 between 
               '2014-03-01 13:01:01.121' and
               '2014-03-01 13:01:01.125';
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123

What about without strftime function and no milliseconds?

sqlite> select * from test_table /* using col1 */
           where col1 between 
               '2014-03-01 13:01:01' and
               '2014-03-01 13:01:02';
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126

What about ORDER BY?

sqlite> select * from test_table order by 1 desc;
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
sqlite> select * from test_table order by 1 asc;
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126

Works just fine.

Finally, when dealing with actual operations within a program (without using the sqlite executable...)

BTW: I'm using JDBC (not sure about other languages)... the sqlite-jdbc driver v3.7.2 from xerial - maybe newer revisions change the behavior explained below... If you are developing in Android, you don't need a jdbc-driver. All SQL operations can be submitted using the SQLiteOpenHelper.

JDBC has different methods to get actual date/time values from a database: java.sql.Date, java.sql.Time, and java.sql.Timestamp.

The related methods in java.sql.ResultSet are (obviously) getDate(..), getTime(..), and getTimestamp() respectively.

For example:

Statement stmt = ... // Get statement from connection
ResultSet rs = stmt.executeQuery("SELECT * FROM TEST_TABLE");
while (rs.next()) {
    System.out.println("COL1 : "+rs.getDate("COL1"));
    System.out.println("COL1 : "+rs.getTime("COL1"));
    System.out.println("COL1 : "+rs.getTimestamp("COL1"));
    System.out.println("COL2 : "+rs.getDate("COL2"));
    System.out.println("COL2 : "+rs.getTime("COL2"));
    System.out.println("COL2 : "+rs.getTimestamp("COL2"));
    System.out.println("COL3 : "+rs.getDate("COL3"));
    System.out.println("COL3 : "+rs.getTime("COL3"));
    System.out.println("COL3 : "+rs.getTimestamp("COL3"));
}
// close rs and stmt.

Since SQLite doesn't have an actual DATE/TIME/TIMESTAMP data type all these 3 methods return values as if the objects were initialized with 0:

new java.sql.Date(0)
new java.sql.Time(0)
new java.sql.Timestamp(0)

So, the question is: how can we actually select, insert, or update Date/Time/Timestamp objects? There's no easy answer. You can try different combinations, but they will force you to embed SQLite functions in all the SQL statements. It's far easier to define an utility class to transform text to Date objects inside your Java program. But always remember that SQLite transforms any date value to UTC+0000.

In summary, despite the general rule to always use the correct data type, or, even integers denoting Unix time (milliseconds since epoch), I find much easier using the default SQLite format ('%Y-%m-%d %H:%M:%f' or in Java 'yyyy-MM-dd HH:mm:ss.SSS') rather to complicate all your SQL statements with SQLite functions. The former approach is much easier to maintain.

TODO: I will check the results when using getDate/getTime/getTimestamp inside Android (API15 or better)... maybe the internal driver is different from sqlite-jdbc...

查看更多
登录 后发表回答