C'est un problème que j'ai rencontré plusieurs fois. Imaginez que vous ayez un enregistrement que vous souhaitez stocker dans une table de base de données. Cette table a une colonne DateTime appelée "date_created". Cet enregistrement particulier a été créé il y a longtemps, et vous n'êtes pas vraiment sûr de la date exacte, mais vous connaissez l'année et le mois. D'autres disques que vous connaissez juste l'année. D'autres enregistrements que vous connaissez le jour, le mois et l'année.
Vous ne pouvez pas utiliser un champ DateTime, car "mai 1978" n'est pas une date valide. Si vous le divisez en plusieurs colonnes, vous perdez la possibilité d'interroger. Quelqu'un d'autre a-t-il rencontré cela, si oui, comment l'avez-vous géré?
Pour clarifier le système que je construis, c'est un système qui suit les archives. Certains contenus ont été produits il y a longtemps et tout ce que nous savons, c'est "mai 1978". Je pourrais le stocker comme 1er mai 1978, mais seulement avec une certaine manière de dénoter que cette date n'est précise qu'au mois. De cette façon, quelques années plus tard, lorsque je récupère cette archive, je ne suis pas confus lorsque les dates ne correspondent pas.
Pour mes besoins, il est important de différencier "jour inconnu en mai 1978" par "1er mai 1978". De plus, je ne voudrais pas stocker les inconnues à 0, comme "0 mai 1978" car la plupart des systèmes de base de données rejetteront cela comme une valeur de date non valide.
Stockez toutes les dates dans le champ DATE normal de la base de données et ayez un champ de précision supplémentaire sur la précision du champ DATE.
date_created DATE,
date_created_accuracy INTEGER,
date_created_accuracy: 1 = date exacte, 2 = mois, 3 = année.
Si votre date est floue (par exemple, mai 1980), enregistrez-la au début de la période (par exemple, le 1er mai 1980). Ou si votre date est exacte à l'année (par exemple 1980), enregistrez-la au 1er janvier. 1980 avec valeur de précision correspondante.
Cette façon peut facilement interroger d'une manière quelque peu naturelle et avoir encore une idée de la précision des dates. Par exemple, cela vous permet de rechercher des dates entre Jan 1st 1980
et Feb 28th 1981
, et obtenez des dates floues 1980
et May 1980
.
Si vous n'avez pas besoin d'utiliser ce type de données comme informations de date-heure normales, n'importe quel format de chaîne simple suffirait.
Mais si vous devez conserver toutes les fonctionnalités, je peux penser à deux solutions, nécessitant toutes deux des informations supplémentaires stockées dans la base de données:
min date
et max date
champs, qui ont des valeurs différentes pour les données "incomplètes", mais coïncideront pour des dates précises.type
aux enregistrements et conservez les informations manquantes.Il s'agit vraiment plus d'une définition des exigences que d'un problème technique - ce sur quoi vous devez vous concentrer est "comment pouvons-nous définir les dates dans le passé" et la solution technique coulera.
Les fois où j'ai dû aborder quelque chose comme ça, nous avons généralement:
Parfois, il faut faire quelque chose comme rendre les dates floues - par exemple, une date peut avoir besoin de répondre à une requête pour quoi que ce soit en mai 1978. C'est faisable - il suffit de rendre vos champs create_date 2, les anciens enregistrements obtiennent un 30 jours répartis comme il convient, les nouveaux obtiennent 2 valeurs identiques.
La façon la plus simple d'indiquer si la date est exacte est de créer un champ de précision INT (1) avec NULL par défaut
Si la date est exacte, enregistrez la date et l'heure dans "date_created" et laissez la précision NULL
Si la date n'est précise qu'au mois, enregistrez la date et l'heure au 1er du mois avec la valeur de précision 1
Si la date n'est exacte que pour l'année, date-heure du 1er janvier avec valeur d'exactitude 2
Vous pouvez utiliser différents nombres pour contenir différentes valeurs telles que le premier trimestre, etc.
Dans le passé, j'ai enregistré des dates avec précision comme date de début et date de fin. Le jour 21 mai 2012 serait représenté comme début = 12 h 00, 21 mai 2012 et fin = 12 h 00, 22 mai 2012. L'année 2012 serait représentée comme début = 12 h, 1er janvier 2012 fin = 12 h, 1er janvier 2013.
Je ne sais pas si je recommanderais cette approche. Lorsque vous affichez les informations à l'utilisateur, vous devez détecter correctement qu'une plage de dates couvre exactement une journée afin d'afficher "le 25 mai" au lieu de deux points de terminaison trop spécifiques (ce qui signifie traiter de l'heure d'été, etc.).
Cependant, lorsque vous n'essayez pas de traduire en humain, la programmation avec les points d'extrémité est beaucoup plus facile qu'avec la précision centre +. Vous ne vous retrouvez pas avec beaucoup de cas. C'est plutôt sympa.
Pourquoi ne pas stocker deux dates.
Created_After et Created_Before. La sémantique réelle étant "créée le ou après" et "créée le ou avant"
Donc, si vous connaissez la date exacte, Created_After et Created_Before seront la même date.
Si vous savez que c'était la première semaine de mai 2000, alors Created_After = '2000-05-01' et Created_Before = '2000-05-07'.
Si vous ne connaissez que mai 1999, les valeurs seront "1999-05-01" et "1999-05-30".
S'il s'agit de "Summer of '42", les valeurs seraient "1942-06-01" et "1942-08-31".
Ce schéma est simple à interroger avec du SQL normal et assez facile à suivre pour un utilisateur non technique.
Par exemple, pour trouver tous les documents qui pourraient ont été créés en mai 2001:
SELECT * FROM DOCTAB WHERE Created_After < '2001-05-31' And Created_Before > 2001-05-01;
Inversement pour retrouver tous les documents qui ont été définitivement créés en mai 2001:
SELECT * FROM DOCTAB WHERE Created_After > '2001-05-01' And Created_Before < 2001-05-31;
ISO 8601 le format date-heure est fourni avec la définition de la durée, par ex.
2012-01-01P1M
(lire: 1er janvier 2012, période: 1 mois) est ce qui devrait être "en janvier 2012".
Je l'utiliserais pour stocker les données. Vous aurez peut-être besoin d'un champ de base de données de type String pour ce faire. C'est un sujet différent sur la façon d'effectuer une recherche judicieuse à ce sujet.
Une autre option serait de stocker les dates sous forme d'entiers de la forme YYYYMMDD
.
19510000
19510300
19510314
0
Vous pouvez stocker votre date floue dans un champ au lieu de deux champs de date ou d'une date et d'une précision comme le suggèrent la plupart des autres réponses.
Les requêtes sont toujours faciles:
SELECT * FROM table WHERE thedate>=19510000 and thedate<19520000
SELECT * FROM table where thedate>=19510300 and thedate<19510400
SELECT * FROM table where thedate=19510314
GetDateString(int fuzzyDate)
qui est assez facile à implémenter.99
Pour le 'padding' au lieu de 00
Pour le mois ou le jour.Si vous le divisez en plusieurs colonnes, vous perdez la possibilité d'interroger.
Dit qui? Voici ce que vous faites:
Donc, si je fais un insert comme: insert into thistable (Day, Month, Year) values (-1, 2, 2012);
alors TheDate deviendra le 2/1/2013 mais je saurai que c'est vraiment une date indéterminée en 2/2012 en raison du -1 dans le champ Jour.
Si je insert into thistable (TheDate) values ('2/5/2012');
alors le jour sera 5, le mois sera 2 et l'année sera 2012 et comme aucun d'entre eux n'est -1, je saurai que c'est la date exacte.
Je ne perds pas la possibilité d'interroger car le déclencheur d'insertion/mise à jour s'assure que mes 3 champs (jour, mois, année) produisent toujours une valeur DateTime dans TheDate qui peut être interrogée.
En règle générale, je les stocke toujours sous forme de dates pour que les requêtes générales soient toujours possibles même si elles sont légèrement moins précises.
S'il est important de connaître la précision que j'ai dans le passé, soit stocké une "fenêtre" de précision soit sous forme de +/- décimale, soit sous forme de recherche (jour, mois, année, etc.). Dans d'autres cas, au lieu de la fenêtre, je stocke simplement la valeur de date d'origine sous forme de chaîne et convertis ce que je peux en une date/heure, éventuellement 1978-05-01 00:00:00 et "mai 1978" pour votre exemple donné.
ISO 8601 spécifie également une syntaxe pour les "dates floues". Le 12 février 2012 à 15h serait "2012-02-12T15" et février 2012 pourrait être simplement "2012-02". Cela s'étend bien en utilisant le tri lexicographique standard:
$ (echo "2013-03"; echo "2013-03"; echo "2012-02-12T15"; echo "2012-02"; echo "2011") | sort
2011
2012
2012-02
2012-02-12T15
2013-03
Voici mon point de vue à ce sujet:
Passer de la date floue à l'objet datetime (qui s'intégrera dans une base de données)
import datetime
import iso8601
def fuzzy_to_datetime(fuzzy):
flen = len(fuzzy)
if flen == 4 and fuzzy.isdigit():
dt = datetime.datetime(year=int(fuzzy), month=1, day=1, microsecond=111111)
Elif flen == 7:
y, m = fuzzy.split('-')
dt = datetime.datetime(year=int(y), month=int(m), day=1, microsecond=222222)
Elif flen == 10:
y, m, d = fuzzy.split('-')
dt = datetime.datetime(year=int(y), month=int(m), day=int(d), microsecond=333333)
Elif flen >= 19:
dt = iso8601.parse_date(fuzzy)
else:
raise ValueError("Unable to parse fuzzy date: %s" % fuzzy)
return dt
Et puis une fonction qui prend l'objet datetime et le ramène dans une date floue.
def datetime_to_fuzzy(dt):
ms = str(dt.microsecond)
flag1 = ms == '111111'
flag2 = ms == '222222'
flag3 = ms == '333333'
is_first = dt.day == 1
is_jan1 = dt.month == 1 and is_first
if flag1 and is_jan1:
return str(dt.year)
if flag2 and is_first:
return dt.strftime("%Y-%m")
if flag3:
return dt.strftime("%Y-%m-%d")
return dt.isoformat()
Et puis un test unitaire. Ai-je raté des cas?
if __name__ == '__main__':
assert fuzzy_to_datetime('2001').isoformat() == '2001-01-01T00:00:00.111111'
assert fuzzy_to_datetime('1981-05').isoformat() == '1981-05-01T00:00:00.222222'
assert fuzzy_to_datetime('2012-02-04').isoformat() == '2012-02-04T00:00:00.333333'
assert fuzzy_to_datetime('2010-11-11T03:12:03Z').isoformat() == '2010-11-11T03:12:03+00:00'
exact = datetime.datetime(year=2001, month=1, day=1, microsecond=231)
assert datetime_to_fuzzy(exact) == exact.isoformat()
assert datetime_to_fuzzy(datetime.datetime(year=2001, month=1, day=1, microsecond=111111)) == '2001'
assert datetime_to_fuzzy(datetime.datetime(year=2001, month=3, day=1, microsecond=222222)) == '2001-03'
assert datetime_to_fuzzy(datetime.datetime(year=2001, month=6, day=6, microsecond=333333)) == '2001-06-06'
assert datetime_to_fuzzy(fuzzy_to_datetime('2002')) == '2002'
assert datetime_to_fuzzy(fuzzy_to_datetime('2002-05')) == '2002-05'
assert datetime_to_fuzzy(fuzzy_to_datetime('2002-02-13')) == '2002-02-13'
assert datetime_to_fuzzy(fuzzy_to_datetime('2010-11-11T03:12:03.293856+00:00')) == '2010-11-11T03:12:03.293856+00:00'
Il y a un cas d'angle où un événement qui s'est produit précisément à 2001-01-01T00:00:00.333333
mais le système ne sera interprété que comme "2001", mais cela semble très improbable.
Je travaille pour une maison d'édition qui traite de nombreux livres anciens où nous ne pouvons souvent pas obtenir les dates exactes des choses. Nous avons généralement deux champs pour une entrée de date donnée, la date et un circa booléen:
date date
dateCirca enum('Y', 'N')
Nous utilisons le champ de date pour indiquer la date d'un événement, ou une date qui est "suffisamment proche" dans le cas où nous ne connaissons pas la vraie date. Dans le cas où nous ne connaissons pas la vraie date, nous marquons le champ dateCirca
comme Y
et donnons une date suffisamment proche, qui est marquée comme "1er", comme
1st March, 2013 // We don't know the day of the month
1st January, 2013 // We don't know the month/day of the year
1st January, 2000 // We don't know the month/day/year, we only know the century
Il existe de nombreuses représentations possibles, et donc des schémas de base de données, pour stocker des dates-heures floues (ou même simplement des dates floues):
[1], [2] et [3] sont tous (implicitement) des intervalles uniformes, c'est-à-dire un ensemble de points (également) possibles dans le temps.
[4] est la plus expressive, c'est-à-dire lorsqu'elle autorise des phrases ou des phrases écrites possibles (ou au moins arbitrairement longues). Mais c'est aussi le plus difficile à travailler. À la limite, l'IA au niveau humain serait nécessaire pour gérer des valeurs arbitraires. En pratique, la plage de valeurs possibles devrait être sévèrement restreinte, et des valeurs `` structurées '' alternatives seraient probablement préférées pour de nombreuses opérations, par ex. tri, recherche.
[5] est probablement la représentation compacte la plus générale qui soit (quelque peu) pratique.
Les intervalles uniformes sont le moyen compact le plus simple de représenter un ensemble de valeurs date-heure (possibles).
Pour [1], les parties de la valeur date-heure sont ignorées, c'est-à-dire les parties correspondant à des unités plus fines que la précision ou l'exactitude indiquée; sinon, cela équivaut à [2] et le code de précision/exactitude est équivalent à un intervalle avec les mêmes unités (et une quantité implicite de 1).
[2] et [3] sont expressivement équivalents. [1] est strictement moins expressif que l'un ou l'autre car il existe des intervalles efficaces qui ne peuvent pas être représentés par [1], ex. une date-heure floue équivalente à un intervalle de 12 heures qui s'étend sur une limite de date.
[1] est plus facile à saisir pour les utilisateurs que toute autre représentation et devrait généralement nécessiter (au moins légèrement) moins de saisie. Si des dates-heures peuvent être saisies dans diverses représentations textuelles, par ex. "2013", "2014-3", "2015-5-2", "7/30/2016 11p", "2016-07-31 18:15", la précision ou l'exactitude pourrait également être déduite automatiquement de l'entrée .
L'exactitude ou la précision de [1] est également plus facile à convertir en un formulaire à transmettre aux utilisateurs, par ex. "2015-5 avec précision du mois" à "mai 2015", contre "13 mai 2015 2p, plus ou moins 13,5 jours" (notez que ce dernier ne peut de toute façon pas être représenté par [1]).
Pratiquement, les valeurs de chaîne devront être converties en d'autres représentations pour interroger, trier ou comparer autrement plusieurs valeurs. Ainsi, alors que tout langage naturel (humain) écrit est strictement plus expressif que [1], [2], [3] ou [5], nous n'avons pas encore les moyens de gérer bien au-delà des représentations ou formats de texte standard. Étant donné que, c'est probablement la représentation la moins utile en soi.
Un avantage de cette représentation est que les valeurs devraient, dans la pratique, être présentées aux utilisateurs telles quelles et ne nécessitent pas de transformation pour être facilement compréhensibles.
Les distributions de probabilité généralisent les représentations d'intervalle uniforme [1], [2], [3] et (sans doute) sont équivalentes à la représentation (générale) des chaînes [4].
Un avantage des distributions de probabilité sur les chaînes est que la première est sans ambiguïté.
[5-1] serait approprié pour les valeurs qui sont (principalement) conformes à une distribution existante, par ex. une valeur date-heure produite par un appareil dont les mesures sont connues (ou supposées) conformes à une distribution spécifique.
[5-2] est probablement le meilleur moyen (quelque peu) pratique de de manière compacte représenter des valeurs arbitraires de 'datetime floue'. Bien sûr, la calculabilité des distributions de probabilités spécifiques utilisées est importante et il y a certainement des problèmes intéressants (et peut-être impossibles) à résoudre lors de l'interrogation, du tri ou de la comparaison de différentes valeurs, mais une grande partie de cela est probablement déjà connue ou résolue quelque part dans l'existant la littérature mathématique et statistique est donc définitivement une représentation extrêmement générale et sans ambiguïté.