将字符串转换为日期时间


2177

我有大量的日期时间列表,例如字符串:

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

我将把它们推回到数据库中正确的日期时间字段中,因此我需要将它们魔术化为实际的日期时间对象。

这是通过Django的ORM进行的,因此我无法使用SQL进行插入时的转换。


6
除非您确定一种格式可以处理每个单个日期时间(没有”,没有NaN,没有不完整,没有格式不匹配,没有尾随字符,时区,微秒时间戳或其他文本...),否则异常strptime()除非你把它包起来,否则会让你发疯。请参阅我的答案,基于Or Weis的答案
smci

我知道,最懒惰,最广泛使用的方法是dateparser(请参阅blog.scrapinghub.com/2015/11/09/…)。它甚至可以与多种语言的自然语言时间表达式一起使用。我想这可能会很慢。
阿曼多

Answers:


3456

datetime.strptime是将字符串解析为日期时间的主要例程。它可以处理各种格式,格式由您为其指定的格式字符串确定:

from datetime import datetime

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

生成的datetime对象是时区未使用的。

链接:

笔记:

  • strptime =“字符串解析时间”
  • strftime =“字符串格式时间”
  • 今天大声发音,您将在6个月内无需再次搜索。

7
'%b'和'%p'在非英语语言环境中可能会失败。
jfs 2014年

15
@User您必须提前知道要排除格式字符串的那部分,但是如果您想要a date而不是a datetimedatetime则可以很好地处理它: datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)
Izkata 2014年

14
如果您知道该字符串表示UTC中的日期时间,则可以datetime通过在Python 3中添加此行来获取时区感知对象:from datetime import timezone; datetime_object = datetime_object.replace(tzinfo=timezone.utc)
Flimm

111
我在寻找"%Y-%m-%d %H:%M:%S"
Martin Thoma

4
@AminahNuraini我通过做from datetime import datetime而不是仅仅解决了类似的问题import datetime
Max Strater

831

使用第三方dateutil库:

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

它可以处理大多数日期格式,包括您需要解析的格式。它比strptime大多数时候都可以猜测正确的格式要方便得多。

这对于编写测试非常有用,在测试中,可读性比性能更重要。

您可以使用以下方法安装它:

pip install python-dateutil

86
请注意,对于大量数据,这可能不是解决问题的最佳方法。每次都猜测格式可能会非常慢。
帕维尔Polewicz

14
很好,但是拥有内置的解决方案而不必去第三方就好了。
brian buck

1
当我尝试解析“ 32nd jan”时,它返回我“ 2032-01-06” ..这是不正确的。有什么方法可以检查字符串是否是有效日期
Kartik Domadiya 2013年

6
@Reef:根据我快速而肮脏的基准,速度慢了5倍。没有我预期的那么慢。
Antony Hatchkins

2
有其自身的问题-例如,从时间中静默删除时区信息:尝试parser.parse('15:55EST')并与parser.parse('15 .55CST')比较作为示例
F1Rumors

490

时间模块中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)

16
据我了解,这个答案只输出时间对象,而不是日期时间对象,这就是为什么答案要比帕特里克的答案更深的原因。
亚历山大·伯德

有没有办法设置DateTimeField的默认datetime格式?
主销

3
正如亚历山大所说,这将返回struct_time而不是datetime。当然,您可以将其转换为日期时间,但是如果您想要最后一个日期时间对象,Patrick的答案会更直接。
Leandro Alves

在标准的python库中,没有什么比strtotime更好的了,但是dateutil的解析器可以识别很多尽力而为的日期格式。
Geoff Gerrietts

1
@BenBlank:'%b','%p'在非英语语言环境中可能会失败。
jfs 2014年

113

我整理了一个可以转换一些真正简洁的表达式的项目。查看时间字符串

以下是一些示例:

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
哇。哇。哇。哇。这很容易。我有一个日期时间字符串,我只想退出年份。就像这样简单: import timestring timestring.Date('27 Mar 2014 12:32:29 GMT').year这个lib非常容易!谢谢。
brandonjp 2014年

你太客气了。我希望您对改进此程序包的意见和想法。让我知道,使用github问题。谢谢!
Steve Peak

嗨,史蒂夫,这个模块很棒。最好还有一个工作日的字符串属性。否则,不确定是从星期一还是从星期日开始
Anake 2014年

1
它无法正确转换,例如“ 2017年2月5日”和“ 2017年2月5日”(这是某些圈子中流行的格式,而IMO是一些清晰易读的最佳日期格式)。将它们存储为2017-02-01。2017年2月5日相同(但是正确地是2017年2月5日);后两种格式都不是我所见过的格式,但我想还是应该指出来。
Brōtsyorfuzthrāx

2
警告:此软件包在过去5年中似乎没有得到任何维护或改进,并且通常会分析明显不正确的日期。例如,实例化Date("20180912")以某种方式解析的值2018-11-21。使用风险自负。
bsplosion

54

记住这一点,您无需再次对日期时间转换感到困惑。

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

datetime对象为其他格式= strftime

Jun 1 2005 1:33PM

等于

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

%b月作为语言环境的缩写名称(六月)

%d月中的一天,以零填充的十进制数字(1)

%Y以世纪为十进制数字的年份(2015)

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

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

等同于AM或PM(PM)的%p语言环境

所以你需要strptime即转换string

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

如果日期格式不同,可以使用panda或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)]

%S秒表示为小数
乐观主义者

1
%b如果您在没有英语语言环境的机器上解析英语日期,不会中断?
bfontaine

47

在Python> = 3.7.0中,

转换YYYY-MM-DD字符串DateTime对象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

32

许多时间戳都有一个隐含的时区。为了确保您的代码在每个时区都能工作,您应该在内部使用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)))

3
mktime()如果您知道第二种方法(datetime.strptime()),为什么还要保持丑陋的(有时是错误的)(在DST转换期间)第一种方法?如果要避免在a秒(第二种方法失败)期间发生异常,则可以calendar.timegm改用:(datetime(1970,1,1)+timedelta(seconds=timegm(time.strptime(..)))).replace(tzinfo=timezone(timedelta(-3)))
jfs 2014年

29

这是两个使用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)]

使用可以从字符串转换为Pandas Timestamps有很多选项to_datetime,因此请检查文档如果需要任何特殊。

同样,时间戳除了具有许多可访问的属性和方法外,.date


26

我个人喜欢使用parser模块的解决方案,这是该问题的第二个答案,而且很漂亮,因为您不必构造任何字符串文字即可使其工作。但是,缺点是它比接受的答案慢90%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

只要你是不是做这个一百万一遍又一遍的时间,我还是觉得parser方法是更方便,会自动处理大部分的时间格式。


24

这里没有提到但有用的东西:在一天中添加一个后缀。我解耦了后缀逻辑,以便您可以将其用于任何您喜欢的数字,而不仅仅是日期。

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

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)

具备USE_TZ = True以下条件时,此转换对Django和Python非常重要:

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

12

创建一个小的实用程序函数,例如:

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

这足够通用:

  • 如果您不传递任何参数,它将返回今天的日期。
  • 有一种默认的日期格式可以覆盖。
  • 您可以轻松地对其进行修改以返回日期时间。

2
format是python中的保留字,不应将其用作变量名。
7:30撕碎了

12

它将有助于将字符串转换为日期时间以及时区

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

arrow提供了许多有用的日期和时间功能。这段代码提供了对该问题的答案,并表明箭头还能够轻松格式化日期并显示其他语言环境的信息。

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

有关更多信息,请参见http://arrow.readthedocs.io/en/latest/



4

如果只需要日期格式,则可以通过传递各个字段来手动将其转换,例如:

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

您可以传递拆分的字符串值以将其转换为日期类型,例如:

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

您将获得日期格式的结果值。


2

您也可以退房 dateparser

dateparser 提供的模块可轻松解析几乎任何网页上常见的字符串格式的本地化日期。

安装:

$ pip install dateparser

我认为,这是解析日期的最简单方法。

最直接的方法是使用dateparser.parse功能,该功能包装了模块中的大多数功能。

样例代码:

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)

输出:

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

1

我的回答

在现实世界的数据中,这是一个实际的问题:多种,不匹配,不完整,不一致以及多语言/区域日期格式,通常在一个数据集中自由地混合使用。生产代码失败是不可能的,更不用说像狐狸一样的异常快乐了。

我们需要尝试...捕获多种日期时间格式fmt1,fmt2,...,fmtn,并strptime()为所有不匹配的对象抑制/处理(来自的)异常(尤其是避免使用yukky n缩进的try梯形图) ..catch子句)。从我的解决方案

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

该问题未提及“多种,不匹配,不完整,不一致和多语言/区域日期格式”等问题。这可能是一个实际问题,但在此不相关。
RoG '18 -10-2

1
@RoG:从来没有说过不是,而是暗示他们是:“巨大的清单...数据库”。在我工作过的大多数数据库/日志文件中(甚至是小型文件),都有多种日期格式,时区标识符,MM-DD等。在生产中,编写易碎的代码是不可接受的,这些代码的格式为硬代码,当崩溃时会崩溃它没有获得预期的格式(即使返回None或”也是可以接受的)。因此需要多种格式。因此,这确实解决了所提出的问题,并且我花了一些时间来找出处理多种格式错误的最Python方式。
smci

“巨大列表...数据库”仅表示它们很多,而不是它们都是不同的格式。如果您知道输入中存在单一格式,那么编写读取单一格式的代码是完全可以接受的。在这种情况下,如果传递的格式不正确,则会崩溃。
RoG '18 -10-3

@RoG:写生产代码会失败,因为代码格式错误/ Unicode乱码/截断/丢失/数据,NaN,M / D / Y与D / M / Y格式,YY与YYYY等崩溃,因此尤其如此如我所展示的,可以使用七线解决方案来避免出现异常。大多数现实世界中的“大型数据库”就是这样。仅仅因为OP没有明确地说那并不意味着它不是典型的上下文。我不会和你争吵。您要处理哪种数据集?为什么您认为这些假设是合理的?除非我们只是在谈论玩具代码,这需要不断的干预。
smci

1
完全肯定地假设OP必须具有从未出现过不一致的数据,这似乎有些愚蠢。是的,可能有这样的数据,但是不,我们不能假设情况就是这样。我认为这个答案很有用,对我来说,对于一个非常相似的问题(其中不一致肯定是一个问题)寻找相似答案的我来说,无疑。
Paul Miller

1
emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")
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 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

通过使用parse_dates选项,read_csv您可以将字符串datetime转换为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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.