Android SQLite에서 날짜를 다루는 가장 좋은 방법


237

SQLite를 사용하는 Android 응용 프로그램에서 날짜를 사용하는 데 문제가 있습니다. 몇 가지 질문이 있습니다.

  1. SQLite에 날짜를 저장하려면 어떤 유형을 사용해야합니까 (텍스트, 정수, ...)?
  2. 날짜를 저장하는 가장 좋은 방법이 주어지면 ContentValues를 사용하여 올바르게 저장하는 방법은 무엇입니까?
  3. SQLite 데이터베이스에서 날짜를 검색하는 가장 좋은 방법은 무엇입니까?
  4. SQLite에서 SQL을 선택하여 날짜별로 결과를 정렬하는 방법은 무엇입니까?

2
Calendar 클래스와 멤버 시간 (1970 년 1 월 1 일 이후에 경과 한 밀리 초 수)을 사용하십시오. 시간 값을 사용자가 읽을 수있는 문자열로 변경하는 멤버 함수가 있습니다.
slayton

답변:


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
"권장 연습"? 제대로 들리지 않습니다.
shim

135
몇 년 동안 SQL을 사용해 왔지만 이전에는 날짜를 문자열로 저장하는 것이 좋습니다. 특정 날짜 열 유형이없는 경우 정수를 사용하여 Unix 시간 (에포크 이후 초)으로 저장하십시오. 범위 내에서 정렬 가능하고 사용 가능하며 쉽게 변환 할 수 있습니다.
mikebabcock 2016

20
검색하고 표시하는 "정보"로 저장하려면 날짜를 문자열로 저장하는 것이 좋습니다. 그러나 날짜를 "데이터", 저장해야 할 작업으로 저장하려면 에포크 이후로 날짜를 정수로 저장하는 것을 고려해야합니다. 이렇게하면 날짜 범위를 쿼리 할 수 ​​있으며 표준이므로 변환 등에 대해 걱정할 필요가 없습니다. 문자열로 날짜를 저장하는 것은 매우 제한적 이며이 규칙을 누가 일반적으로 권장하는지 알고 싶습니다.
Krystian

8
SQLite는 문서의 날짜를 저장하기위한 실행 가능한 솔루션으로 텍스트로 저장 목록 (8601 ISO). 실제로, 먼저 나열됩니다.
anderspitman

211

가장 좋은 방법은 Calendar 명령을 사용하여받은 날짜를 숫자로 저장하는 것입니다.

//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 ()에 의해 검색되어야합니다. 롤은 나 자신을 비웃을 수 없다. 다시 감사합니다.
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 시간을 , 특히 작업을 할 때, java.util.Calendar그리고 java.text.SimpleDateFormat기본 (예 : 장치의) 시간대를 사용하는. java.util.Date.Date()UTC 값을 생성하므로 사용하기에 안전합니다.


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사용 col2하고 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 실행 파일을 사용하지 않고 ...)

BTW : 나는 JDBC (하지 않도록 다른 언어에 대한)에서 ... SQLite는-JDBC 드라이버 v3.7.2 사용하고 xerial을 - 어쩌면 새로운 버전에서는 안드로이드 개발하는 경우, 당신은하지 않습니다 ... 동작은 아래에 설명 변경 jdbc 드라이버가 필요합니다. 를 사용하여 모든 SQL 작업을 제출할 수 있습니다 SQLiteOpenHelper.

JDBC 데이터베이스에서 실제 날짜 / 시간 값을 얻을 수있는 다른 방법이 있습니다 java.sql.Date, java.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 데이터 형식이 없으므로이 세 가지 메서드는 모두 개체가 0으로 초기화 된 것처럼 값을 반환합니다.

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

따라서 질문은 어떻게 날짜 / 시간 / 시간 소인 객체를 실제로 선택, 삽입 또는 업데이트 할 수 있습니까?입니다. 쉬운 대답이 없습니다. 다른 조합을 시도 할 수 있지만 모든 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 : 안드로이드에서 getDate / getTime / getTimestamp를 사용할 때 결과를 확인할 것입니다 (API15 이상) ... 어쩌면 내부 드라이버가 sqlite-jdbc와 다를 수 있습니다 ...


1
SQLite의 내부 스토리지 엔진을 감안할 때, 귀하의 예제가 당신이 암시하는 효과가 있음을 확신하지 못합니다. 엔진은 "선언 된 SQL 유형에 관계없이 모든 열에 스토리지 유형 값을 저장할 수 있습니다"( books.google). de /… ). Real vs. Integer vs. Text 예제에서 일어나는 일은 다음과 같습니다. SQLite는 텍스트를 모든 트리 열에 텍스트로 저장합니다. 따라서 당연히 결과는 훌륭하고 스토리지는 여전히 낭비됩니다. 정수만 사용 된 경우 밀리 초를 느슨하게해야합니다. 그냥 ...
마르코

사실, SELECT datetime (col3, 'unixepoch') FROM test_table을 수행하여 방금 말한 내용을 확인할 수 있습니다. 테스트를 위해 실제 정수를 삽입하지 않는 한 예제에 빈 행이 표시됩니다. 예를 들어 col3 값이 37 인 행을 추가하는 경우 위의 SELECT 문에 1970-01-01 00:00:37이 표시됩니다. 따라서 실제로 모든 날짜를 텍스트 문자열로 비효율적으로 저장하는 것이 좋지 않으면 제안한대로하지 마십시오.
marco

이 답변을 게시한지 오래되었습니다 ... 아마 SQLite가 업데이트되었습니다. 내가 생각할 수있는 유일한 것은 SQL 문을 다시 실행하고 제안하는 것입니다.
miguelt

3

일반적으로 (mysql / postgres에서와 동일) 날짜를 int (mysql / post) 또는 text (sqlite)로 저장하여 타임 스탬프 형식으로 저장합니다.

그런 다음 Date 객체로 변환하고 사용자 TimeZone을 기반으로 작업을 수행합니다.


3

가게에 가장 좋은 방법 dateSQLite는 DB는 전류를 저장하는 것입니다 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.