Преобразование строки в дату и время

2188

У меня есть огромный список дат, таких как строки:

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

Я собираюсь перенести их обратно в надлежащие поля даты и времени в базе данных, поэтому мне нужно преобразовать их в реальные объекты даты и времени.

Это происходит через ORM Django, поэтому я не могу использовать SQL для преобразования при вставке.

Oli
источник
6
Если вы не уверены, что один формат обрабатывает каждую дату-дату (нет '', нет NaN, нет неполных, нет несовпадений форматов, нет конечных символов, часовых поясов, меток времени в микросекундах или другого текста ...), счастье исключения - strptime()сводит вас с ума, если вы не оберните его. Смотрите мой ответ, основанный на ответе Or Weis на это
smci
Самый ленивый, наиболее широко используемый подход, который я знаю, это dateparser (см. Blog.scrapinghub.com/2015/11/09/… ). Это работает даже с естественными языковыми выражениями времени на нескольких языках из коробки. Я думаю, что это может быть медленно, хотя.
Армандо
Здесь есть полезная ссылка: stackabuse.com/converting-strings-to-datetime-in-python
GoingMyWay

Ответы:

3463

datetime.strptimeявляется основной процедурой для разбора строк в datetime. Он может обрабатывать все виды форматов, причем формат определяется строкой формата, которую вы ему задаете:

from datetime import datetime

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

Полученный datetimeобъект является часовым поясом.

Ссылки:

  • Документация Python для strptime: Python 2 , Python 3

  • Документация Python для строк strptime/ strftimeformat: Python 2 , Python 3

  • strftime.org также очень хороший справочник для strftime

Ноты:

  • strptime = "время разбора строки"
  • strftime = "время форматирования строки"
  • Произнесите это вслух сегодня, и вам не придется искать его снова через 6 месяцев.
Патрик Харрингтон
источник
7
"% b", "% p" может завершиться с ошибкой в ​​неанглийской локали.
JFS
15
@User Вам нужно знать заранее, чтобы исключить эту часть строки формата, но, если вы хотите dateвместо a datetime, прохождение datetimeобрабатывает это красиво: datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)
Izkata
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"
Мартин Тома
4
@AminahNuraini Я справился с подобной проблемой, занимаясь from datetime import datetimeвместо просто import datetime.
Макс Стратер
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
Помните, что для больших объемов данных это может быть не самым оптимальным способом решения проблемы. Угадывание формата каждый раз может быть ужасно медленным.
Павел Полевич
14
Это хорошо, но было бы неплохо иметь встроенное решение, а не обращаться к третьей стороне.
Брайан Бак
1
Когда я пытаюсь разобрать "32-й Ян", он возвращает мне "2032-01-06" .. что неверно. есть ли способ проверить, является ли строка действительной датой или нет
Kartik Domadiya
6
@Reef: в 5 раз медленнее, в соответствии с моим быстрым и грязным тестом. Не так ужасно медленно, как я ожидал.
Энтони Хэтчкинс
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?
вор в законе
3
Как сказал Александр, это возвращает struct_time, а не datetime. Конечно, вы можете преобразовать его в дату и время, но ответ Патрика будет более прямым, если вам нужен объект даты и времени в конце.
Леандро Алвес
В стандартной библиотеке python нет ничего похожего на strtotime, но у dateutil есть синтаксический анализатор, который распознает множество форматов даты наилучшего уровня.
Джефф Джерриеттс
1
@BenBlank: «% b», «% p» может не работать в неанглийской локали.
Jfs
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эта библиотека сделала это так легко! Спасибо.
brandonjp
Добро пожаловать. Буду рад вашим комментариям и идеям по улучшению этого пакета. Дайте мне знать, используйте вопросы GitHub. Спасибо!
Стив Пик
Привет Стив, модуль отличный. Было бы неплохо иметь атрибут строки дня недели. В противном случае не уверен, начнете ли вы с понедельника или воскресенья
Anake
1
Он не конвертирует, например, «5 февраля 2017» и «5 февраля 2017» (форматы, популярные в некоторых кругах, и IMO - одни из лучших форматов дат для ясности и читабельности). Он хранит их как 2017-02-01. То же самое для 5 февраля 2017 года (однако, 5 февраля 2017 года это правильно); ни один из тех последних двух форматов, которые я когда-либо видел, не использовался для моих знаний, но я все равно решил указать на это.
Brōtsyorfuzthrāx
2
ПРЕДУПРЕЖДЕНИЕ. Похоже, что этот пакет не обновлялся и не улучшался ни в какой момент за последние 5 лет и регулярно анализирует явно неправильные даты. Например, создание экземпляра Date("20180912")каким-то образом анализирует значение 2018-11-21. Используйте на свой риск.
Bsplosion
54

Помните об этом, и вам не нужно было снова путаться в преобразовании даты и времени.

String to datetime object = strptime

объект datetime в другие форматы = strftime

Jun 1 2005 1:33PM

равно

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

% b Месяц как сокращенное название локали (июнь)

% d День месяца в виде десятичного числа с нулем (1)

% Y Год с веком в виде десятичного числа (2015)

% I час (12-часовые часы) как десятичное число с нулем (01)

% M Минута как десятичное число с добавлением нуля (33)

% p Локальный эквивалент AM или PM (PM)

так что вам нужно 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сломается, если вы проанализируете английское свидание на машине, у которой нет английского языка?
Бфонтене
48

В Python> = 3.7.0,

для преобразования 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
SuperNova
источник
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()при переходах DST) 1-го метода, если знаете 2-й метод ( datetime.strptime())? Если вы хотите избежать исключения в течение високосной секунды (2-й метод не работает), вы можете использовать calendar.timegmвместо этого:(datetime(1970,1,1)+timedelta(seconds=timegm(time.strptime(..)))).replace(tzinfo=timezone(timedelta(-3)))
jfs
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

А вот как конвертировать оригинальные примеры даты и времени ОП:

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 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метод более удобен и будет обрабатывать большинство форматов времени автоматически.

user1767754
источник
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
guneysus
источник
16

Пример объекта даты / времени с поддержкой Django Timezone.

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)

Это преобразование очень важно для Django и Python, когда у вас есть 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
источник
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()

Это достаточно универсально:

  • Если вы не передадите никаких аргументов, он вернет сегодняшнюю дату.
  • По умолчанию есть формат даты, который вы можете переопределить.
  • Вы можете легко изменить его, чтобы вернуть дату и время.
Mackraken
источник
2
formatявляется зарезервированным словом в python и не должен использоваться в качестве имени переменной.
измельчение
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

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

>>> 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/ для получения дополнительной информации.

Билл Белл
источник
6

Вы можете использовать easy_date, чтобы сделать это легко:

import date_converter
converted_date = date_converter.string_to_datetime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')
Рафаэль Амоедо
источник
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()) для всех тех, которые не соответствуют друг другу (и, в частности, избегать необходимости в юкки-н-глубокой лестнице попытки с отступом) ... поймать пункты). Из моего решения

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
источник
В вопросе ничего не сказано о «множественных, несовпадающих, неполных, несовместимых и многоязычных / региональных форматах даты» и т. Д. Это может быть реальной проблемой, но не относящейся к делу.
RoG
1
@RoG: никогда не говорилось, что они не были, и это подразумевало, что они были: «огромный список ... база данных» . В большинстве каждой базы данных / файла журнала, над которым я работал (даже небольшого размера), было несколько форматов даты, идентификаторов часовых поясов, MM-DD и т. Д. В производстве недопустимо писать хрупкий код, который жестко кодирует в форматах и ​​вылетает, за исключением случаев, когда он не получает ожидаемый формат (даже возвращает None или '' более приемлемо). Отсюда необходимость в нескольких форматах. Следовательно, это отвечает на поставленный вопрос, и я потратил немного времени на то, чтобы выяснить наиболее питонский способ обработки ошибок из разных форматов.
августа
«огромный список ... база данных» просто подразумевает, что их много, а не то, что все они имеют разные форматы. Вполне допустимо писать код, который читает один формат, если вы знаете, что во входе есть один формат. В этом случае должно произойти сбой, если передается что-то, что не в правильном формате.
RoG
@RoG: недопустимо писать производственный код, который дает сбой в неправильном формате / искаженном Unicode / усеченном / отсутствующем / data, NaNs, M / D / Y против D / M / Y, YY против YYYY и т. Д. Особенно, если эти исключений можно избежать с помощью решения с семью линиями, как я показал. Большинство реальных "огромных баз данных" такие. То, что ОП явно не говорит, что это не означает, что это не типичный контекст. Я не собираюсь спорить с вами. С какими наборами данных вы работаете и почему вы считаете эти предположения разумными? Если только мы не говорим о игрушечном коде, который требует постоянного вмешательства.
19
1
Кажется немного глупым предполагать с полной уверенностью, что у ОП должны быть данные, которые никогда не имели несоответствий. Да, возможно иметь такие данные, но мы не можем предполагать, что это так. Я думал, что этот ответ был полезен, конечно, для меня, чей поиск похожих ответов на очень похожий вопрос, где несоответствия, безусловно, является проблемой.
Пол Миллер
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
Riz.Khan
источник