web-dev-qa-db-fra.com

Vérifiez si la chaîne a une date, n'importe quel format

Comment vérifier si une chaîne peut être analysée à une date?

  • 19 janv.1990
  • 19 janvier 1990
  • 19 janv.1990
  • 19/01/1990
  • 19/01/90
  • 1990
  • Jan 1990
  • Janvier1990

Ce sont toutes des dates valides. S'il y a un problème concernant le manque d'espace entre les éléments de l'élément n ° 3 et le dernier élément ci-dessus, cela peut être facilement résolu en insérant automatiquement un espace entre les lettres/caractères et les chiffres, si nécessaire.

Mais d'abord, les bases:

J'ai essayé de le mettre dans un if statement:

if datetime.strptime(item, '%Y') or datetime.strptime(item, '%b %d %y') or datetime.strptime(item, '%b %d %Y')  or datetime.strptime(item, '%B %d %y') or datetime.strptime(item, '%B %d %Y'):

Mais c'est dans un bloc try-except et continue de renvoyer quelque chose comme ceci:

16343 time data 'JUNE1890' does not match format '%Y'

Sauf s'il remplit la première condition de l'instruction if.

Pour clarifier, je n'ai pas réellement besoin de la valeur de la date - je veux juste savoir si elle l'est. Idéalement, cela aurait été quelque chose comme ceci:

if item is date:
    print date
else:
    print "Not a date"

Est-ce qu'il y a un moyen de faire ça?

34
zack_falcon

La fonction parse dans dateutils.parser est capable d'analyser de nombreux formats de chaîne de date dans un objet datetime.

Si vous voulez simplement savoir si une chaîne particulière pourrait représenter ou contenir une date valide, vous pouvez essayer la fonction simple suivante:

from dateutil.parser import parse

def is_date(string, fuzzy=False):
    """
    Return whether the string can be interpreted as a date.

    :param string: str, string to check for date
    :param fuzzy: bool, ignore unknown tokens in string if True
    """
    try: 
        parse(string, fuzzy=fuzzy)
        return True

    except ValueError:
        return False

Vous avez alors:

>>> is_date("1990-12-1")
True
>>> is_date("2005/3")
True
>>> is_date("Jan 19, 1990")
True
>>> is_date("today is 2019-03-27")
False
>>> is_date("today is 2019-03-27", fuzzy=True)
True
>>> is_date("Monday at 12:01am")
True
>>> is_date("xyz_not_a_date")
False
>>> is_date("yesterday")
False

Analyse personnalisée

parse peut reconnaître certaines chaînes comme des dates que vous ne voulez pas traiter comme des dates. Par exemple:

  • L'analyse de "12" Et "1999" Renverra un objet datetime représentant la date actuelle avec le jour et l'année substitués au nombre dans la chaîne

  • "23, 4" Et "23 4" Seront analysés comme datetime.datetime(2023, 4, 16, 0, 0).

  • "Friday" Renverra la date du vendredi le plus proche à l'avenir.
  • De même "August" Correspond à la date actuelle avec le mois changé en août.

De plus, parse n'est pas sensible aux paramètres régionaux, donc ne reconnaît pas les mois ou les jours de la semaine dans des langues autres que l'anglais.

Ces deux problèmes peuvent être résolus dans une certaine mesure en utilisant une classe parserinfo personnalisée, qui définit la façon dont les noms de mois et de jour sont reconnus:

from dateutil.parser import parserinfo

class CustomParserInfo(parserinfo):

    # three months in Spanish for illustration
    MONTHS = [("Enero", "Enero"), ("Feb", "Febrero"), ("Marzo", "Marzo")]

Une instance de cette classe peut ensuite être utilisée avec parse:

>>> parse("Enero 1990")
# ValueError: Unknown string format
>>> parse("Enero 1990", parserinfo=CustomParserInfo())
datetime.datetime(1990, 1, 27, 0, 0)
67
Alex Riley

Si vous souhaitez analyser ces formats particuliers, vous pouvez simplement les comparer à une liste de formats:

txt='''\
Jan 19, 1990
January 19, 1990
Jan 19,1990
01/19/1990
01/19/90
1990
Jan 1990
January1990'''

import datetime as dt

fmts = ('%Y','%b %d, %Y','%b %d, %Y','%B %d, %Y','%B %d %Y','%m/%d/%Y','%m/%d/%y','%b %Y','%B%Y','%b %d,%Y')

parsed=[]
for e in txt.splitlines():
    for fmt in fmts:
        try:
           t = dt.datetime.strptime(e, fmt)
           parsed.append((e, fmt, t)) 
           break
        except ValueError as err:
           pass

# check that all the cases are handled        
success={t[0] for t in parsed}
for e in txt.splitlines():
    if e not in success:
        print e    

for t in parsed:
    print '"{:20}" => "{:20}" => {}'.format(*t) 

Tirages:

"Jan 19, 1990        " => "%b %d, %Y           " => 1990-01-19 00:00:00
"January 19, 1990    " => "%B %d, %Y           " => 1990-01-19 00:00:00
"Jan 19,1990         " => "%b %d,%Y            " => 1990-01-19 00:00:00
"01/19/1990          " => "%m/%d/%Y            " => 1990-01-19 00:00:00
"01/19/90            " => "%m/%d/%y            " => 1990-01-19 00:00:00
"1990                " => "%Y                  " => 1990-01-01 00:00:00
"Jan 1990            " => "%b %Y               " => 1990-01-01 00:00:00
"January1990         " => "%B%Y                " => 1990-01-01 00:00:00
11
dawg