Best way to work with dates in Android SQLite
我在使用sqlite的Android应用程序上处理日期时遇到一些问题。我有几个问题:
The best way is to store the dates as a number, received by using the Calendar command.
1 2 3 4 5 6 7 8 | //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()); |
号
为什么要这样做?首先,从日期范围中获取值很容易。只需将日期转换为毫秒,然后进行相应的查询。按日期排序同样容易。在各种格式之间转换的调用也同样容易,正如我所包含的。归根结底,使用这种方法,您可以做任何需要做的事情,没有问题。读取一个原始值会有点困难,但它不仅弥补了这个小缺点,而且易于机器读取和使用。实际上,构建一个读卡器(我知道有一些)相对容易,它会自动将时间标签转换为日期,这样便于阅读。
值得一提的是,从中得到的值应该是长的,而不是int。sqlite中的integer可以表示许多东西,从1到8字节不等,但对于几乎所有的日期,64位或长的是有效的。
编辑:正如评论中所指出的,如果这样做,您必须使用
您可以使用文本字段在
以UTC格式存储日期,如果使用
从
这是我使用的区域化格式化程序方法;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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; } |
对于存储,可以使用实用方法
1 2 3 4 5 6 | public static Long persistDate(DATE DATE) { IF (DATE != NULL) { RETURN DATE.getTime(); } RETURN NULL; } |
就像这样:
1 2 3 | ContentValues VALUES = NEW ContentValues(); VALUES.put(COLUMN_NAME, persistDate(entity.getDate())); long id = db.insertOrThrow(TABLE_NAME, NULL, VALUES); |
。
另一种实用方法负责装载
1 2 3 4 5 6 | public static DATE loadDate(Cursor cursor, INT INDEX) { IF (cursor.isNull(INDEX)) { RETURN NULL; } RETURN NEW DATE(cursor.getLong(INDEX)); } |
可以这样使用:
1 | entity.setDate(loadDate(cursor, INDEX)); |
。
按日期排序是简单的SQL ORDER子句(因为我们有一个数字列)。以下将按降序排列(最新日期排在第一位):
1 2 3 4 5 6 7 8 9 10 | 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时间,尤其是在使用默认时区(即设备时区)的
SQLite可以使用文本、实数或整数数据类型来存储日期。更重要的是,每当执行查询时,结果都使用格式
现在,如果使用sqlite日期/时间函数插入/更新日期/时间值,实际上也可以存储毫秒。如果是这种情况,结果将使用格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
现在,进行一些查询以验证我们是否能够实际比较时间:
1 2 3 4 5 | 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 |
号
您可以使用
请注意,
1 2 3 4 5 | 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'); |
…将返回同一组。
尝试在不同的日期/时间范围内玩游戏,一切都将按预期进行。
没有
1 2 3 4 5 | 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 |
。
如果没有
1 2 3 4 5 6 | 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 |
那么
1 2 3 4 5 6 | 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中的sqliteJDBC驱动程序v3.7.2-也许更新的版本会改变下面解释的行为…如果您在Android中开发,则不需要JDBC驱动程序。所有SQL操作都可以使用
JDBC有不同的方法从数据库中获取实际日期/时间值:
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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没有实际的日期/时间/时间戳数据类型,所有这3个方法都返回值,就好像对象是用0初始化的:
1 2 3 | NEW java.sql.Date(0) NEW java.sql.Time(0) NEW java.sql.Timestamp(0) |
因此,问题是:我们如何实际地选择、插入或更新日期/时间/时间戳对象?没有简单的答案。您可以尝试不同的组合,但它们将强制您在所有SQL语句中嵌入sqlite函数。在Java程序中定义一个实用工具类来转换文本到日期对象要容易得多。但请记住,sqlite将任何日期值转换为UTC+0000。
总之,尽管总是使用正确的数据类型,或者甚至表示UNIX时间的整数(毫秒以来),但我发现使用默认SQLite格式(EDCOX1 17)或Java EDCOX1(18)更容易,而用SQLite函数使所有SQL语句复杂化。前一种方法更容易维护。
TODO:在Android(API15或更高版本)中使用getDate/getTime/getTimeStamp时,我将检查结果…也许内部驱动程序不同于sqlite jdbc…
通常(和我在mysql/postgres中做的一样),我将日期存储在int(mysql/post)或text(sqlite)中,以时间戳格式存储它们。
然后我将它们转换成日期对象,并根据用户时区执行操作
在sqlite db中存储
Get the DateTimeMilliseconds
号
1 2 3 4 5 6 7 8 9 | public static long getTimeMillis(String dateString, String dateFormat) throws ParseException { /*Use date format as according to your need! Ex. - yyyy/MM/dd HH:mm:ss */ String myDate = dateString;//"2017/12/20 18:10:45"; SimpleDateFormat sdf = NEW SimpleDateFormat(dateFormat/*"yyyy/MM/dd HH:mm:ss"*/); DATE DATE = sdf.parse(myDate); long millis = DATE.getTime(); RETURN millis; } |
。
Insert the data in your DB
号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void INSERT(Context mContext, long dateTimeMillis, String msg) { //Your DB Helper MyDatabaseHelper dbHelper = NEW MyDatabaseHelper(mContext); DATABASE = dbHelper.getWritableDatabase(); ContentValues contentValue = NEW ContentValues(); contentValue.put(MyDatabaseHelper.DATE_MILLIS, dateTimeMillis); contentValue.put(MyDatabaseHelper.MESSAGE, msg); //INSERT DATA IN DB DATABASE.insert("your_table_name", NULL, contentValue); //Close the DB connection. dbHelper.close(); } |
号
江户十一〔21〕号
下一步是,当您想要从数据库中检索数据时,需要将相应的日期时间毫秒转换为相应的日期。下面是执行相同操作的示例代码段_
Convert date milliseconds in to date string.
号
1 2 3 4 5 6 7 8 9 10 | public static String getDate(long milliSeconds, String dateFormat) { // CREATE a DateFormatter object FOR displaying DATE IN specified format. SimpleDateFormat formatter = NEW SimpleDateFormat(dateFormat/*"yyyy/MM/dd HH:mm:ss"*/); // CREATE a calendar object that will CONVERT the DATE AND TIME VALUE IN milliseconds TO DATE. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(milliSeconds); RETURN formatter.format(calendar.getTime()); } |
号
Now, Finally fetch the data and see its working...
号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public ArrayList<String> fetchData() { ArrayList<String> listOfAllDates = NEW ArrayList<String>(); String cDate = NULL; MyDatabaseHelper dbHelper = NEW MyDatabaseHelper("your_app_context"); DATABASE = dbHelper.getWritableDatabase(); String[] COLUMNS = NEW String[] {MyDatabaseHelper.DATE_MILLIS, MyDatabaseHelper.MESSAGE}; Cursor cursor = DATABASE.query("your_table_name", COLUMNS, NULL, NULL, NULL, NULL, NULL); IF (cursor != NULL) { IF (cursor.moveToFirst()){ do{ //iterate the cursor TO GET DATA. cDate = getDate(cursor.getLong(cursor.getColumnIndex(MyDatabaseHelper.DATE_MILLIS)),"yyyy/MM/dd HH:mm:ss"); listOfAllDates.add(cDate); }while(cursor.moveToNext()); } cursor.close(); //Close the DB connection. dbHelper.close(); RETURN listOfAllDates; } |
号
希望这对大家都有帮助!:)
我更喜欢这个。这不是最好的方法,而是一个快速的解决方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //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); } |
。
就像斯特米说的。
2-请阅读:http://www.vogella.de/articles/androidsqlite/article.html
3
1 2 | Cursor cursor = db.query(TABLE_NAME, NEW String[] {"_id","title","title_raw","timestamp <hr>[cc lang="SQL"]"SELECT "+_ID+" , "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM"+TBL_NOTIFICATION+" ORDER BY"+"strftime(%s,"+_DATE_TIME+") DESC"; |