Преобразовать временную строку, выраженную как <число> [m | h | d | s | w] в секундах в Python

Есть ли хороший способ преобразования строки, представляющей время в формате [m | h | d | s | w] (m = минуты, h = часы, d = дни, s = секунды w = неделя), чтобы количество секунд? То есть.

def convert_to_seconds(timeduration):
    ...

convert_to_seconds("1h")
-> 3600

convert_to_seconds("1d")
-> 86400

и т.д.?

Спасибо!

Ответ 1

Да, существует хороший простой метод, который можно использовать на большинстве языков без необходимости читать руководство для библиотеки datetime. Этот метод также может быть экстраполирован на унции/фунты/тонну и т.д. И т.д.:

seconds_per_unit = {"s": 1, "m": 60, "h": 3600, "d": 86400, "w": 604800}

def convert_to_seconds(s):
    return int(s[:-1]) * seconds_per_unit[s[-1]]

Ответ 2

Я рекомендую использовать класс timedelta из модуля datetime:

from datetime import timedelta

UNITS = {"s":"seconds", "m":"minutes", "h":"hours", "d":"days", "w":"weeks"}

def convert_to_seconds(s):
    count = int(s[:-1])
    unit = UNITS[ s[-1] ]
    td = timedelta(**{unit: count})
    return td.seconds + 60 * 60 * 24 * td.days

Внутренне объекты timedelta хранят все в виде микросекунд, секунд и дней. Таким образом, хотя вы можете задавать параметры в единицах измерения, например, в миллисекундах, месяцах или годах, в конце вам придется взять timedelta вами timedelta и преобразовать ее в секунды.

В случае, если синтаксис ** сбивает вас с толку, это применяется синтаксис Python. По сути, все эти вызовы функций эквивалентны:

def f(x, y): pass

f(5, 6)
f(x=5, y=6)
f(y=6, x=5)

d = {"x": 5, "y": 6}
f(**d)

Ответ 3

Мне обычно нужно поддерживать необработанные числа, строковые номера и строковые номера, заканчивающиеся на [m | h | d | s | w].

Эта версия будет обрабатывать: 10, "10", "10 с", "10 м", "10 ч", "10 д", "10 Вт".

Шляпа подсказка @Eli Courtwright ответ на преобразование строки.

UNITS = {"s":"seconds", "m":"minutes", "h":"hours", "d":"days", "w":"weeks"}

def convert_to_seconds(s):
    if isinstance(s, int):
        # We are dealing with a raw number
        return s

    try:
        seconds = int(s)
        # We are dealing with an integer string
        return seconds
    except ValueError:
        # We are dealing with some other string or type
        pass

    # Expecting a string ending in [m|h|d|s|w]
    count = int(s[:-1])
    unit = UNITS[ s[-1] ]
    td = timedelta(**{unit: count})
    return td.seconds + 60 * 60 * 24 * td.days

Ответ 4

Я написал библиотеку с открытым исходным кодом MgntUtils на Java (не php), которая частично отвечает этому требованию. Он содержит статический метод parsingStringToTimeInterval(String value) который анализирует строку, которая, как ожидается, будет содержать некоторое значение временного интервала - числовое значение с необязательным суффиксом единицы времени. Например, строка "38s" будет проанализирована как 38 секунд, "24m" - 24 минуты, "4h" - 4 часа, "3d" - 3 дня и "45" как 45 миллисекунд. Поддерживаемые суффиксы: "s" для секунд, "m" для минут, "h" для часов и "d" для дней. Считается, что строка без суффикса содержит значение в миллисекундах. Суффиксы нечувствительны к регистру. Если предоставленная строка содержит неподдерживаемый суффикс или содержит отрицательное числовое значение или ноль или содержит не числовое значение, то генерируется исключение IllegalArgumentException. Этот метод возвращает класс TimeInterval - класс, также определенный в этой библиотеке. По сути, он содержит два свойства с соответствующими методами получения и установки: long "value" и java.util.concurrent.TimeUnit. Но в дополнение к методам получения и установки этот класс имеет методы toMillis(), toSeconds(), toMinutes(), toHours() toDays(). Эти методы возвращают длинное значение в указанном масштабе времени (так же, как соответствующие методы в классе java.util.concurrent.TimeUnit)

Этот метод может быть очень полезен для анализа свойств временных интервалов, таких как время ожидания или периоды ожидания, из файлов конфигурации. Это устраняет ненужные вычисления от различных временных масштабов до миллисекунд назад и вперед. Учтите, что у вас есть свойство methodInvokingInterval, которое необходимо установить на 5 дней. Таким образом, чтобы установить значение в миллисекундах, вам необходимо рассчитать, что 5 дней - это 432000000 миллисекунд (очевидно, это не невыполнимая задача, но раздражает и подвержена ошибкам), а затем любой, кто увидит значение 432000000, должен будет рассчитать его до 5 дней, которые расстраивает Но используя этот метод, вы получите значение свойства "5d" и вызов кода

long seconds = TextUtils.parsingStringToTimeInterval("5d").toSeconds();

решит вашу проблему конверсии. Очевидно, что это не слишком сложная функция, но она может добавить простоту и ясность в ваши файлы конфигурации и сэкономить некоторое разочарование и "глупый" просчет в ошибках миллисекунд. Вот ссылка на статью, которая описывает библиотеку MgntUtils, а также где ее взять: MgntUtils

Ответ 5

И еще один добавить в смесь. Это решение короткое, но достаточно терпимое и допускает кратные значения, например 10m 30s

from datetime import timedelta
import re

UNITS = {'s':'seconds', 'm':'minutes', 'h':'hours', 'd':'days', 'w':'weeks'}

def convert_to_seconds(s):
    return int(timedelta(**{
        UNITS.get(m.group('unit').lower(), 'seconds'): int(m.group('val'))
        for m in re.finditer(r'(?P<val>\d+)(?P<unit>[smhdw]?)', s, flags=re.I)
    }).total_seconds())

Результаты теста:

>>> convert_to_seconds('10s')
10
>>> convert_to_seconds('1')  # defaults to seconds
1
>>> convert_to_seconds('1m 10s')  # chaining
70
>>> convert_to_seconds('1M10S')  # case insensitive
70
>>> convert_to_seconds('1week 3days')  # ignores 'eek' and 'ays'
864000

не идеально

>>> convert_to_seconds('1month 3days')  # actually 1minute + 3 days
259260
>>> convert_to_seconds('40s 10s')  # 1st value clobbered by 2nd
10