Как я могу разобрать несколько (неизвестных) форматов даты в Python? - PullRequest
12 голосов
/ 13 августа 2011

У меня есть куча документов Excel, из которых я извлекаю даты.Я пытаюсь преобразовать их в стандартный формат, чтобы я мог поместить их в базу данных.Есть ли функция, в которую я могу добавить эти строки и вернуть стандартный формат?Вот небольшая выборка моих данных:

Хорошо, что я знаю, что это всегда месяц / день

10/02/09
07/22/09
09-08-2008
9/9/2008
11/4/2010
 03-07-2009
09/01/2010

Я бы хотел перенести их все в MM / DD / YYYYформат.Есть ли способ, которым я могу сделать это, не пытаясь каждый шаблон против строки?

Ответы [ 4 ]

16 голосов
/ 13 августа 2011
import re

ss = '''10/02/09
07/22/09
09-08-2008
9/9/2008
11/4/2010
03-07-2009
09/01/2010'''


regx = re.compile('[-/]')
for xd in ss.splitlines():
    m,d,y = regx.split(xd)
    print xd,'   ','/'.join((m.zfill(2),d.zfill(2),'20'+y.zfill(2) if len(y)==2 else y))

результат

10/02/09     10/02/2009
07/22/09     07/22/2009
09-08-2008     09/08/2008
9/9/2008     09/09/2008
11/4/2010     11/04/2010
03-07-2009     03/07/2009
09/01/2010     09/01/2010

Редактировать 1

И Редактировать 2 : с учетом информации о '{0:0>2}'.format(day) от JBernardo, я добавил 4-йрешение, которое представляется самым быстрым

import re
from time import clock
iterat = 100

from datetime import datetime
dates = ['10/02/09', '07/22/09', '09-08-2008', '9/9/2008', '11/4/2010',
         ' 03-07-2009', '09/01/2010']

reobj = re.compile(
r"""\s*  # optional whitespace
(\d+)    # Month
[-/]     # separator
(\d+)    # Day
[-/]     # separator
(?:20)?  # century (optional)
(\d+)    # years (YY)
\s*      # optional whitespace""",
re.VERBOSE)

te = clock()
for i in xrange(iterat):
    ndates = (reobj.sub(r"\1/\2/20\3", date) for date in dates)
    fdates1 = [datetime.strftime(datetime.strptime(date,"%m/%d/%Y"), "%m/%d/%Y")
               for date in ndates]
print "Tim's method   ",clock()-te,'seconds'



regx = re.compile('[-/]')


te = clock()
for i in xrange(iterat):
    ndates = (reobj.match(date).groups() for date in dates)
    fdates2 = ['%s/%s/20%s' % tuple(x.zfill(2) for x in tu) for tu in ndates]
print "mixing solution",clock()-te,'seconds'


te = clock()
for i in xrange(iterat):
    ndates = (regx.split(date.strip()) for date in dates)
    fdates3 = ['/'.join((m.zfill(2),d.zfill(2),('20'+y.zfill(2) if len(y)==2 else y)))
              for m,d,y in ndates]
print "eyquem's method",clock()-te,'seconds'



te = clock()
for i in xrange(iterat):
    fdates4 = ['{:0>2}/{:0>2}/20{}'.format(*reobj.match(date).groups()) for date in dates]
print "Tim + format   ",clock()-te,'seconds'


print fdates1==fdates2==fdates3==fdates4

результат

number of iteration's turns : 100
Tim's method    0.295053700959 seconds
mixing solution 0.0459111423379 seconds
eyquem's method 0.0192239516475 seconds
Tim + format    0.0153756971906 seconds 
True

Решение для смешивания интересно, потому что оно сочетает в себе скорость моего решения и способность регулярного выражения Тима Пицкера обнаружение дат в строке.

Это еще более верно для решения, объединяющего единицу Тима и форматирования с {:0>2}.Я не могу объединить {:0>2} с моим, потому что regx.split(date.strip()) производит год с 2 или 4 цифрами

16 голосов
/ 13 августа 2011

Сторонний модуль dateutil имеет функцию parse, которая работает аналогично PHP strtotime: вам не нужно указывать конкретный формат даты, он просто пробует несколько своих собственных.

>>> from dateutil.parser import parse
>>> parse("10/02/09", fuzzy=True)
datetime.datetime(2009, 10, 2, 0, 0)  # default to be in American date format

Он также позволяет указывать различные предположения:

  • dayfirst - интерпретировать ли первое значение в неоднозначной 3-целочисленная дата (например, 01/05/09) как день (True) или месяц (False).Если yearfirst установлен в True, это различает YDM и YMD.Если установлено значение None, это значение извлекается из текущего объекта parserinfo (который по умолчанию имеет значение False).
  • yearfirst - интерпретировать ли первое значение в неоднозначной трехзначной дате (например, 01.05.09) как год.Если True, то первым числом считается год, в противном случае последним числом считается год.Если для этого параметра установлено значение None, значение извлекается из текущего объекта parserinfo (который по умолчанию равен False).
10 голосов
/ 13 августа 2011

Если вы не хотите устанавливать сторонний модуль, например dateutil:

import re
from datetime import datetime
dates = ['10/02/09', '07/22/09', '09-08-2008', '9/9/2008', '11/4/2010', ' 03-07-2009', '09/01/2010']
reobj = re.compile(
    r"""\s*  # optional whitespace
    (\d+)    # Month
    [-/]     # separator
    (\d+)    # Day
    [-/]     # separator
    (?:20)?  # century (optional)
    (\d+)    # years (YY)
    \s*      # optional whitespace""", 
    re.VERBOSE)
ndates = [reobj.sub(r"\1/\2/20\3", date) for date in dates]
fdates = [datetime.strftime(datetime.strptime(date,"%m/%d/%Y"), "%m/%d/%Y")
          for date in ndates]

Результат:

['10/02/2009', '07/22/2009', '09/08/2008', '09/09/2008', '11/04/2010', '03/07/2009', '09/01/2010']
4 голосов
/ 13 августа 2011

Вы можете использовать регулярное выражение, например r'(\d+)\D(\d+)\D(\d+)', чтобы получить месяц, день и год в кортеже с функцией re.findall.

затем просто объедините двухзначные годы с номером 20 или 19 и используйте разделитель, к которому вы хотите присоединиться, затем вернитесь:

'/'.join(the_list)

Как указал Тим:

Чтобы нормализовать дни, просто сделайте '{0:0>2}'.format(day) и то же самое с месяцами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...