Перетворення рядка в дату


2179

У мене величезний список дат, таких як рядки:

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

Я збираюсь засунути ці дані назад у відповідні поля дат у базі даних, тому мені потрібно вписати їх у реальні об'єкти дати.

Це відбувається через ORM Django, тому я не можу використовувати SQL для перетворення на вставку.


6
Якщо ви впевнені, що один формат обробляє кожну дату (ні '', ні NaN, не завершені, не збігаються у форматі, немає символів, часових поясів, мікроксекундних міток чи іншого тексту ...), виняток - щастя strptime()буде ганяти вас горіхами, якщо ви не загорнете його. Дивіться мою відповідь, що грунтується на відповіді або Weis на це
smci

Найдавніший, найбільш відомий мені підхід - це датування даних (перевірити blog.scrapinghub.com/2015/11/09/… ). Це працює навіть із природними мовними часовими виразами на декількох мовах, що не входять у поле. Я думаю, це може бути повільним, хоча.
Армандо

Тут є корисне посилання: stackabuse.com/converting-strings-to-datetime-in-python
GoingMyWay

Відповіді:


3456

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/ strftimeформатних рядків: Python 2 , Python 3

  • strvince.org - це також дуже приємна довідка для strearch

Примітки:

  • strptime = "час розбору рядків"
  • strftime = "час форматування рядка"
  • Вимовте це вголос сьогодні, і вам не доведеться шукати його знову через 6 місяців.

7
'% b', '% p' ​​може вийти з ладу в не англійській мові.
jfs

15
@User Ви повинні будете знати заздалегідь, щоб виключити цю частину формату рядка, але якщо ви хочете dateзамість 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

Використовуйте сторонню бібліотеку датаутилів :

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

Він може обробляти більшість форматів дати, включаючи той, який вам потрібно розібрати. Це зручніше, ніж strptimeвін може здогадатися про правильний формат більшу частину часу.

Це дуже корисно для написання тестів, де читабельність важливіша за ефективність.

Ви можете встановити його за допомогою:

pip install python-dateutil

86
Майте на увазі, що для великих обсягів даних це може бути не найбільш оптимальним способом наближення до проблеми. Відгадування формату кожен раз може бути жахливо повільним.
Paweł Polewicz

14
Це добре, але було б непогано мати вбудоване рішення, а не звертатися до третьої сторони.
Брайан Бак

1
Коли я намагаюся розібрати "32-й січень", це поверне мені "2032-01-06" .. що неправильно. чи є спосіб перевірити, чи є рядок дійсною датою чи ні
Картік Домадія

6
@Reef: у 5 разів повільніше відповідно до мого швидкого та брудного орієнтиру. Не так жахливо повільно, як я б очікував.
Антоні Хеткінс

2
Має свої проблеми - наприклад, мовчки скидаючи інформацію часового поясу з разів: спробуйте parser.parse ('15: 55EST ') і порівняйте з parser.parse ('15 .55CST') як приклад
F1Rumors

490

Ознайомтеся зі строптаймом у часовому модулі. Це зворотна стріллінг .

$ 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. Дякую!
Стів Пік

Привіт Стів, модуль чудовий. Було б добре мати атрибут string string також. Інакше не впевнений, почнете ви з понеділка чи неділі
Anake

1
Він не конвертує належним чином, наприклад, "5 лютого 2017 року" та "5 лютого 2017 року" (які формати популярні в деяких колах, а ІМО - деякі найкращі формати дати для наочності та читабельності). Він зберігає їх як 2017-02-01. Те саме для 5/2017/2017 (все-таки це зроблено в лютому / 5/2017 правильно); жоден із цих останніх двох форматів, які я коли-небудь бачив, звик до своїх знань, але я подумав, що все одно це зазначу.
Brōtsyorfuzthrāx

2
ПОПЕРЕДЖЕННЯ: Схоже, цей пакет не підтримувався і не вдосконалювався в будь-який момент протягом останніх 5 років і регулярно аналізує, очевидно, неправильні дати. Наприклад, інстанція Date("20180912")якось аналізує значення 2018-11-21. Використовуйте на свій страх і ризик.
bsplosion

54

Пам'ятайте про це, і вам не потрібно було знову плутатись у перетворенні дат.

Рядок до об'єкта datetime = strptime

об'єкт datetime в інших форматах = strftime

Jun 1 2005 1:33PM

дорівнює

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

% b Місяць як скорочена назва локалу (червень)

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

% Y Рік із століттям у вигляді десяткового числа (2015)

% I Hour (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]

OutPut

[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

48

У 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()під час переходу на 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 Timestamps за допомогою to_datetime, тому перевірте документи якщо вам потрібно щось особливе.

Так само, у Timestamps є багато властивостей та методів, до яких можна отримати доступ.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)

Ця конверсія дуже важлива для 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.

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, і його не слід використовувати як ім'я змінної.
подрібнення

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/ для отримання додаткової інформації.



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()) для всіх тих, хто не відповідає (і, зокрема, уникати необхідності юккі n-глибоко відступів сходів спробувати ..загальні пропозиції). З мого рішення

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

1
@RoG: Ніколи не говорили, що їх не було, і це означало, що це: "величезний список ... база даних" . У більшості кожної бази даних / лог-файлів, над якими я працював (навіть невеликого розміру), було кілька форматів дат, ідентифікатори часових поясів, MM-DD і т.д. він не отримує очікуваного формату (навіть повернення None або "" є більш прийнятним). Звідси потреба у кількох форматах. Отже, це стосується поставленого запитання, і я витратив трохи часу, з'ясовуючи найбільш пітонічний спосіб обробляти помилки з різних форматів.
smci

"величезний список ... база даних" просто означає, що їх дуже багато, а не те, що вони різного формату. Цілком прийнятно писати код, який читає єдиний формат, якщо ви знаєте, що на вході є один формат. У цьому випадку він повинен вийти з ладу, якщо буде передано щось, що не в потрібному форматі.
RoG

@RoG: неприпустимо писати виробничий код, який виходить з ладу у неправильному форматі / змішаному Unicode / усіченому / відсутньому / даних, NaNs, M / D / Y проти D / M / Y форматі, YY проти YYYY тощо. Особливо якщо це винятків можна уникнути, якщо я показав, з семилінійним рішенням. Більшість "величезних баз даних" у реальному світі подібні. Просто тому, що ОП прямо не сказала, це не означає, що це не типовий контекст. Я з вами не збираюся битися. Над якими наборами даних ви працюєте і чому ви вважаєте, що ці припущення є розумними? Якщо тільки ми не говоримо лише про код іграшки, який вимагає постійного втручання.
smci

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ви можете перетворити строковий дату в формат дати панди.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.