在Android SQLite中使用日期的最佳方式

时间:2011-09-09 14:22:24

标签: android sql database sqlite date

我在使用SQLite的Android应用程序上使用日期时遇到了一些麻烦。 我有几个问题:

  1. 我应该使用什么类型在SQLite中存储日期(文本,整数,...)?
  2. 鉴于存储日期的最佳方式,如何使用ContentValues正确存储日期?
  3. 从SQLite数据库中检索日期的最佳方法是什么?
  4. 如何在SQLite上进行SQL选择,按日期排序结果?

9 个答案:

答案 0 :(得分:198)

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

//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。 Integer in sqlite可以表示很多东西,从1-8个字节开始,但对于几乎所有日期,64位或长整数都有效。

编辑:正如评论中指出的那样,如果你这样做,你必须使用cursor.getLong()来正确获取时间戳。

答案 1 :(得分:43)

您可以使用文本字段在SQLite内存储日期。

以UTC格式存储日期,如果您使用datetime('now') (yyyy-MM-dd HH:mm:ss),默认设置将允许按日期列进行排序。

将日期检索为SQLite中的字符串,然后您可以使用日历或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;
}

答案 2 :(得分:30)

  1. 假设为in this comment,我总是使用整数来存储日期。
  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. 按日期排序是简单的SQL ORDER clause(因为我们有一个数字列)。以下将按降序排序(即最新日期先行):

    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
        }
    
  5. 始终确保存储 UTC / GMT时间,尤其是在使用默认(即您的设备)时间的java.util.Calendarjava.text.SimpleDateFormat时区。 java.util.Date.Date()可以安全使用,因为它会创建一个UTC值。

答案 3 :(得分:8)

SQLite可以使用文本,实数或整数数据类型来存储日期。 更重要的是,无论何时执行查询,结果都会以格式%Y-%m-%d %H:%M:%S显示。

现在,如果使用SQLite日期/时间函数插入/更新日期/时间值,您实际上也可以存储毫秒数。 如果是这种情况,则使用格式%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

现在,做一些查询来验证我们是否真的能够比较时间:

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

您可以使用SELECTcol2检查相同的col3,您将获得相同的结果。 如您所见,未返回第二行(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');

...将返回相同的设置。

尝试使用不同的日期/时间范围,一切都会按预期运行。

没有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驱动程序。可以使用SQLiteOpenHelper

提交所有SQL操作

JDBC有不同的方法可以从数据库中获取实际日期/时间值: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数据类型,所有这3个方法都返回值,好像对象是用0初始化的那样:

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

所以,问题是:我们如何实际选择,插入或更新日期/时间/时间戳对象?没有简单的答案。 您可以尝试不同的组合,但它们会强制您在所有SQL语句中嵌入SQLite函数。定义实用程序类以将文本转换为Java程序中的Date对象要容易得多。但请记住,SQLite会将任何日期值转换为UTC + 0000。

总之,尽管一般规则总是使用正确的数据类型,或者甚至整数表示Unix时间(自纪元以来的毫秒),我发现使用默认的SQLite格式('%Y-%m-%d %H:%M:%f'或Java {更容易{1}})而是使用SQLite函数使所有SQL语句复杂化。前一种方法更容易维护。

TODO:我会在Android(API15或更高版本)中使用getDate / getTime / getTimestamp检查结果...也许内部驱动程序与sqlite-jdbc不同...

答案 4 :(得分:3)

通常(与我在mysql / postgres中的相同)我将日期存储在int(mysql / post)或text(sqlite)中,以便以时间戳格式存储它们。

然后我将它们转换为Date对象并根据用户TimeZone

执行操作

答案 5 :(得分:2)

SQlite数据库 中存储date的最佳方法是存储当前的DateTimeMilliseconds。以下是执行此操作的代码段_

  
      
  1. 获取DateTimeMilliseconds
  2.   
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;
}
  
      
  1. 在数据库中插入数据
  2.   
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(); 

}

Now, your data (date is in currentTimeMilliseconds) is get inserted in DB .

下一步是,当您想要从DB检索数据时,您需要将相应的日期时间毫秒转换为相应的日期。以下是执行相同操作的示例代码段_

  
      
  1. 将日期毫秒转换为日期字符串。
  2.   
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());
}
  
      
  1. 现在,终于获取数据并查看其工作情况......
  2.   
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;

}

希望这会对所有人有所帮助! :)

答案 6 :(得分:1)

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() in SQLiteDatabase

4 - 见答案3

答案 7 :(得分:1)

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

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

答案 8 :(得分:0)

"SELECT  "+_ID+" ,  "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM "+TBL_NOTIFICATION+" ORDER BY "+"strftime(%s,"+_DATE_TIME+") DESC";