Python 'x days ago' to datetime

У меня есть строки, которые показывают дату в следующем формате:

x minutes/hours/days/months/years ago

Мне нужно разобрать это на datetime, используя python.

Кажется, датаутил не может этого сделать.

Есть ли способ сделать это?

Ответ 1

Конечно, вы можете это сделать. Вам просто нужно 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 в аргументах (и, по-видимому, аргументы должны быть целыми).

Ответ 2

Это можно легко сделать с помощью 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().

Ответ 3

Так как ваши аргументы - это что-то вроде 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')

Ответ 4

полностью преувеличенное решение, но мне нужно было что-то более гибкое:

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 дней соответственно. Не всегда то, что вы хотите, хотя этого достаточно для некоторых случаев.

Ответ 5

Пользовательская функция для преобразования 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