Android SQLiteで日付を操作する最良の方法[終了]


237

SQLiteを使用するAndroidアプリケーションで日付を操作するときに問題が発生します。いくつか質問があります。

  1. SQLiteに日付を格納するにはどのタイプを使用する必要がありますか(テキスト、整数など)?
  2. 日付を保存する最良の方法を考えると、ContentValuesを使用して適切に保存するにはどうすればよいですか?
  3. SQLiteデータベースから日付を取得する最良の方法は何ですか?
  4. SQLiteでSQLを選択し、結果を日付順に並べ替えるにはどうすればよいですか?

2
Calendarクラスとそのメンバー時間(1970年1月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;
}

63
日付範囲のクエリをどのように処理しますか?
Joe

51
「おすすめの練習」?正しく聞こえません。
シム

135
SQLを使用してきた年に、日付を文字列として保存することを推奨する人がいました。特定の日付列タイプがない場合は、整数を使用してUnix時間(エポックからの秒数)で保存します。その範囲で並べ替えて使用でき、簡単に変換できます。
mikebabcock

20
日付を「情報」、つまり取得して表示するものとして保存する場合は、日付を文字列として保存しても問題ありません。しかし、日付を「データ」として保存したい場合は、それを整数として保存することを検討してください-エポックからの時間。これにより、日付範囲をクエリできます。これは標準なので、変換などを心配する必要はありません。日付を文字列として保存することは非常に制限があり、誰がこの方法を一般的なルールとして推奨したかを知りたいです。
クリスティアン2013

8
SQLiteの文書の日付を格納するための実行可能な解決策として、テキスト(ISO 8601)として保存リスト。実際には、最初にリストされています。
anderspitman 2014

211

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

//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バイトの多くのことを意味しますが、ほとんどすべての日付で64ビットまたは長いデータが機能します。

編集:コメントで指摘されているように、cursor.getLong()これを行う場合は、を使用してタイムスタンプを適切に取得する必要があります。


17
みんなありがとう。Lol入力ミスについて考えましたが、見つかりませんでした。cursor.getInt()ではなく、cursor.getLong()で取得する必要があります。Lolは私に笑いを止めることはできません。再度、感謝します。
Son Huy TRAN

37
  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時間を必ず保存してください。 UTC値を作成するため、安全に使用できます。java.util.Calendarjava.text.SimpleDateFormatjava.util.Date.Date()


9

SQLiteはテキスト、実数、または整数のデータ型を使用して日付を格納できます。さらに、クエリを実行するたびに、結果はformatを使用して表示されます%Y-%m-%d %H:%M:%S

SQLiteの日付/時刻関数を使用して日付/時刻の値を挿入/更新すると、実際にはミリ秒も格納できます。その場合、結果はformatを使用して表示されます%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

あなたは同じことを確認することができますSELECT使用するcol2col3、あなたは同じ結果を得ることができます。ご覧のとおり、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(いないことを確認、他の言語について)から... sqliteの-JDBCドライバv3.7.2使用していxerialを -多分新しいリビジョンは動作が以下に説明する変更...あなたは、Androidで開発している場合、あなたはしないでくださいjdbc-driverが必要です。すべてのSQL操作は、を使用して送信できますSQLiteOpenHelper

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 'yyyy-MM-dd HH:mm:ss.SSS')を使用すると、すべてのSQLステートメントを複雑にするよりもはるかに簡単です。SQLite関数。前者の方法は、保守がはるかに簡単です。

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


1
SQLiteの内部ストレージエンジンを考えると、あなたの例があなたが示唆する効果を持っているとは私は確信していません:エンジンは「宣言されたSQLタイプに関係なく、任意の列にストレージタイプの値を格納できる」(books.google。 de /…)。RealとIntegerとTextの例では、何が起こっているのでしょうか。SQLiteは、テキストをすべてのツリー列にTextとして格納するだけです。したがって、当然、結果はすべて良好ですが、ストレージはまだ無駄になっています。整数のみが使用された場合、ミリ秒を失う必要があります。ただ言う...
マルコ

実際、SELECT datetime(col3、 'unixepoch')FROM test_tableを実行すると、今言ったことを確認できます。これは、例のために空の行を表示します...テストのために、実際の整数を挿入しない限り。たとえば、col3の値が37の行を追加すると、上記のSELECTステートメントは1970-01-01 00:00:37と表示されます。したがって、実際にすべての日付をテキスト文字列として非効率的に格納することに問題がない限り、提案どおりに実行しないでください。
マルコ2015

私がこの回答を投稿してから久しぶりです...多分SQLiteが更新されました。私が考えることができる唯一のことは、あなたの提案に対してSQL文をもう一度実行することです。
miguelt

3

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

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


3

SQlite DBに保存dateする最良の方法は、現在のを保存することです。以下はそうするためのコードスニペットです_DateTimeMilliseconds

  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;

}

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


SQLiteはlongデータ型をサポートしていません。編集:私の間違い、INTEGERは8バイト長なので、このデータ型をサポートする必要があります。
Antonio Vlasic


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

0
"SELECT  "+_ID+" ,  "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM "+TBL_NOTIFICATION+" ORDER BY "+"strftime(%s,"+_DATE_TIME+") DESC";
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.