Je ne parviens pas à utiliser les dates de mon application Android qui utilise SQLite. J'ai quelques questions:
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;
}
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.
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);
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));
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.
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 ...
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.
Le meilleur moyen de stocker date
in SQlite DB consiste à stocker le DateTimeMilliseconds
actuel. Ci-dessous l'extrait de code pour le faire_
- 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;
}
- 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_
- 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());
}
- 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! :)
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
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);
}
"SELECT "+_ID+" , "+_DESCRIPTION +","+_CREATED_DATE +","+_DATE_TIME+" FROM "+TBL_NOTIFICATION+" ORDER BY "+"strftime(%s,"+_DATE_TIME+") DESC";