将字符串“Jun 1 2005 1:33PM”转换为日期时间。

2944

我有一个包含大量日期时间字符串的列表,如下所示

["Jun 1 2005 1:33PM", "Aug 28 1999 12:00AM"]

我该如何将它们转换为 datetime 对象?


10
除非您确定一种格式适用于每个日期时间(无空值、无NaN、无不完整、无格式不匹配、无尾随字符、无时区、微秒时间戳或其他文本...)否则,strptime() 的异常处理将让您抓狂,除非您将其包装起来。请参考我的回答,基于Or Weis 对此的回答 - smci
我知道的最懒、最常用的方法是使用dateparser(请查看https://blog.scrapinghub.com/2015/11/09/parse-natural-language-dates-with-dateparser)。它甚至可以直接处理多种语言中的自然语言时间表达式。不过,我猜它可能会比较慢。 - Way Too Simple
这里有一个有用的链接:https://stackabuse.com/converting-strings-to-datetime-in-python/ - GoingMyWay
1
正如其他人所提到的那样,datetime.strptime是一个很有用的函数。如果您喜欢视频讲解,可以在这里观看。 - Ben
27个回答

4418

datetime.strptime会将用户指定格式的输入字符串解析为一个无时区信息datetime对象:

>>> from datetime import datetime
>>> datetime.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')
datetime.datetime(2005, 6, 1, 13, 33)

使用现有的datetime对象获取一个date对象,使用.date()进行转换:
>>> datetime.strptime('Jun 1 2005', '%b %d %Y').date()
date(2005, 6, 1)

链接:

注释:

  • strptime = "解析字符串时间"
  • strftime = "格式化字符串时间"

11
在非英语环境下,'%b'和'%p'可能会出现错误。 - jfs
1
如果字符串中没有时间,只有“2014年4月25日”,该怎么办? - User
33
如果你想要一个 date 而不是一个 datetime,可以通过使用 datetime 很好地处理它:datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)。你需要提前知道排除格式字符串的那一部分。 - Izkata
23
如果您知道该字符串表示的是UTC时间,您可以在Python 3中添加以下这行代码来获取一个带有时区信息的datetime对象:from datetime import timezone; datetime_object = datetime_object.replace(tzinfo=timezone.utc) - Flimm
1
在我的情况下,“https://dev59.com/p3RB5IYBdhLWcg3w7reV#54830426” 的答案更匹配,我希望我们能将其作为补充内容包含在这个解决方案中。 - hm6

1068

使用第三方dateutil库:

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

它可以处理大多数日期格式,比strptime更方便,因为它通常会猜对正确的格式。对于编写测试非常有用,因为可读性比性能更重要。

通过以下方式安装:

pip install python-dateutil

125
请注意,对于大量数据,这可能不是解决问题的最优方式。每次都猜测格式可能会非常缓慢。 - Paweł Polewicz
29
这很好,但最好有一个内置的解决方案,而不是必须去第三方。 - brian buck
2
这非常适用于无法保证日期格式的情况。 - wisbucky
如果您有不确定的格式,并且其中一些是不完整的,例如2009年6月而不是2009年6月12日,它将假定任意日期。对于没有月份的日期也是如此。 - theProcrastinator
1
哦,别闹了。Python总是吹嘘自己强大的标准库。它难道还不能做到这一点吗? - java-addict301

515

请查看time模块中的strptime函数。它是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)

18
据我理解,这个答案只会输出时间对象而不是日期时间对象--这就是为什么相比于Patrick的答案这个答案会被忽略的原因。 - Alexander Bird

274

Python >= 3.7

要将YYYY-MM-DD字符串转换为日期时间对象,可以使用datetime.fromisoformat

from datetime import datetime

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

注意文档中的警告:

这个函数不支持解析任意的 ISO 8601 时间字符串 - 它仅仅是 datetime.isoformat() 的逆操作。如果需要一个更加完整的 ISO 8601 解析器,可以使用第三方库 dateutil 中的 dateutil.parser.isoparse


有没有办法将这个回答包含在顶部答案中? - hm6
1
这个 fromisoformat() 在我的情况下似乎返回了一个没有时区信息的日期时间格式。因此,从 datetime.datetime.now() 中减去它会导致错误。请参考这个链接:https://dev59.com/fm855IYBdhLWcg3wHwaH#4530166 - Zincfan
虽然这个答案与原问题无关,但对于大多数来此的人而言,它非常有用。 - Walter Tross
@Zincfan fromisoformat在正确的情况下确实返回带有时区偏移量的datetime对象。请参阅Python文档:https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat - Flimm
很难相信他们会添加一个如此误导的方法。如果无法解析ISO格式,为什么要命名为ISO? - java-addict301

133

我已经完成了一个项目,可以将一些非常棒的表达式转换成时间。请查看timestring

下面是一些示例:

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

70

记住这个,你就不会再在日期时间转换中感到困惑了。

字符串转日期时间对象 = strptime

日期时间对象转其他格式 = strftime

Jun 1 2005 1:33PM

等同于

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

%b 本地缩写月份名称(Jun)

%d 月份中的第几天,以零填充的十进制数(1)

%Y 带有世纪的年份,以十进制数表示(2015)

%I 小时(12小时制)以零填充的十进制数(01)

%M 分钟,以零填充的十进制数(33)

%p 上午或下午的本地化等效项(PM)

所以你需要使用strptime函数,即将字符串转换为

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

输出

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

如果你有不同格式的日期,你可以使用Pandas或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]

输出

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

51
许多时间戳都有隐含的时区。为了确保您的代码在任何时区下都能正常工作,应在内部使用UTC,并在每次外部对象进入系统时附加时区。
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)))

这里假设您已经知道偏移量。如果您不知道,但是您知道例如位置,您可以使用pytz包查询IANA时区数据库的偏移量。我将在此处以德黑兰为例,因为它有半小时的偏移量:
>>> tehran = pytz.timezone("Asia/Tehran")
>>> local_time = tehran.localize(
...   datetime.datetime.strptime("March 5, 2014, 20:13:50",
...                              "%B %d, %Y, %H:%M:%S")
... )
>>> local_time
datetime.datetime(2014, 3, 5, 20, 13, 50, tzinfo=<DstTzInfo 'Asia/Tehran' +0330+3:30:00 STD>)

正如您所看到的,pytz 确定在那个特定日期的偏移量为+3:30。现在您可以将其转换为UTC时间,并应用该偏移量:

>>> utc_time = local_time.astimezone(pytz.utc)
>>> utc_time
datetime.datetime(2014, 3, 5, 16, 43, 50, tzinfo=<UTC>)

请注意,采用时区之前的日期将给您带来奇怪的偏移量。这是因为IANA决定使用本地平均时间
>>> chicago = pytz.timezone("America/Chicago")
>>> weird_time = chicago.localize(
...   datetime.datetime.strptime("November 18, 1883, 11:00:00",
...                              "%B %d, %Y, %H:%M:%S")
... )
>>> weird_time.astimezone(pytz.utc)
datetime.datetime(1883, 11, 18, 7, 34, tzinfo=<UTC>)

“7小时34分钟”的奇怪时间是由芝加哥的经度推导出来的。我使用这个时间戳,因为它是在芝加哥采用标准时间之前。

39

如果你的字符串采用ISO 8601格式,并且你使用Python 3.7或更高版本,则可以使用以下简单的代码:

import datetime

aDate = datetime.date.fromisoformat('2020-10-04')

用于日期和

import datetime

aDateTime = datetime.datetime.fromisoformat('2020-10-04 22:47:00')

用于包含日期和时间的字符串。如果包含时间戳,函数datetime.datetime.isoformat()支持以下格式:

YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]]

*表示匹配任意单个字符。详见此处此处


34

以下是使用Pandas将格式为字符串的日期转换为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)]

时间

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

以下是如何将原始日期时间示例转换为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)]

使用to_datetime可以有多种将字符串转换为Pandas时间戳的选项,如果您需要任何特殊要求,请查看文档

同样,除了.date之外,时间戳还有许多属性和方法可供访问。


我认为现在的时间已经改变了(Python 3.9,pandas 1.3.3); 在我的机器上,pd.DatetimeIndex(dates).date.tolist() 的运行速度大约比 [d.date() for d in pd.to_datetime(dates)] 快3倍。 - FObersteiner

33

我个人喜欢使用parser模块的解决方案,它是这个问题的第二个答案,而且非常精美,因为你不必构造任何字符串字面量来使其工作。 但是,缺点是它比使用strptime的被接受的答案慢90%

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

只要你不是一遍又一遍地执行这个操作 一百万 次,我仍然认为使用 parser 方法更加方便,并且会自动处理大多数时间格式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接