Convertir cadena en fecha y hora

2185

Tengo una gran lista de fechas y fechas como esta:

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

Voy a volver a insertarlos en los campos de fecha y hora adecuados en una base de datos, así que necesito convertirlos en objetos de fecha y hora reales.

Esto está pasando por el ORM de Django, así que no puedo usar SQL para hacer la conversión en la inserción.

Oli
fuente
66
A menos que esté seguro de que un formato maneja cada fecha y hora (sin '', sin NaNs, sin incompletos, sin discrepancias de formato, sin caracteres finales, zonas horarias, marcas de tiempo de microsegundos u otro texto ...), la felicidad excepcional de strptime()te volverá loco, a menos que lo envuelvas. Vea mi respuesta, basada en la respuesta de Or Weis a esto
smci
El enfoque más vago y más usable que conozco es dateparser (consulte blog.scrapinghub.com/2015/11/09/… ). Funciona incluso con expresiones de tiempo de lenguaje natural en varios idiomas fuera de la caja. Sin embargo, supongo que puede ser lento.
Armando
Hay un enlace útil aquí: stackabuse.com/converting-strings-to-datetime-in-python
GoingMyWay

Respuestas:

3462

datetime.strptimees la rutina principal para analizar cadenas en fechas y horas. Puede manejar todo tipo de formatos, con el formato determinado por una cadena de formato que le dé:

from datetime import datetime

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

La resultante datetime objeto es timezone-ingenuo.

Enlaces:

Notas:

  • strptime = "tiempo de análisis de cadena"
  • strftime = "hora de formato de cadena"
  • Pronuncialo en voz alta hoy y no tendrás que volver a buscarlo en 6 meses.
Patrick Harrington
fuente
77
'% b', '% p' ​​puede fallar en una configuración regional que no esté en inglés.
jfs
15
@Usuario Tendrá que saber con anticipación para excluir esa parte de la cadena de formato, pero si desea una en datelugar de una datetime, pasar a través lo datetimemaneja muy bien: datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)
Izkata
14
Si sabe que la cadena representa una fecha y hora en UTC, puede obtener un datetimeobjeto con reconocimiento de zona horaria agregando esta línea en Python 3:from datetime import timezone; datetime_object = datetime_object.replace(tzinfo=timezone.utc)
Flimm
111
Estaba buscando"%Y-%m-%d %H:%M:%S"
Martin Thoma
44
@AminahNuraini Resolví un problema similar haciendo en from datetime import datetimelugar de solo import datetime.
Max Strater
831

Utilice la biblioteca de dateutil de terceros :

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

Puede manejar la mayoría de los formatos de fecha, incluido el que necesita analizar. Es más conveniente questrptime ya que puede adivinar el formato correcto la mayor parte del tiempo.

Es muy útil para escribir pruebas, donde la legibilidad es más importante que el rendimiento.

Puedes instalarlo con:

pip install python-dateutil
Simon Willison
fuente
86
Tenga en cuenta que para grandes cantidades de datos, esta podría no ser la forma más óptima de abordar el problema. Adivinar el formato cada vez puede ser terriblemente lento.
Paweł Polewicz
14
Esto es bueno, pero sería bueno tener una solución integrada en lugar de tener que recurrir a un tercero.
Brian Buck
1
Cuando trato de analizar "32nd ene", me devuelve "2032-01-06" ... que es incorrecto. ¿Hay alguna manera de verificar si la cadena es una fecha válida o no?
Kartik Domadiya
66
@Reef: 5 veces más lento según mi punto de referencia rápido y sucio. No tan horriblemente lento como esperaba.
Antony Hatchkins
2
Tiene sus propios problemas, como, por ejemplo, descartar silenciosamente la información de zona horaria de veces: intente parser.parse ('15: 55EST ') y compare con parser.parse ('15 .55CST') como ejemplo
F1Rumors
490

Echa un vistazo a strptime en el módulo de tiempo . Es el inverso del tiempo de espera .

$ 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)
florín
fuente
16
Por lo que entiendo, esta respuesta solo genera objetos de tiempo, no objetos de fecha y hora, por lo que la respuesta estaría enterrada en comparación con la respuesta de Patrick.
Alexander Bird
¿Hay alguna manera de establecer el formato de fecha y hora predeterminado de DateTimeField?
Kingpin
3
Como dijo Alexander, esto devuelve un struct_time, no un datetime. Por supuesto, puede convertirlo a una fecha y hora, pero la respuesta de Patrick es más sencilla si desea un objeto de fecha y hora al final.
Leandro Alves
No hay nada como strtotime en la biblioteca estándar de python, pero dateutil tiene un analizador que reconoce muchos formatos de fecha de mejor esfuerzo.
Geoff Gerrietts
1
@BenBlank: '% b', '% p' ​​puede fallar en una configuración regional que no esté en inglés.
jfs
113

He reunido un proyecto que puede convertir algunas expresiones realmente ordenadas. Echa un vistazo a la cadena de tiempo .

Aquí hay algunos ejemplos a continuación:

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))
Steve Peak
fuente
2
Guau. Guau. Guau. Guau. Esto es muy facil. Tengo una cadena de fecha y hora y solo quiero sacar el año. Tan simple como: ¡ import timestring timestring.Date('27 Mar 2014 12:32:29 GMT').yearEsta lib lo hizo TAN FÁCIL! Gracias.
brandonjp
Eres muy bienvenido. Me gustaría conocer sus comentarios e ideas sobre cómo mejorar este paquete. Déjame saber, usa problemas de github ¡Gracias!
Steve Peak
Hola Steve, el módulo es genial. Sería bueno tener un atributo de cadena entre semana también. De lo contrario, no estoy seguro si comienza desde el lunes o el domingo
Anake
1
No se convierte como '5 de febrero de 2017' y '5 de febrero de 2017' correctamente (que son formatos populares en algunos círculos, y OMI, algunos de los mejores formatos de fecha para mayor claridad y legibilidad). Los almacena como 2017-02-01. Lo mismo para el 5 / Feb / 2017 (sin embargo, lo hace correctamente el 5 / Feb / 2017); ninguno de esos dos últimos son formatos que he visto utilizados, que yo sepa, pero pensé en señalarlo de todos modos.
Brōtsyorfuzthrāx
2
ADVERTENCIA: Este paquete no parece haberse mantenido o mejorado en ningún momento durante los últimos 5 años y analiza rutinariamente fechas obviamente incorrectas. Por ejemplo, instanciar de Date("20180912")alguna manera analiza un valor de 2018-11-21. Úselo bajo su propio riesgo.
bsplosion
54

Recuerde esto y no necesita confundirse en la conversión de fecha y hora nuevamente.

Cadena al objeto datetime = strptime

objeto de fecha y hora a otros formatos = strftime

Jun 1 2005 1:33PM

es igual a

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

% b Mes como nombre abreviado del entorno local (junio)

% d Día del mes como un número decimal rellenado con ceros (1)

% Y Año con siglo como número decimal (2015)

% I Hour (reloj de 12 horas) como un número decimal con relleno de cero (01)

% M Minuto como un número decimal rellenado con ceros (33)

% p Equivalente de la configuración regional de AM o PM (PM)

entonces necesita strptime, es decir, convertir stringa

>>> 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
... 

Salida

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

¿Qué sucede si tiene un formato diferente de fechas? Puede usar panda o 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]

Salida

[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)]
Rizwan Mumtaz
fuente
% S para segundos como decimal
optimista el
1
¿No se %bromperá si analiza una fecha en inglés en una máquina que no tiene una configuración regional en inglés?
bfontaine
48

En Python> = 3.7.0,

para convertir la cadena AAAA-MM-DD en objeto de fecha y hora , datetime.fromisoformatpodría usarse.

>>> from datetime import datetime

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

Muchas marcas de tiempo tienen una zona horaria implícita. Para asegurarse de que su código funcionará en cada zona horaria, debe usar UTC internamente y adjuntar una zona horaria cada vez que un objeto extraño ingrese al sistema.

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)))
Janus Troelsen
fuente
3
¿Por qué mantiene el mktime()primer método feo y, a veces, incorrecto ( durante las transiciones DST) si conoce el segundo método ( datetime.strptime())? Si desea evitar una excepción durante un segundo calendar.timegm(datetime(1970,1,1)+timedelta(seconds=timegm(time.strptime(..)))).replace(tzinfo=timezone(timedelta(-3)))
intercalar
29

Aquí hay dos soluciones que usan Pandas para convertir fechas formateadas como cadenas en objetos 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)]

Tiempos

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

Y aquí está cómo convertir los ejemplos de fecha y hora originales del 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)]

Hay muchas opciones para convertir de cadenas a marcas de tiempo de Pandas usando to_datetime, así que revise los documentos si necesita algo especial.

Del mismo modo, las marcas de tiempo tienen muchas propiedades y métodos a los que se puede acceder además de.date

Alejandro
fuente
26

Personalmente, me gusta la solución que usa el parsermódulo, que es la segunda respuesta a esta pregunta y es hermosa, ya que no tiene que construir ningún literal de cadena para que funcione. PERO , un inconveniente es que es 90% más lento que la respuesta aceptada 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

Mientras no lo hagas un millón de veces una y otra vez, sigo pensando que el parsermétodo es más conveniente y manejará la mayoría de los formatos de tiempo automáticamente.

usuario1767754
fuente
24

Algo que no se menciona aquí y que es útil: agregar un sufijo al día. Desacoplé la lógica del sufijo para que pueda usarla para cualquier número que desee, no solo fechas.

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"))​​​​​​​
Aram Kocharyan
fuente
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
guneysus
fuente
16

Ejemplo de objeto de fecha y hora consciente de la zona horaria de Django.

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)

Esta conversión es muy importante para Django y Python cuando tienes USE_TZ = True:

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

Cree una pequeña función de utilidad como:

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

Esto es lo suficientemente versátil:

  • Si no pasa ningún argumento, devolverá la fecha de hoy.
  • Hay un formato de fecha predeterminado que puede anular.
  • Puede modificarlo fácilmente para devolver una fecha y hora.
Mackraken
fuente
2
formates una palabra reservada en python y no debe usarse como nombre de variable.
Destrucción del
12

Sería útil para convertir cadenas a fecha y hora y también con zona horaria

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)
Kanish Mathew
fuente
9

arrow ofrece muchas funciones útiles para fechas y horas. Este bit de código proporciona una respuesta a la pregunta y muestra que la flecha también es capaz de formatear fechas fácilmente y mostrar información para otras configuraciones regionales.

>>> 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'

Ver http://arrow.readthedocs.io/en/latest/ para más información.

Bill Bell
fuente
6

Puede usar easy_date para facilitarlo:

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

Si solo desea el formato de fecha, puede convertirlo manualmente pasando sus campos individuales como:

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

Puede pasar sus valores de cadena dividida para convertirlos en tipo de fecha como:

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]))

Obtendrá el valor resultante en formato de fecha.

Javed
fuente
2

También puedes consultar dateparser

dateparser proporciona módulos para analizar fácilmente fechas localizadas en casi cualquier formato de cadena que se encuentre comúnmente en las páginas web.

Instalar en pc:

$ pip install dateparser

Esta es, creo, la forma más fácil de analizar las fechas.

La forma más directa es utilizar la dateparser.parsefunción, que envuelve la mayor parte de la funcionalidad del módulo.

Código de muestra:

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)

Salida:

2005-06-01 13:33:00
1999-08-28 00:00:00
Bilesh Ganguly
fuente
1

Mira mi respuesta .

En los datos del mundo real, este es un problema real: formatos de fecha múltiples, incompatibles, incompletos, inconsistentes y multilingües / región, a menudo mezclados libremente en un conjunto de datos. No está bien que el código de producción falle, y mucho menos ir feliz como un zorro.

Tenemos que intentar ... capturar múltiples formatos de fecha y hora fmt1, fmt2, ..., fmtn y suprimir / manejar las excepciones (de strptime()) para todos aquellos que no coinciden (y en particular, evitar la necesidad de una escalera de prueba con sangría n-deep yukky) .. cláusulas de captura). De mi solución

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
smci
fuente
La pregunta no decía nada sobre "formatos de fecha múltiples, incompatibles, incompletos, inconsistentes y en varios idiomas / regiones", etc. Esto puede ser un problema real, pero aquí no es relevante.
RoG
1
@RoG: Nunca dijo que no lo estaban, e implicaba que eran: "gran lista ... base de datos" . En la mayoría de todas las bases de datos / archivos de registro en los que he trabajado (incluso de tamaño pequeño), había múltiples formatos de fecha, identificadores de zona horaria, MM-DD, etc. En la producción, es inaceptable escribir código frágil que codifique en formatos y bloqueos con excepción cuando no obtiene el formato que esperaba (incluso si devuelve None o '' es más aceptable). De ahí la necesidad de múltiples formatos. Por lo tanto, esto responde a la pregunta formulada, y pasé un poco de tiempo descubriendo la forma más pitónica de manejar los errores de múltiples formatos.
smci
"gran lista ... base de datos" simplemente implica que hay muchos de ellos, no que todos tengan formatos diferentes. Es totalmente aceptable escribir código que lea un único formato, si sabe que hay un único formato en la entrada. En este caso, debería bloquearse si se pasa algo que no está en el formato correcto.
RoG
@RoG: es inaceptable escribir código de producción que se bloquea en un formato incorrecto / Unicode maltratado / truncado / faltante / datos, NaNs, M / D / Y vs D / M / Y formato, YY vs YYYY, etc. Especialmente si esos Las excepciones se pueden evitar con una solución de siete líneas como lo mostré. La mayoría de las "grandes bases de datos" del mundo real son así. Solo porque el OP no dijo explícitamente eso no significa que no sea el contexto típico. No voy a discutir contigo. ¿En qué tipo de conjuntos de datos trabaja y por qué cree que esas suposiciones son razonables? A menos que solo estemos hablando del código de juguete que requiere una intervención constante.
smci
1
Parece un poco tonto suponer con total certeza que el OP debe tener datos que nunca tengan inconsistencias. Sí, es posible tener datos como ese, pero no, no podemos asumir que ese es el caso aquí. Pensé que esta respuesta era útil, ciertamente para mí, cuya búsqueda de respuestas similares a una pregunta muy similar, donde las inconsistencias son definitivamente un problema.
Paul Miller
1
emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")
emp.info()

muestra la columna "Hora de fecha de inicio" y "Última hora de inicio de sesión", ambas son "objeto = cadenas" en el marco de datos

<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

Al usar la parse_datesopción read_csvmencionada, puede convertir su cadena de fecha y hora al formato de fecha y hora de pandas.

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
Riz.Khan
fuente