У меня есть строки, которые показывают дату в следующем формате:
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 ago
in 60 seconds
2 DAY and 4 Secs
in 1 year, 1 Month, 2 days and 4 MICRO
2 Weeks 4 secs ago
7 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