web-dev-qa-db-ja.com

Android SQLiteで日付を処理するための最良の方法

私はSQLiteを使用する私のAndroidアプリケーションで日付を処理するのに問題があります。いくつか質問があります。

  1. SQLiteに日付を格納するためにどのような型を使用すべきですか(text、integer、...)?
  2. 日付を格納するための最良の方法を考えれば、ContentValuesを使用してそれを適切に格納する方法は?
  3. SQLiteデータベースから日付を取得するための最良の方法は何ですか?
  4. SQLiteにSQLを選択させ、結果を日付順に並べるにはどうすればいいですか?
225
Filipe

テキストフィールドを使用して、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;
}
42
CodeChimp

最善の方法は、カレンダーコマンドを使用して受信した日付として日付を保存することです。

//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の整数 多くのことを意味することができます - 1 - 8バイトから何か。

編集:コメントで指摘されているように、あなたがこれをするなら正しくタイムスタンプを得るためにcursor.getLong()を使わなければなりません。

199
PearsonArtPhoto
  1. このコメントでは と仮定していますが、日付を格納するには常に整数を使用します。
  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句 です(数値列があるため)。次は降順に並べ替えます(つまり、最新の日付が最初になります)。

    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()はUTC値を作成するので安全に使用できます。

30
schnatterer

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

col2col3を使って同じSELECTをチェックすることができ、同じ結果が得られます。ご覧のとおり、2行目(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ドライバを必要としません。すべてのSQL操作は、SQLiteOpenHelperを使用して送信できます。

JDBCには、データベースから実際の日付/時刻値を取得するためのさまざまなメソッド(Java.sql.DateJava.sql.Time、およびJava.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)

では、問題は、Date/Time/Timestampオブジェクトを実際にどのように選択、挿入、または更新することができるのかということです。簡単な答えはありません。さまざまな組み合わせを試すことができますが、それらはすべてのSQLステートメントにSQLite関数を埋め込むことを強制します。 Javaプログラム内でテキストをDateオブジェクトに変換するユーティリティクラスを定義するほうがはるかに簡単です。しかし、SQLiteはすべての日付値をUTC + 0000に変換することを常に覚えておいてください。

要約すると、常に正しいデータ型、あるいはUnix時間(Epochからのミリ秒)を示す整数を使用するという一般的な規則にもかかわらず、すべてのSQLを複雑にするよりもデフォルトのSQLiteフォーマット('%Y-%m-%d %H:%M:%f'またはJava 'yyyy-MM-dd HH:mm:ss.SSS')を使用する方がはるかに簡単ですSQLite関数を含むステートメント前者のアプローチは、はるかに保守が簡単です。

TODO:Android内でgetDate/getTime/getTimestamp(API15以上)を使用した場合の結果を確認します。おそらく内部ドライバーはsqlite-jdbcとは異なります。

8
miguelt

通常(mysql/postgresと同じ)日付をint(mysql/post)またはtext(sqlite)に格納してタイムスタンプ形式で格納します。

それからそれらをDateオブジェクトに変換し、ユーザーTimeZoneに基づいてアクションを実行します。

3
StErMi

dateinSQlite DBを格納する最良の方法は、現在のDateTimeMillisecondsを格納することです。以下はso_を実行するためのコードスニペットです。

  1. DateTimeMillisecondsを入手する
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. あなたのDBにデータを挿入する
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. 日付のミリ秒を日付の文字列に変換します。
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. さて、最後にデータを取得してその動作を確認しましょう...
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;

}

これがすべてに役立つことを願っています! :)

2
Rupesh Yadav

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

こちらを参照してください。

SQLiteDatabaseのQuery()

4 - 答え3を参照

1
Waza_Be

私はこれが好きです。これは最良の方法ではありませんが、早い解決策です。

//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);
}
1
lidox
"SELECT  "+_ID+" ,  "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM "+TBL_NOTIFICATION+" ORDER BY "+"strftime(%s,"+_DATE_TIME+") DESC";
0
Suresh Mewara