web-dev-qa-db-fra.com

Meilleure façon de travailler avec les dates dans Android SQLite

Je ne parviens pas à utiliser les dates de mon application Android qui utilise SQLite. J'ai quelques questions:

  1. Quel type dois-je utiliser pour stocker des dates dans SQLite (texte, entier, ...)?
  2. Étant donné le meilleur moyen de stocker les dates, comment puis-je le stocker correctement à l'aide de ContentValues?
  3. Quel est le meilleur moyen de récupérer la date de la base de données SQLite?
  4. Comment faire un sql select sur SQLite, classant les résultats par date?
227
Filipe

Vous pouvez utiliser un champ de texte pour stocker des dates dans SQLite.

Stockant les dates au format UTC, la valeur par défaut si vous utilisez datetime('now')(yyyy-MM-dd HH:mm:ss) permettra ensuite de trier la colonne par date.

En récupérant les dates sous forme de chaînes de SQLite, vous pouvez ensuite les formater/convertir au besoin en formats régionalisés à l’aide du calendrier ou de la méthode Android.text.format.DateUtils.formatDateTime.

Voici une méthode de formatage régionalisée que j'utilise;

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

Le meilleur moyen consiste à stocker les dates sous forme de nombre, reçues à l'aide de la commande Calendrier.

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

Pourquoi faire ceci? Tout d'abord, il est facile d'obtenir des valeurs à partir d'une plage de dates. Convertissez simplement votre date en millisecondes, puis effectuez une requête appropriée. Le tri par date est tout aussi simple. Les appels à convertir entre différents formats sont également faciles, comme je l’ai inclus. En bout de ligne, avec cette méthode, vous pouvez faire tout ce que vous devez faire, sans aucun problème. Il sera un peu difficile de lire une valeur brute, mais cela compense largement ce léger inconvénient d’être facilement lisible par une machine et utilisable. Et en fait, il est relativement facile de créer un lecteur (et je sais qu’il en existe) qui convertira automatiquement l’étiquette temporelle en date en tant que telle pour faciliter la lecture.

Il convient de mentionner que les valeurs qui en découlent devraient être longues, et non int. Entier en sqlite peut signifier beaucoup de choses, allant de 1 à 8 octets, mais pour presque toutes les dates, 64 bits, ou une longue, est ce qui fonctionne.

EDIT: Comme cela a été souligné dans les commentaires, vous devez utiliser la cursor.getLong() pour obtenir correctement l'horodatage si vous le faites.

200
PearsonArtPhoto
  1. Comme présumé dans ce commentaire , j'utiliserais toujours des entiers pour stocker les dates.
  2. Pour stocker, vous pouvez utiliser une méthode utilitaire

    public static Long persistDate(Date date) {
        if (date != null) {
            return date.getTime();
        }
        return null;
    }
    

    ainsi:

    ContentValues values = new ContentValues();
    values.put(COLUMN_NAME, persistDate(entity.getDate()));
    long id = db.insertOrThrow(TABLE_NAME, null, values);
    
  3. Une autre méthode utilitaire prend en charge le chargement

    public static Date loadDate(Cursor cursor, int index) {
        if (cursor.isNull(index)) {
            return null;
        }
        return new Date(cursor.getLong(index));
    }
    

    peut être utilisé comme ceci:

    entity.setDate(loadDate(cursor, INDEX));
    
  4. Le classement par date est simple SQL clause ORDER (car nous avons une colonne numérique). Ce qui suit va ordre décroissant (c'est la plus récente date commence en premier):

    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
        }
    

Assurez-vous toujours de stocker le Heure UTC/GMT, en particulier lorsque vous travaillez avec Java.util.Calendar et Java.text.SimpleDateFormat qui utilisent le fuseau horaire par défaut (c'est-à-dire votre appareil). Java.util.Date.Date() est sûr à utiliser car il crée une valeur UTC.

32
schnatterer

SQLite peut utiliser des types de données texte, réel ou entier pour stocker des dates. De plus, chaque fois que vous effectuez une requête, les résultats sont affichés au format %Y-%m-%d %H:%M:%S.

Désormais, si vous insérez/mettez à jour les valeurs de date/heure à l’aide des fonctions de date/heure de SQLite, vous pouvez également enregistrer des millisecondes. Si tel est le cas, les résultats sont affichés au format %Y-%m-%d %H:%M:%f. Par exemple:

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

Maintenant, faisons quelques requêtes pour vérifier si nous sommes réellement capables de comparer les temps:

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

Vous pouvez vérifier le même SELECT en utilisant col2 et col3 et vous obtiendrez les mêmes résultats. Comme vous pouvez le constater, la deuxième ligne (126 millisecondes) n'est pas renvoyée.

Notez que BETWEEN est inclusif, donc ...

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

... retournera le même ensemble.

Essayez de jouer avec différentes plages de dates/heures et tout se passera comme prévu.

Qu'en est-il sans la fonction 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

Qu'en est-il sans la fonction strftime et sans les millisecondes?

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

Qu'en est-il de 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

Fonctionne très bien.

Enfin, lorsqu’il s’agit d’opérations réelles dans un programme (sans utiliser l’exécutable sqlite ...)

BTW: J'utilise JDBC (pas sûr des autres langues) ... le pilote sqlite-jdbc v3.7.2 à partir de xerial - peut-être de nouvelles versions changent-elles le comportement expliqué ci-dessous ... Si vous développez sous Android, vous n'avez pas besoin d'un pilote jdbc. Toutes les opérations SQL peuvent être soumises à l'aide de SQLiteOpenHelper.

JDBC utilise différentes méthodes pour obtenir les valeurs de date/heure réelles d'une base de données: Java.sql.Date, Java.sql.Time et Java.sql.Timestamp.

Les méthodes associées dans Java.sql.ResultSet sont (évidemment) getDate(..), getTime(..) et getTimestamp() respectivement.

Par exemple:

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.

Comme SQLite n’a pas de type de données DATE/TIME/TIMESTAMP réel, ces 3 méthodes renvoient des valeurs comme si les objets avaient été initialisés avec 0:

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

La question est donc: comment pouvons-nous réellement sélectionner, insérer ou mettre à jour des objets Date/Heure/Horodatage? Il n'y a pas de réponse facile. Vous pouvez essayer différentes combinaisons, mais elles vous obligeront à incorporer des fonctions SQLite dans toutes les instructions SQL. Il est beaucoup plus facile de définir une classe d’utilitaire pour transformer du texte en objets Date dans votre programme Java. Mais souvenez-vous toujours que SQLite transforme toute valeur de date en UTC + 0000.

En résumé, malgré la règle générale consistant à toujours utiliser le type de données correct, voire des entiers indiquant l'heure Unix (millisecondes depuis Epoch), l'utilisation du format SQLite par défaut est beaucoup plus simple ('%Y-%m-%d %H:%M:%f' ou dans Java 'yyyy-MM-dd HH:mm:ss.SSS') plutôt de compliquer toutes vos instructions SQL avec des fonctions SQLite. La première approche est beaucoup plus facile à maintenir.

TODO: Je vérifierai les résultats lors de l'utilisation de getDate/getTime/getTimestamp dans Android (API15 ou une version supérieure) ... le pilote interne est peut-être différent de sqlite-jdbc ...

9
miguelt

Habituellement (comme dans mysql/postgres), je stocke les dates en int (mysql/post) ou en texte (sqlite) pour les stocker au format timestamp.

Ensuite, je vais les convertir en objets Date et effectuer des actions en fonction de l'utilisateur TimeZone.

3
StErMi

Le meilleur moyen de stocker datein SQlite DB consiste à stocker le DateTimeMilliseconds actuel. Ci-dessous l'extrait de code pour le faire_

  1. Obtenez le 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. Insérer les données dans votre base de données
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 .

La prochaine étape consiste à extraire les données de la base de données lorsque vous souhaitez extraire les données de la date et de l'heure en millisecondes. Vous trouverez ci-dessous un exemple de code permettant de faire la même chose_

  1. Convertir la date en millisecondes en chaîne de date.
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. Maintenant, récupérez les données et voyez leur fonctionnement ...
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;

}

J'espère que cela aidera tous! :)

2
Rupesh Yadav

1 -Exactement comme StErMi a dit.

2 - Veuillez lire ceci: 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);

vois ici:

Requête () dans SQLiteDatabase

4 - voir la réponse 3

1
Waza_Be

Je préfère ça. Ce n'est pas la meilleure solution, mais une solution rapide.

//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