Conversion d'une chaîne en datetime


2188

J'ai une énorme liste de dates comme celle-ci sous forme de chaînes:

Jun 1 2005  1:33PM
Aug 28 1999 12:00AM

Je vais les replacer dans des champs datetime appropriés dans une base de données, je dois donc les transformer en objets datetime réels.

Cela passe par l'ORM de Django, donc je ne peux pas utiliser SQL pour effectuer la conversion lors de l'insertion.


6
À moins que vous ne soyez sûr qu'un format gère chaque date-heure (pas de '', pas de NaN, pas d'incomplétude, pas de décalage de format, pas de caractères de fin, de fuseaux horaires, d'horodatages en microsecondes ou autre texte ...), l'exception-bonheur de strptime()vous rendra fou, à moins que vous ne l'enveloppiez. Voir ma réponse, basée sur la réponse d'Or Weis à cela
smci

L'approche la plus paresseuse et la plus largement utilisable que je connaisse est datparser (consultez blog.scrapinghub.com/2015/11/09/… ). Il fonctionne même avec des expressions temporelles en langage naturel dans plusieurs langues prêtes à l'emploi. Je suppose que ça peut être lent cependant.
Armando

Réponses:


3463

datetime.strptimeest la routine principale pour l'analyse des chaînes en temps de données. Il peut gérer toutes sortes de formats, le format étant déterminé par une chaîne de format que vous lui donnez:

from datetime import datetime

datetime_object = datetime.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')

L' datetimeobjet résultant est naïf de fuseau horaire.

Liens:

Remarques:

  • strptime = "temps d'analyse de chaîne"
  • strftime = "heure de format de chaîne"
  • Prononcez-le à voix haute aujourd'hui et vous n'aurez plus à le rechercher dans 6 mois.

7
'% b', '% p' ​​peut échouer dans des paramètres régionaux non anglais.
jfs

15
@User Vous devrez savoir à l'avance pour exclure cette partie de la chaîne de formatage, mais si vous voulez un dateau lieu d'un datetime, le datetimegérer datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)
correctement

14
Si vous savez que la chaîne représente une date / heure en UTC, vous pouvez obtenir un datetimeobjet sensible au fuseau horaire en ajoutant cette ligne en Python 3:from datetime import timezone; datetime_object = datetime_object.replace(tzinfo=timezone.utc)
Flimm

111
Je cherchais"%Y-%m-%d %H:%M:%S"
Martin Thoma

4
@AminahNuraini J'ai contourné un problème similaire en faisant from datetime import datetimeau lieu de simplement import datetime.
Max Strater

831

Utilisez la bibliothèque de dateutil tierce :

from dateutil import parser
parser.parse("Aug 28 1999 12:00AM")  # datetime.datetime(1999, 8, 28, 0, 0)

Il peut gérer la plupart des formats de date, y compris celui que vous devez analyser. C'est plus pratique que strptimecar il peut deviner le bon format la plupart du temps.

Il est très utile pour écrire des tests, où la lisibilité est plus importante que les performances.

Vous pouvez l'installer avec:

pip install python-dateutil

86
Sachez que pour de grandes quantités de données, ce n'est peut-être pas la façon la plus optimale d'aborder le problème. Deviner le format à chaque fois peut être horriblement lent.
Paweł Polewicz

14
C'est bien, mais ce serait bien d'avoir une solution intégrée plutôt que de devoir recourir à un tiers.
brian buck

1
Lorsque j'essaye d'analyser "32 jan", cela me renvoie "2032-01-06" .. ce qui est incorrect. existe-t-il un moyen de vérifier si la chaîne est une date valide ou non
Kartik Domadiya

6
@Reef: 5 fois plus lent selon mon benchmark rapide et sale. Pas si horriblement lent que je m'y attendais.
Antony Hatchkins

2
A ses propres problèmes - comme, par exemple, la suppression silencieuse des informations de fuseau horaire à partir des heures: essayez parser.parse ('15: 55EST ') et comparez-le avec parser.parse ('15 .55CST') à titre d'exemple
F1Rumors

490

Découvrez strptime dans le module de temps . C'est l'inverse de strftime .

$ python
>>> import time
>>> my_time = time.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')
time.struct_time(tm_year=2005, tm_mon=6, tm_mday=1,
                 tm_hour=13, tm_min=33, tm_sec=0,
                 tm_wday=2, tm_yday=152, tm_isdst=-1)

timestamp = time.mktime(my_time)
# convert time object to datetime
from datetime import datetime
my_datetime = datetime.fromtimestamp(timestamp)
# convert time object to date
from datetime import date
my_date = date.fromtimestamp(timestamp)

16
D'après ce que je comprends, cette réponse ne produit que des objets temporels, pas des objets datetime - c'est pourquoi la réponse serait enterrée par rapport à la réponse de Patrick.
Alexander Bird

Existe-t-il un moyen de définir le format datetime par défaut de DateTimeField?
cheville ouvrière du

3
Comme Alexander l'a dit, cela renvoie un struct_time, pas un datetime. Bien sûr, vous pouvez le convertir en datetime, mais la réponse de Patrick est plus simple si vous voulez un objet datetime à la fin.
Leandro Alves

Il n'y a rien de tel que strtotime dans la bibliothèque python standard, mais dateutil a un analyseur qui reconnaît beaucoup de formats de date optimisés.
Geoff Gerrietts

1
@BenBlank: '% b', '% p' ​​peut échouer dans des paramètres régionaux non anglais.
jfs

113

J'ai mis sur pied un projet qui peut convertir des expressions vraiment soignées. Découvrez l' horodatage .

Voici quelques exemples ci-dessous:

pip install timestring
>>> import timestring
>>> timestring.Date('monday, aug 15th 2015 at 8:40 pm')
<timestring.Date 2015-08-15 20:40:00 4491909392>
>>> timestring.Date('monday, aug 15th 2015 at 8:40 pm').date
datetime.datetime(2015, 8, 15, 20, 40)
>>> timestring.Range('next week')
<timestring.Range From 03/10/14 00:00:00 to 03/03/14 00:00:00 4496004880>
>>> (timestring.Range('next week').start.date, timestring.Range('next week').end.date)
(datetime.datetime(2014, 3, 10, 0, 0), datetime.datetime(2014, 3, 14, 0, 0))

2
Sensationnel. Sensationnel. Sensationnel. Sensationnel. C'est tellement simple. J'ai une chaîne datetime et je veux juste retirer l'année. Aussi simple que: import timestring timestring.Date('27 Mar 2014 12:32:29 GMT').yearCette bibliothèque l'a rendu si facile! Je vous remercie.
brandonjp

De rien. J'adorerais vos commentaires et vos idées sur l'amélioration de ce package. Faites-le moi savoir, utilisez les problèmes de github. Merci!
Steve Peak

Salut steve, le module est super. Ce serait bien d'avoir un attribut de chaîne en semaine également. Sinon, je ne sais pas si vous commencez du lundi ou du dimanche
Anake

1
Il ne convertit pas correctement comme `` 5 février 2017 '' et `` 5 février 2017 '' (qui sont des formats populaires dans certains cercles, et IMO certains des meilleurs formats de date pour la clarté et la lisibilité). Il les stocke sous 2017-02-01. Idem pour le 5 / février / 2017 (il fait correctement le 5 février / 2017, cependant); aucun de ces deux derniers n'est un format que j'ai jamais vu utilisé à ma connaissance, mais j'ai pensé que je le ferais remarquer quand même.
Brōtsyorfuzthrāx

2
AVERTISSEMENT: ce package ne semble avoir été maintenu ou amélioré à aucun moment au cours des 5 dernières années et analyse régulièrement les dates manifestement incorrectes. Par exemple, l'instanciation Date("20180912")analyse en quelque sorte une valeur de 2018-11-21. À utiliser à vos risques et périls.
bsplosion

54

N'oubliez pas ceci et vous n'avez plus besoin de vous perdre dans la conversion datetime.

Chaîne en objet datetime = strptime

objet datetime vers d'autres formats = strftime

Jun 1 2005 1:33PM

est égal à

%b %d %Y %I:%M%p

% b mois comme nom abrégé des paramètres régionaux (juin)

% d Jour du mois sous la forme d'un nombre décimal à zéro (1)

% Y Année avec siècle comme nombre décimal (2015)

% I heure (horloge de 12 heures) sous forme de nombre décimal à zéro (01)

% M Minute sous forme de nombre décimal à zéro (33)

% p équivalent local de AM ou PM (PM)

vous avez donc besoin de strptime c'est- stringà- dire la conversion en

>>> dates = []
>>> dates.append('Jun 1 2005  1:33PM')
>>> dates.append('Aug 28 1999 12:00AM')
>>> from datetime import datetime
>>> for d in dates:
...     date = datetime.strptime(d, '%b %d %Y %I:%M%p')
...     print type(date)
...     print date
... 

Production

<type 'datetime.datetime'>
2005-06-01 13:33:00
<type 'datetime.datetime'>
1999-08-28 00:00:00

Et si vous avez différents formats de dates, vous pouvez utiliser panda ou dateutil.parse

>>> import dateutil
>>> dates = []
>>> dates.append('12 1 2017')
>>> dates.append('1 1 2017')
>>> dates.append('1 12 2017')
>>> dates.append('June 1 2017 1:30:00AM')
>>> [parser.parse(x) for x in dates]

Production

[datetime.datetime(2017, 12, 1, 0, 0), datetime.datetime(2017, 1, 1, 0, 0), datetime.datetime(2017, 1, 12, 0, 0), datetime.datetime(2017, 6, 1, 1, 30)]

% S pour les secondes sous forme décimale
optimiste

1
Ne %bcassera pas si vous analysez une date en anglais sur une machine qui n'a pas de paramètres régionaux en anglais?
bfontaine

48

En Python> = 3.7.0,

pour convertir la chaîne AAAA-MM-JJ en objet datetime , datetime.fromisoformatpourrait être utilisé.

>>> from datetime import datetime

>>> date_string = "2012-12-12 10:10:10"
>>> print (datetime.fromisoformat(date_string))
>>> 2012-12-12 10:10:10

32

De nombreux horodatages ont un fuseau horaire implicite. Pour vous assurer que votre code fonctionnera dans chaque fuseau horaire, vous devez utiliser UTC en interne et attacher un fuseau horaire chaque fois qu'un objet étranger pénètre dans le système.

Python 3.2+:

>>> datetime.datetime.strptime(
...     "March 5, 2014, 20:13:50", "%B %d, %Y, %H:%M:%S"
... ).replace(tzinfo=datetime.timezone(datetime.timedelta(hours=-3)))

3
Pourquoi gardez-vous la mktime()1ère méthode laide et parfois erronée ( pendant les transitions DST) si vous connaissez la 2ème méthode ( datetime.strptime())? Si vous voulez éviter une exception pendant une seconde calendar.timegm(datetime(1970,1,1)+timedelta(seconds=timegm(time.strptime(..)))).replace(tzinfo=timezone(timedelta(-3)))
intercalaire

29

Voici deux solutions utilisant Pandas pour convertir des dates formatées sous forme de chaînes en objets datetime.date.

import pandas as pd

dates = ['2015-12-25', '2015-12-26']

# 1) Use a list comprehension.
>>> [d.date() for d in pd.to_datetime(dates)]
[datetime.date(2015, 12, 25), datetime.date(2015, 12, 26)]

# 2) Convert the dates to a DatetimeIndex and extract the python dates.
>>> pd.DatetimeIndex(dates).date.tolist()
[datetime.date(2015, 12, 25), datetime.date(2015, 12, 26)]

Timings

dates = pd.DatetimeIndex(start='2000-1-1', end='2010-1-1', freq='d').date.tolist()

>>> %timeit [d.date() for d in pd.to_datetime(dates)]
# 100 loops, best of 3: 3.11 ms per loop

>>> %timeit pd.DatetimeIndex(dates).date.tolist()
# 100 loops, best of 3: 6.85 ms per loop

Et voici comment convertir les exemples de date-heure d'origine de l'OP:

datetimes = ['Jun 1 2005  1:33PM', 'Aug 28 1999 12:00AM']

>>> pd.to_datetime(datetimes).to_pydatetime().tolist()
[datetime.datetime(2005, 6, 1, 13, 33), 
 datetime.datetime(1999, 8, 28, 0, 0)]

Il existe de nombreuses options pour la conversion des chaînes en horodatages Pandas à l'aide to_datetime, alors vérifiez les documents si vous avez besoin de quelque chose de spécial.

De même, les horodatages ont de nombreuses propriétés et méthodes accessibles en plus de.date


26

Personnellement, j'aime la solution utilisant le parsermodule, qui est la deuxième réponse à cette question et est magnifique, car vous n'avez pas à construire de littéraux de chaîne pour le faire fonctionner. MAIS , un inconvénient est qu'il est 90% plus lent que la réponse acceptée strptime.

from dateutil import parser
from datetime import datetime
import timeit

def dt():
    dt = parser.parse("Jun 1 2005  1:33PM")
def strptime():
    datetime_object = datetime.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')

print(timeit.timeit(stmt=dt, number=10**5))
print(timeit.timeit(stmt=strptime, number=10**5))
>10.70296801342902
>1.3627995655316933

Tant que vous ne le faites pas un million de fois, je pense toujours que la parserméthode est plus pratique et gérera la plupart des formats de temps automatiquement.


24

Quelque chose qui n'est pas mentionné ici et qui est utile: ajouter un suffixe à la journée. J'ai découplé la logique du suffixe afin que vous puissiez l'utiliser pour n'importe quel nombre que vous aimez, pas seulement pour les dates.

import time

def num_suffix(n):
    '''
    Returns the suffix for any given int
    '''
    suf = ('th','st', 'nd', 'rd')
    n = abs(n) # wise guy
    tens = int(str(n)[-2:])
    units = n % 10
    if tens > 10 and tens < 20:
        return suf[0] # teens with 'th'
    elif units <= 3:
        return suf[units]
    else:
        return suf[0] # 'th'

def day_suffix(t):
    '''
    Returns the suffix of the given struct_time day
    '''
    return num_suffix(t.tm_mday)

# Examples
print num_suffix(123)
print num_suffix(3431)
print num_suffix(1234)
print ''
print day_suffix(time.strptime("1 Dec 00", "%d %b %y"))
print day_suffix(time.strptime("2 Nov 01", "%d %b %y"))
print day_suffix(time.strptime("3 Oct 02", "%d %b %y"))
print day_suffix(time.strptime("4 Sep 03", "%d %b %y"))
print day_suffix(time.strptime("13 Nov 90", "%d %b %y"))
print day_suffix(time.strptime("14 Oct 10", "%d %b %y"))​​​​​​​

17
In [34]: import datetime

In [35]: _now = datetime.datetime.now()

In [36]: _now
Out[36]: datetime.datetime(2016, 1, 19, 9, 47, 0, 432000)

In [37]: print _now
2016-01-19 09:47:00.432000

In [38]: _parsed = datetime.datetime.strptime(str(_now),"%Y-%m-%d %H:%M:%S.%f")

In [39]: _parsed
Out[39]: datetime.datetime(2016, 1, 19, 9, 47, 0, 432000)

In [40]: assert _now == _parsed

16

Exemple d'objet datetime compatible avec Django Timezone.

import datetime
from django.utils.timezone import get_current_timezone
tz = get_current_timezone()

format = '%b %d %Y %I:%M%p'
date_object = datetime.datetime.strptime('Jun 1 2005  1:33PM', format)
date_obj = tz.localize(date_object)

Cette conversion est très importante pour Django et Python lorsque vous avez USE_TZ = True:

RuntimeWarning: DateTimeField MyModel.created received a naive datetime (2016-03-04 00:00:00) while time zone support is active.

12

Créez une petite fonction utilitaire comme:

def date(datestr="", format="%Y-%m-%d"):
    from datetime import datetime
    if not datestr:
        return datetime.today().date()
    return datetime.strptime(datestr, format).date()

C'est assez polyvalent:

  • Si vous ne passez aucun argument, il renverra la date du jour.
  • Il existe un format de date par défaut que vous pouvez remplacer.
  • Vous pouvez facilement le modifier pour renvoyer une date / heure.

2
formatest un mot réservé en python et ne doit pas être utilisé comme nom de variable.
déchiquetage le

12

Il serait utile pour convertir la chaîne en datetime et également avec le fuseau horaire

def convert_string_to_time(date_string, timezone):
    from datetime import datetime
    import pytz
    date_time_obj = datetime.strptime(date_string[:26], '%Y-%m-%d %H:%M:%S.%f')
    date_time_obj_timezone = pytz.timezone(timezone).localize(date_time_obj)

    return date_time_obj_timezone

date = '2018-08-14 13:09:24.543953+00:00'
TIME_ZONE = 'UTC'
date_time_obj_timezone = convert_string_to_time(date, TIME_ZONE)

9

la flèche offre de nombreuses fonctions utiles pour les dates et les heures. Ce morceau de code fournit une réponse à la question et montre que la flèche est également capable de formater facilement les dates et d'afficher des informations pour d'autres paramètres régionaux.

>>> import arrow
>>> dateStrings = [ 'Jun 1  2005 1:33PM', 'Aug 28 1999 12:00AM' ]
>>> for dateString in dateStrings:
...     dateString
...     arrow.get(dateString.replace('  ',' '), 'MMM D YYYY H:mmA').datetime
...     arrow.get(dateString.replace('  ',' '), 'MMM D YYYY H:mmA').format('ddd, Do MMM YYYY HH:mm')
...     arrow.get(dateString.replace('  ',' '), 'MMM D YYYY H:mmA').humanize(locale='de')
...
'Jun 1  2005 1:33PM'
datetime.datetime(2005, 6, 1, 13, 33, tzinfo=tzutc())
'Wed, 1st Jun 2005 13:33'
'vor 11 Jahren'
'Aug 28 1999 12:00AM'
datetime.datetime(1999, 8, 28, 0, 0, tzinfo=tzutc())
'Sat, 28th Aug 1999 00:00'
'vor 17 Jahren'

Voir http://arrow.readthedocs.io/en/latest/ pour en savoir plus.


6

Vous pouvez utiliser easy_date pour simplifier:

import date_converter
converted_date = date_converter.string_to_datetime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')

4

Si vous ne voulez que le format de date, vous pouvez le convertir manuellement en passant vos champs individuels comme:

>>> import datetime
>>> date = datetime.date(int('2017'),int('12'),int('21'))
>>> date
datetime.date(2017, 12, 21)
>>> type(date)
<type 'datetime.date'>

Vous pouvez passer vos valeurs de chaîne fractionnées pour les convertir en type de date comme:

selected_month_rec = '2017-09-01'
date_formate = datetime.date(int(selected_month_rec.split('-')[0]),int(selected_month_rec.split('-')[1]),int(selected_month_rec.split('-')[2]))

Vous obtiendrez la valeur résultante au format date.


2

Vous pouvez également vérifier dateparser

dateparser fournit des modules pour analyser facilement les dates localisées dans presque tous les formats de chaîne couramment trouvés sur les pages Web.

Installer:

$ pip install dateparser

C'est, je pense, la façon la plus simple d'analyser les dates.

La façon la plus simple consiste à utiliser la dateparser.parsefonction, qui englobe la plupart des fonctionnalités du module.

Exemple de code:

import dateparser

t1 = 'Jun 1 2005  1:33PM'
t2 = 'Aug 28 1999 12:00AM'

dt1 = dateparser.parse(t1)
dt2 = dateparser.parse(t2)

print(dt1)
print(dt2)

Production:

2005-06-01 13:33:00
1999-08-28 00:00:00

1

Voir ma réponse .

Dans les données du monde réel, c'est un vrai problème: plusieurs formats de date, incompatibles, incomplets, incohérents et multilingues / région, souvent mélangés librement dans un seul ensemble de données. Ce n'est pas ok pour le code de production d'échouer, et encore moins de faire exception comme un renard.

Nous devons essayer ... attraper plusieurs formats datetime fmt1, fmt2, ..., fmtn et supprimer / gérer les exceptions (de strptime()) pour tous ceux qui ne correspondent pas (et en particulier, éviter d'avoir besoin d'une échelle indentée yukky n-deep d'essayer ..catch clauses). De ma solution

def try_strptime(s, fmts=['%d-%b-%y','%m/%d/%Y']):
    for fmt in fmts:
        try:
            return datetime.strptime(s, fmt)
        except:
            continue

    return None # or reraise the ValueError if no format matched, if you prefer

La question ne dit rien sur les "formats de date multiples, incompatibles, incomplets, incohérents et multilingues / régionaux", etc. Cela peut être un vrai problème, mais pas pertinent ici.
RoG

1
@RoG: Il n'a jamais dit qu'ils ne l'étaient pas, et cela impliquait qu'ils étaient: "énorme liste ... base de données" . Dans la plupart des bases de données / fichiers journaux sur lesquels j'ai travaillé (même de petite taille), il y avait plusieurs formats de date, des identifiants de fuseau horaire, MM-DD, etc. il n'obtient pas le format attendu (même retourner None ou '' est plus acceptable). D'où la nécessité de plusieurs formats. Par conséquent, cela répond à la question posée, et j'ai passé un peu de temps à trouver la manière la plus Pythonique de gérer les erreurs de plusieurs formats.
smci

"liste énorme ... base de données" implique simplement qu'il y en a beaucoup, pas qu'ils soient tous de formats différents. Il est tout à fait acceptable d'écrire du code qui lit un seul format, si vous savez qu'il y a un seul format en entrée. Dans ce cas, il devrait planter s'il est transmis quelque chose qui n'est pas au bon format.
RoG

@RoG: il est inacceptable d'écrire du code de production qui plante sur Unicode mal formaté / tronqué / manquant / données, NaNs, M / D / Y vs format D / M / Y, YY vs YYYY, etc. Surtout si ceux-ci les exceptions peuvent être évitées avec une solution à sept lignes comme je l'ai montré. La plupart des "énormes bases de données" du monde réel sont comme ça. Ce n'est pas parce que le PO n'a pas dit explicitement que ce n'est pas le contexte typique. Je ne vais pas me chamailler avec toi. Sur quel type d'ensembles de données travaillez-vous et pourquoi pensez-vous que ces hypothèses sont raisonnables? À moins que nous ne parlions que du code du jouet qui nécessite une intervention constante.
smci

1
Il semble un peu stupide de supposer avec une certitude absolue que le PO doit avoir des données qui n'ont jamais d'incohérences. Oui, il est possible d'avoir des données comme ça, mais non, nous ne pouvons pas supposer que c'est le cas ici. J'ai pensé que cette réponse était utile, certainement pour moi dont la recherche de réponses similaires à une question très similaire, où les incohérences sont certainement un problème.
Paul Miller

1
emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")
emp.info()

il affiche la colonne "Date de début" et la "Dernière heure de connexion" sont toutes deux "objet = chaînes" dans la trame de données

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
First Name           933 non-null object
Gender               855 non-null object
Start Date           1000 non-null object

Last Login Time      1000 non-null object
Salary               1000 non-null int64
Bonus %              1000 non-null float64
Senior Management    933 non-null object
Team                 957 non-null object
dtypes: float64(1), int64(1), object(6)
memory usage: 62.6+ KB

En utilisant l' parse_datesoption read_csvmentionnée, vous pouvez convertir votre chaîne datetime au format pandas datetime.

emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv", parse_dates=["Start Date", "Last Login Time"])
emp.info()


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
First Name           933 non-null object
Gender               855 non-null object
Start Date           1000 non-null datetime64[ns]
Last Login Time      1000 non-null datetime64[ns]
Salary               1000 non-null int64
Bonus %              1000 non-null float64
Senior Management    933 non-null object
Team                 957 non-null object
dtypes: datetime64[ns](2), float64(1), int64(1), object(4)
memory usage: 62.6+ KB
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.