解析包含不同长度的时间戳 Python

Parse timestamps containing different lengths Python

我有 180,000 行时间戳,我想将其解析为日期时间格式,例如:

YYYY-MM-DD HH:MM:SS

以下是时间戳(注意前 9 小时内没有前导零):

19-May-14 3:36:00 PM PDT
19-May-14 10:37:00 PM PDT 

我一直在使用 parse_dates 作为 pandas.read 的一部分来解析这些日期,但我发现这种方法很慢(通常约为 80 秒)。我也尝试过 dateutil 解析器,结果相似。

我想更快地解析时间戳,但我在处理时间戳的不同宽度时遇到了问题。我发现 this SO solution 这似乎与我的问题非常相似,但未能使该方法适应不同长度的时间戳。

有人可以推荐一个可行的适应链接的解决方案,或者其他更好的方法吗?

谢谢

此解决方案基于附件 link 中提供的 accepted answer 并假定时区正好由 3 个字符组成(并忽略其特定值)。


可以根据年月日相对于字符串开头的位置提取年月日,如下:

month_abbreviations = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4,
                       'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8,
                       'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
day = int(line[0:2])
month = month_abbreviations[line[3:6]]
year = 2000 + int(line[7:9]) # this should be adapted to your specific use-case

可以提取分秒和AM/PM根据它们相对于字符串末尾的位置,如下:

AM_PM = line[-6:-4]
second = int(line[-9:-7])
minute = int(line[-12:-10])

您可以根据它与字符串开头和结尾的相对位置来提取小时:

hour = int(line[10:-13])

那么就可以直接根据AM_PM值计算出准确的小时数,如下:

hour = hour if AM_PM == 'AM' else hour + 12

根据我的计算,这比使用 dict 稍微快一些,但也快不了多少:

hour_shifter = {(0, 'AM'): 0, (0, 'PM'): 12,
                (1, 'AM'): 1, (1, 'PM'): 13,
                ...
                (11, 'AM'): 11, (11, 'PM'): 23,
                (12, 'AM'): 12}
hour = hour_shifter[(hour, AM_PM)]

现在您可以实例化 datetime 对象:

datetime.datetime(year, month, day, hour, minute, second)

使用正则表达式如何?你能提供你的数据文件来测试吗?

patt = re.compile(r'(?P<day>\d\d)-(?P<month>\w+)-(?P<year>\d\d)'
                  r' (?P<hour>\d{1,2}):(?P<minute>\d\d):(?P<second>\d\d)'
                  r' (?P<noon>\w\w) (?P<tz>\w+)')

for date in dates:
    res = patt.match(date)
    print(res.groupdict())

然后将日、月、年等转换为整数,创建时区对象:

from pytz import timezone
tz = timezone(res.groupdict()['tz'])

首先,一些问题。

  1. 您显示该小时有 1 个或 2 个字符。日子也不同吗?或者它总是 2 个字符?
  2. 你用时区做什么?扔了吗?
  3. 您如何处理看起来像是 1900 年代的年份?您是否必须处理未来的日期?您确定 48 年是 1948 年而不是 2048 年吗?

以下是我会尝试的方法。首先建立一些年和月的查找字典。

months = {'Jan': '01', 'Feb': '02', ... 'Dec': '12'} 
years = {}
for i in range(50, 100):
    years[str(i)] = '19' + str(i)
for i in range(0, 50):
    years[str(i)] = '20' + str(i)

遍历每条记录并

  1. 在空格处拆分每个字符串
  2. 从日期字符串中提取日、月和年子字符串。从字典中查找年份和月份。按原样使用天。
  3. 从时间的小时部分拆分分钟和秒。分钟和秒以文本形式显示是很好的。
  4. 提取小时的整数值。如果拆分操作的第 3 个字段是 'PM',则添加 12,必要时考虑大小写。
  5. 重新assemble 将所有内容转换为您的目标格式。如果只有一个字符,请用零填充小时。

测试年份字典是否优于将两位数年份转换为整数、检查值并根据您选择的截止值添加 1900 或 2000 可能是明智的。我希望字典能赢,但很难说。

假设您的日期字符串中的“14”对应于 2014 年:

import datetime

month_abbr = {'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5,'Jun':6, 
              'Jul':7, 'Aug':8, 'Sep':9, 'Oct':10, 'Nov':11, 'Dec':12
              }   

def format_date(date_str):
    day, month, year = (date_str.split(' ')[0]).split('-')
    hour, minute, sec = (date_str.split(' ')[1]).split(':')
    return datetime.datetime(int(year)+2000, month_abbr[month], 
           int(day), int(hour), int(minute), int(sec))


date_str = '19-May-14 3:36:00 PM PDT'
#date_str = '19-May-14 10:37:00 PM PDT'
formatted_date = format_date(date_str)
print(formatted_date)
2014-05-19 03:36:00

datetime 对象的默认格式是 YYYY-MM-DD HH:MM:SS,因此在这种情况下您无需指定唯一格式。如果您以后这样做,请查看 datetime 中的 strftime 函数。

如果“14”可以在 1900 年代和 2000 年代之间切换,那么您需要 (1) 在获取日期字符串之前了解该信息,以及 (2) 调整上述代码以将 1900 或 2000 添加到年。