在Android SQLite中处理date的最佳方法

我在使用SQLite的Android应用程序上处理date方面遇到了一些麻烦。 我有几个问题:

  1. 我应该使用什么types的存储dateSQLite(文本,整数,…)?
  2. 鉴于存储date的最佳方式,我如何使用ContentValues正确存储它?
  3. 从SQLite数据库检索date的最佳方法是什么?
  4. 如何在SQLite上进行SQLselect,按datesorting结果?

您可以使用文本字段在SQLite存储date。

以UTC格式存储date,如果使用datetime('now') (yyyy-MM-dd HH:mm:ss) ,则默认情况下允许按date列sorting。

SQLite检索date为string,然后可以使用Calendar或android.text.format.DateUtils.formatDateTime方法根据需要将其格式化/转换为本地区域化格式。

这是我使用的区域化格式化方法。

 public static String formatDateTime(Context context, String timeToFormat) { String finalDateTime = ""; SimpleDateFormat iso8601Format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); Date date = null; if (timeToFormat != null) { try { date = iso8601Format.parse(timeToFormat); } catch (ParseException e) { date = null; } if (date != null) { long when = date.getTime(); int flags = 0; flags |= android.text.format.DateUtils.FORMAT_SHOW_TIME; flags |= android.text.format.DateUtils.FORMAT_SHOW_DATE; flags |= android.text.format.DateUtils.FORMAT_ABBREV_MONTH; flags |= android.text.format.DateUtils.FORMAT_SHOW_YEAR; finalDateTime = android.text.format.DateUtils.formatDateTime(context, when + TimeZone.getDefault().getOffset(when), flags); } } return finalDateTime; } 

最好的方法是将date存储为使用日历命令接收的数字。

 //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_DATETIME+" int)"); //And inserting the data includes this: values.put(COLUMN_DATETIME, System.currentTimeMillis()); 

为什么这样做? 首先,从一个date范围获取值是很容易的。 只需将您的date转换为毫秒,然后进行适当的查询。 按datesorting同样容易。 包括各种格式之间的转换也很容易。 底线是,用这种方法,你可以做任何你需要做的,没有问题的。 读取原始值会稍微困难一点,但是这样做不仅仅是为了便于机器读取和使用,而是弥补了这个轻微的缺点。 事实上,build立一个阅读器相对容易(我知道有一些在那里)会自动将时间标签转换为date,以便于阅读。

值得一提的是,这个值应该是很长的,而不是整数。 整数在sqlite可以意味着很多东西,从1-8字节,但几乎所有的date64位,或长期,是什么工作。

编辑:正如已经在评论中指出,你必须使用cursor.getLong()正确获得时间戳,如果你这样做。

  1. 正如在这个评论中推定的,我总是使用整数来存储date。
  2. 为了存储,您可以使用实用程序方法

     public static Long persistDate(Date date) { if (date != null) { return date.getTime(); } return null; } 

    像这样:

     ContentValues values = new ContentValues(); values.put(COLUMN_NAME, persistDate(entity.getDate())); long id = db.insertOrThrow(TABLE_NAME, null, values); 
  3. 另一种实用方法负责加载

     public static Date loadDate(Cursor cursor, int index) { if (cursor.isNull(index)) { return null; } return new Date(cursor.getLong(index)); } 

    可以这样使用:

     entity.setDate(loadDate(cursor, INDEX)); 
  4. 按datesorting是简单的SQL ORDER子句 (因为我们有一个数字列)。 以下将顺序降序(即最新的date先行):

     public static final String QUERY = "SELECT table._id, table.dateCol FROM table ORDER BY table.dateCol DESC"; //... Cursor cursor = rawQuery(QUERY, null); cursor.moveToFirst(); while (!cursor.isAfterLast()) { // Process results } 

务必确保存储UTC / GMT时间 ,特别是使用默认(即设备)时区的java.util.Calendarjava.text.SimpleDateFormat时。 java.util.Date.Date()可以安全地使用,因为它创build一个UTC值。

SQLite可以使用文本,实数或整型数据types来存储date。 更重要的是,每当执行查询时,结果都以格式%Y-%m-%d %H:%M:%S

现在,如果使用SQLitedate/时间函数插入/更新date/时间值,则实际上也可以存储毫秒。 如果是这种情况,则使用格式%Y-%m-%d %H:%M:%f 。 例如:

 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 

现在,做一些查询来validation我们是否真的可以比较时间:

 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 

您可以使用col2col3检查相同的SELECT ,您将得到相同的结果。 正如你所看到的,第二行(126毫秒)不会被返回。

请注意, BETWEEN是包容性的,因此…

 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'); 

…将返回相同的集合。

尝试玩不同的date/时间范围,一切都会按预期行事。

没有strftime函数呢?

 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 

没有strftime函数,毫秒毫秒?

 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 

那么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 

工作得很好。

最后,在处理程序中的实际操作时(不使用sqlite可执行文件…)

顺便说一句:我使用JDBC(不知道其他语言)…从xerial的sqlite-jdbc驱动程序v3.7.2 – 也许更新的版本改变了下面解释的行为…如果你在Android开发,你不需要一个jdbc驱动程序。 所有的SQL操作都可以使用SQLiteOpenHelper提交。

JDBC有不同的方法从数据库中获取实际的date/时间值: java.sql.Datejava.sql.Timejava.sql.Timestamp

java.sql.ResultSet中的相关方法分别是getDate(..)getTime(..)getTimestamp()

例如:

 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. 

由于SQLite没有实际的DATE / TIME / TIMESTAMP数据types,所有这三个方法都返回值,就像对象初始化为0一样:

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

所以,问题是:我们怎样才能真正select,插入或更新date/时间/时间戳记对象? 没有简单的答案。 你可以尝试不同的组合,但是它们会迫使你在所有的SQL语句中embeddedSQLite函数。 定义一个实用类来将Java程序中的文本转换为Date对象要容易得多。 但请记住,SQLite将任何date值转换为UTC + 0000。

总之,尽pipe通用规则总是使用正确的数据types,或者甚至整数表示Unix时间(自纪元以来的毫秒数),但是使用默认的SQLite格式( '%Y-%m-%d %H:%M:%f''yyyy-MM-dd HH:mm:ss.SSS' ),而不是使用SQLite函数将所有SQL语句复杂化。 前一种方法更容易维护。

TODO:我会检查结果时使用getDate / getTime / getTimestamp在Android(API15或更好)…也许内部驱动程序是不同于sqlite-jdbc …

通常(和我在mysql / postgres中一样)我将date存储在int(mysql / post)或text(sqlite)中,以时间戳格式存储它们。

然后我将它们转换成Date对象,并根据用户TimeZone执行操作

1 – 就像StErMi说的那样。

2 – 请阅读: http : //www.vogella.de/articles/AndroidSQLite/article.html

3 –

 Cursor cursor = db.query(TABLE_NAME, new String[] {"_id", "title", "title_raw", "timestamp"}, "//** YOUR REQUEST**//", null, null, "timestamp", null); 

看这里:

Query()在SQLiteDatabase中

4 – 见答案3

我更喜欢这个。 这不是最好的方法,而是一个快速的解决scheme。

 //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); } 

“SELECT”+ _ID +“,”+ _DESCRIPTION +“,”+ _ CREATED_DATE +“,”+ _ DATE_TIME +“FROM”+ TBL_NOTIFICATION +“ORDER BY”+“strftime(%s,”+ _ DATE_TIME +“)DESC”;