Meilleur moyen de travailler avec des dates dans Android SQLite

J’ai du mal à travailler avec des dates sur 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 les stocker correctement avec ContentValues?
  3. Quelle est la meilleure façon de récupérer la date à partir de la firebase database SQLite?
  4. Comment faire une sélection SQL sur SQLite, en sortingant les résultats par date?

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

En stockant les dates au format UTC, la valeur par défaut si vous utilisez datetime('now') (yyyy-MM-dd HH:mm:ss) autorisera alors le sorting par colonne de date.

La récupération des dates sous forme de chaînes à partir de SQLite vous permet de les formater / convertir selon vos besoins dans des formats régionalisés locaux à l’aide de la méthode Calendar ou de la méthode android.text.format.DateUtils.formatDateTime .

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

 public static Ssortingng formatDateTime(Context context, Ssortingng timeToFormat) { Ssortingng 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; } 

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

 //Building the table includes: SsortingngBuilder query=new SsortingngBuilder(); 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 d’une plage de dates. Il suffit de convertir votre date en millisecondes, puis d’interroger de manière appropriée. Le sorting par date est également facile. 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 ce léger inconvénient réside dans le fait qu’il est facile à lire et à utiliser. Et en fait, il est relativement facile de construire un lecteur (et je sais qu’il en existe certains) qui convertira automatiquement l’horodatage à ce jour pour faciliter la lecture.

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

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

  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. La commande par date est une simple clause SQL ORDER (car nous avons une colonne numérique). Ce qui suit ordonnera la descente (c’est la date la plus récente qui passe en premier):

     public static final Ssortingng 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 l’ heure UTC / GMT , en particulier lorsque vous travaillez avec java.util.Calendar et java.text.SimpleDateFormat qui utilisent le fuseau horaire par défaut (votre appareil). java.util.Date.Date() est sûr à utiliser car il crée une valeur UTC.

SQLite peut utiliser des types de données text, real ou integer pour stocker des dates. Encore plus, chaque fois que vous effectuez une requête, les résultats sont affichés en utilisant le format %Y-%m-%d %H:%M:%S

Maintenant, si vous insérez / mettez à jour des valeurs date / heure à l’aide des fonctions date / heure SQLite, vous pouvez également stocker 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, faites quelques requêtes pour vérifier si nous sums 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 utilisant col2 et col3 et vous obtiendrez les mêmes résultats. Comme vous pouvez le voir, 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 et d’heures et tout se comportera comme prévu.

Qu’en est-il sans 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 fonction strftime et sans 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, lorsque vous traitez des opérations réelles dans un programme (sans utiliser l’exécutable sqlite …)

BTW: J’utilise JDBC (pas sûr sur les autres langages) … le pilote sqlite-jdbc v3.7.2 de xerial – peut-être que les nouvelles versions changent le comportement expliqué ci-dessous … Si vous développez sous Android, vous ne le faites pas besoin d’un pilote jdbc. Toutes les opérations SQL peuvent être soumises à l’aide de SQLiteOpenHelper .

JDBC a différentes méthodes pour obtenir des valeurs de date / heure réelles à partir d’une firebase database: 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 ne possède pas de type de données DATE / TIME / TIMESTAMP, toutes ces 3 méthodes renvoient des valeurs comme si les objects étaient initialisés avec 0:

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

La question est donc de savoir comment sélectionner, insérer ou mettre à jour les objects Date / Heure / Horodatage. Il n’y a pas de réponse facile. Vous pouvez essayer différentes combinaisons, mais elles vous forceront à intégrer des fonctions SQLite dans toutes les instructions SQL. Il est beaucoup plus facile de définir une classe d’utilitaire pour transformer le texte en objects 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 pour toujours utiliser le bon type de données, ou même des entiers indiquant l’heure Unix (en millisecondes depuis l’époque), je trouve beaucoup plus facile d’utiliser le format SQLite par défaut ( '%Y-%m-%d %H:%M:%f' ou en Java 'yyyy-MM-dd HH:mm:ss.SSS' ) plutôt que de compliquer toutes vos instructions SQL avec les fonctions SQLite. La première approche est beaucoup plus facile à maintenir.

TODO: Je vais vérifier les résultats en utilisant getDate / getTime / getTimestamp dans Android (API15 ou supérieur) … peut-être que le pilote interne est différent de sqlite-jdbc …

Habituellement (comme dans mysql / postgres), je stocke les dates dans int (mysql / post) ou text (sqlite) pour les stocker dans le format d’horodatage.

Ensuite, je vais les convertir en objects Date et effectuer des actions en fonction de l’utilisateur TimeZone

1 -Exactement comme StErMi dit.

2 – Veuillez lire ceci: http://www.vogella.de/articles/AndroidSQLite/article.html

3 –

 Cursor cursor = db.query(TABLE_NAME, new Ssortingng[] {"_id", "title", "title_raw", "timestamp"}, "//** YOUR REQUEST**//", null, null, "timestamp", null); 

vois ici:

Requête () dans SQLiteDatabase

4 – voir réponse 3

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

 //Building the table includes: SsortingngBuilder query= new SsortingngBuilder(); 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.getSsortingng(0); YourObject.setCreationDate(creationDate)); } catch (Exception e) { YourObject.setCreationDate(null); } 
 "SELECT "+_ID+" , "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM "+TBL_NOTIFICATION+" ORDER BY "+"strftime(%s,"+_DATE_TIME+") DESC"; 

Le meilleur moyen de stocker la date dans SQlite DB consiste à stocker les DateTimeMilliseconds actuelles. Voici l’extrait de code à faire_

  1. Récupère les DateTimeMilliseconds
 public static long getTimeMillis(Ssortingng dateSsortingng, Ssortingng dateFormat) throws ParseException { /*Use date format as according to your need! Ex. - yyyy/MM/dd HH:mm:ss */ Ssortingng myDate = dateSsortingng;//"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 firebase database
 public void insert(Context mContext, long dateTimeMillis, Ssortingng 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 à convertir les données de la firebase database en millisecondes à la date correspondante. Vous trouverez ci-dessous l’extrait de code à utiliser pour faire la même chose_

  1. Convertissez la date en millisecondes en chaîne de date.
 public static Ssortingng getDate(long milliSeconds, Ssortingng 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, enfin, récupérez les données et voyez son fonctionnement …
 public ArrayList fetchData() { ArrayList listOfAllDates = new ArrayList(); Ssortingng cDate = null; MyDatabaseHelper dbHelper = new MyDatabaseHelper("your_app_context"); database = dbHelper.getWritableDatabase(); Ssortingng[] columns = new Ssortingng[] {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 tout le monde! 🙂