У меня есть строки, которые показывают дату в следующем формате:
x minutes/hours/days/months/years ago
Мне нужно разобрать это на datetime, используя python.
Кажется, датаутил не может этого сделать.
Есть ли способ сделать это?
У меня есть строки, которые показывают дату в следующем формате:
x minutes/hours/days/months/years ago
Мне нужно разобрать это на datetime, используя python.
Кажется, датаутил не может этого сделать.
Есть ли способ сделать это?
Конечно, вы можете это сделать. Вам просто нужно timedelta.
s = "3 days ago"
parsed_s = [s.split()[:2]]
time_dict = dict((fmt,float(amount)) for amount,fmt in parsed_s)
dt = datetime.timedelta(**time_dict)
past_time = datetime.datetime.now() - dt
Как в стороне, это выглядит как dateutil имеет relativedelta, который действует как timedelta, но конструктор также принимает months и years в аргументах (и, по-видимому, аргументы должны быть целыми).
Это можно легко сделать с помощью timedelta s:
import datetime
def string_to_delta(string_delta):
value, unit, _ = string_delta.split()
return datetime.timedelta(**{unit: float(value)})
Производство:
>>> string_to_delta("20 hours ago")
datetime.timedelta(0, 72000)
Хотя это потребует дополнительной работы для работы с месяцами/годами, поскольку добавление месяца к дате является неоднозначной операцией, но это должно быть простым дополнением, если вы знаете, что вы хотите, чтобы это означало.
Чтобы получить фактическое время, просто отвлеките дельту от datetime.datetime.now().
Так как ваши аргументы - это что-то вроде 2 дней назад, 3 месяца назад, 2 года назад. Функция ниже может помочь в получении точной даты для аргументов. Сначала вам нужно импортировать следующие утилиты date
import datetime
from dateutil.relativedelta import relativedelta
Затем реализуем функцию ниже
def get_past_date(str_days_ago):
TODAY = datetime.date.today()
splitted = str_days_ago.split()
if len(splitted) == 1 and splitted[0].lower() == 'today':
return str(TODAY.isoformat())
elif len(splitted) == 1 and splitted[0].lower() == 'yesterday':
date = TODAY - relativedelta(days=1)
return str(date.isoformat())
elif splitted[1].lower() in ['hour', 'hours', 'hr', 'hrs', 'h']:
date = datetime.datetime.now() - relativedelta(hours=int(splitted[0]))
return str(date.date().isoformat())
elif splitted[1].lower() in ['day', 'days', 'd']:
date = TODAY - relativedelta(days=int(splitted[0]))
return str(date.isoformat())
elif splitted[1].lower() in ['wk', 'wks', 'week', 'weeks', 'w']:
date = TODAY - relativedelta(weeks=int(splitted[0]))
return str(date.isoformat())
elif splitted[1].lower() in ['mon', 'mons', 'month', 'months', 'm']:
date = TODAY - relativedelta(months=int(splitted[0]))
return str(date.isoformat())
elif splitted[1].lower() in ['yrs', 'yr', 'years', 'year', 'y']:
date = TODAY - relativedelta(years=int(splitted[0]))
return str(date.isoformat())
else:
return "Wrong Argument format"
Вы можете вызвать функцию следующим образом:
print get_past_date('5 hours ago')
print get_past_date('yesterday')
print get_past_date('3 days ago')
print get_past_date('4 months ago')
print get_past_date('2 years ago')
print get_past_date('today')
полностью преувеличенное решение, но мне нужно было что-то более гибкое:
def string_to_delta(relative):
#using simplistic year (no leap months are 30 days long.
#WARNING: 12 months != 1 year
unit_mapping = [('mic', 'microseconds', 1),
('millis', 'microseconds', 1000),
('sec', 'seconds', 1),
('day', 'days', 1),
('week', 'days', 7),
('mon', 'days', 30),
('year', 'days', 365)]
try:
tokens = relative.lower().split(' ')
past = False
if tokens[-1] == 'ago':
past = True
tokens = tokens[:-1]
elif tokens[0] == 'in':
tokens = tokens[1:]
units = dict(days = 0, seconds = 0, microseconds = 0)
#we should always get pairs, if not we let this die and throw an exception
while len(tokens) > 0:
value = tokens.pop(0)
if value == 'and': #just skip this token
continue
else:
value = float(value)
unit = tokens.pop(0)
for match, time_unit, time_constant in unit_mapping:
if unit.startswith(match):
units[time_unit] += value * time_constant
return datetime.timedelta(**units), past
except Exception as e:
raise ValueError("Don't know how to parse %s: %s" % (relative, e))
Это может анализировать такие вещи, как:
2 days agoin 60 seconds2 DAY and 4 Secsin 1 year, 1 Month, 2 days and 4 MICRO2 Weeks 4 secs ago7 millis agoОгромный, но: он упрощает месяц и год до 30 и 365 дней соответственно. Не всегда то, что вы хотите, хотя этого достаточно для некоторых случаев.
Пользовательская функция для преобразования x hours ago в datetime, x hour, y mins ago в datetime и т.д. В Python.
Функция принимает единственный параметр типа string, который анализируется с помощью RegExp. RegExp может быть настроен в соответствии с функцией ввода.
Для использования см. Примеры ниже.
import re
from datetime import datetime, timedelta
def convert_datetime(datetime_ago):
matches = re.search(r"(\d+ weeks?,? )?(\d+ days?,? )?(\d+ hours?,? )?(\d+ mins?,? )?(\d+ secs? )?ago", datetime_ago)
if not matches:
return None
date_pieces = {'week': 0, 'day': 0, 'hour': 0, 'min': 0, 'sec': 0}
for i in range(1, len(date_pieces) + 1):
if matches.group(i):
value_unit = matches.group(i).rstrip(', ')
if len(value_unit.split()) == 2:
value, unit = value_unit.split()
date_pieces[unit.rstrip('s')] = int(value)
d = datetime.today() - timedelta(
weeks=date_pieces['week'],
days=date_pieces['day'],
hours=date_pieces['hour'],
minutes=date_pieces['min'],
seconds=date_pieces['sec']
)
return d
Пример использования:
dates = [
'1 week, 6 days, 11 hours, 20 mins, 13 secs ago',
'1 week, 10 hours ago',
'1 week, 1 day ago',
'6 days, 11 hours, 20 mins ago',
'1 hour ago',
'11 hours, 20 mins ago',
'20 mins 10 secs ago',
'10 secs ago',
'1 sec ago',
]
for date in dates:
print(convert_datetime(date))
Выход:
2019-05-10 06:26:40.937027
2019-05-16 07:46:53.937027
2019-05-15 17:46:53.937027
2019-05-17 06:26:53.937027
2019-05-23 16:46:53.937027
2019-05-23 06:26:53.937027
2019-05-23 17:26:43.937027
2019-05-23 17:46:43.937027
2019-05-23 17:46:52.937027