Страницы

Поиск по вопросам

пятница, 13 декабря 2019 г.

Преобразование даты между строковыми представлениями

#python #python_3x #локализация #datetime


Мне приходят данные в виде 'Март 1, 2010', 'Сен. 1, 2010' и т.п. Меняю им вид вот так: 

def date_convertion(datetime):
    res = datetime.split(' ')
    res.reverse()
    return (res[0] + '-' + res[2][:-1] + '-' + res[1][:-1])


datetime получаю вот такой:

['Март 1, 2010']


на выходе функции это уже вот так:

['2010-Март-1']


Вопрос: как "Март" преобразовать в "03"? Создать словарь (он же ассоциативный массив)
и искать по ключу значение или можно это сделать иначе?
    


Ответы

Ответ 1



При преобразовании из одного строкового формата в другой, особенно если входной формат нефиксированный и возможны вариации, то полезно использовать промежуточный объект-дату, чтобы убедиться что значения были интерпретированы корректно. входная строка -> объект-дата -> выходная строка Например: datetime.strptime("входная строка", "формат") -> datetime объект -> dt.strftime("формат вывода") setlocale() вариант Если русская локаль установлена для программы или известно её название на данной платформе, то можно её активировать и попробовать распознать входные строки, используя фиксированный список форматов: #!/usr/bin/env python3 import locale from datetime import datetime locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8') # the ru locale is installed date_strings = ['Март 1, 2010', 'Сен. 1, 2010', '2015-Апрель-26'] print(date_strings) date_formats = '%B %d, %Y', '%b %d, %Y', '%Y-%B-%d' dates = [] for date_str in date_strings: date_str = date_str.replace('Сен.', 'Сент.') # fix the abbr. for date_fmt in date_formats: try: dates.append(datetime.strptime(date_str, date_fmt).date()) except ValueError: pass else: break else: print('failed to parse %r' % date_str) output_date_strings = list(map(str, dates)) print(output_date_strings) Вывод ['Март 1, 2010', 'Сен. 1, 2010', '2015-Апрель-26'] ['2010-03-01', '2010-09-01', '2015-04-26'] ICU вариант Если есть возможность установить PyICU, то можно использовать несколько локалей независимо от наличия соответствующей системной локали и без изменения глобального состояния программы (может быть полезно в многопоточном приложении) [синтакс ICU форматов для времени]: #!/usr/bin/env python3 from datetime import datetime import icu # PyICU date_strings = ['Март 1, 2010', 'Сен. 1, 2010', '2015-Апрель-26'] print(date_strings) df = icu.SimpleDateFormat('', icu.Locale('ru')) output_df = icu.SimpleDateFormat('yyyy-MM-dd') output_date_strings = [] for date_str in date_strings: date_str = date_str.replace('Сен.', 'Сент.') # fix the abbr. for pattern in 'LLLL d, yyyy', 'yyyy-LLLL-dd': df.applyPattern(pattern) try: output_date_strings.append(output_df.format(df.parse(date_str))) except icu.ICUError: pass else: break else: print('failed to parse %r' % date_str) print(output_date_strings) Результат такой же как у первой программы. str.replace() вариант Если входные данные более разнообразны, то можно ещё добавить предварительный шаг, который сделает их более регулярными, например, как date_str.replace() выше (можно словарь использовать со списком замен). Например, можно избавиться от зависимости на локаль, заменяя все названия соответствующими цифрами: for old, new in [('Март', '3'), ('Сен.', '9'), ('Апрель', '4')]: date_str = date_str.replace(old, new) После этого можно использовать datetime.strptime() c '%m %d, %Y', '%Y-%m-%d' форматами без setlocale() вызова.

Ответ 2



Кратко Многое в решении вашей проблемы зависит от формата строки. Если названия месяцев соответствуют русской локали и записаны либо полностью, либо сокращённо до трёх букв, то можете воспользоваться стандартной библиотекой datetime. В противном случае, если единого формата нет, можете задать словарь замен. Например (для двух месяцев): month = {'Март': '03', 'Апр.': '04'} Использовать его в вашем случае легко: month[res[2][:-1]]. Небольшое замечание. Решение с использованием reverse и split, конечно, работоспособно, но не очень хорошо читается. Я бы использовал на вашем месте регулярные выражения. Тогда при замене формата строки изменения в программе будут минимальными. Замена с помощью datetime Для начала задайте локаль (достаточно один раз в начале программы). Если у вас система уже настроена на русский язык, то достаточно выполнить следующие команды: import locale locale.setlocale(locale.LC_ALL, '') Если язык системы другой, то вместо пустых кавычек укажите требуемую локаль. При помощи метода strptime вы можете проанализировать строку на соответствие некоторому формату. Пример: from datetime import datetime d = datetime.strptime("Апр. 6, 2015", "%b. %d, %Y") print(d) 2015-04-06 00:00:00 Здесь %b — краткое название месяца в текущей локали, %d — номер дня, %Y — номер года (4 цифры). В переменной d будет находиться экземпляр класса datetime, который можно привести к любому другому виду. В вашем случае: print(d.strftime('%Y-%m-%d')) 2015-04-06 Перечисленный операции можно объединить в одну функцию и использовать её для добавления элементов в список: def date_convert(s): return datetime.strptime(s, "%b. %d, %Y").strftime('%Y-%m-%d') [date_convert(d) for d in dates] Здесь dates — список строк, хранящих даты в указанном вами формате. Если требуется одновременно использовать две локали, то действия с датами можно выполнять, например, внутри with написав контекстный процессор. Библиотека datetime

Ответ 3



Добавление элемента в список или в ассоциативный массив Добавление элемента в список производится с помощью функции append: my_list.append( new_element ) Другой способ: my_list += [ new_element ] Добавление в ассоциативный массив выполняется так: my_dict.update({ new_key : new_element }) или ещё проще: my_dict[ new_key ] = new_element Это если говорить в общем. У вас же вопрос немного туманный и не совсем понятно, что вы пытаетесь сделать. Преобразование даты между форматами Преобразование даты осуществляется обычно с помощью функции strptime, которая из вашей строки сделает объект даты, с которым можно выполнять дальнейшие манипуляции, в том числе форматирование в другой формат. Это будет выглядеть так: d = datetime.datetime.strptime(d_string, fmt) d_string2 = d.strfime(fmt2) В данном случае я преобразовал исходную строку d_string из формата fmt в формат fmt2, результат записан в d_string2. (правда это к спискам не имеет никакого отношения).

Ответ 4



import functools, datetime, icu _Replace = [('фев', 'февр'), ('ноя', 'нояб'), ('май', 'мая'), ('сен', 'сент'), ] MonthReplace = (_Replace + [(s.capitalize(), r) for (s, r) in _Replace]) # для автозамены + capitalize AllLocales = (En, Ru,) = list(map(icu.Locale, ['en', 'ru', ])) TZ = icu.ICUtzinfo.getDefault() # time zone, ~ в 2012г была другая, эти даты парсит на час назад s_rus = 'авдимнопсфчя' # первые буквы имен месяцев и недель S_Rus = set(s_rus + s_rus.upper()) IcuDateMasks = ['dd/MMM/yy HH:mm:ss', 'dd/MMM/yy HH:mm', 'EEE, dd MMM y HH:mm:ss', 'EEE, dd MMM y HH:mm', 'EEE, dd MMM y', 'EEE MMM dd HH:mm:ss y', ] # http://userguide.icu-project.org/formatparse/datetime DateMaskForIcu = {(mask, lcl): icu.SimpleDateFormat(mask, lcl) for mask in IcuDateMasks for lcl in AllLocales} @functools.lru_cache() def str_to_datetime(datetime_str: str) -> 'datetime.datetime': (dtime_str, lcl) = _find_locale(datetime_str.split('+', 1)[0].strip()) for mask in IcuDateMasks: # выбрать правильный формат маски try: return datetime.datetime.fromtimestamp(icu.DateFormat.parse(DateMaskForIcu[mask, lcl], dtime_str), TZ, ).replace(tzinfo=None) except icu.ICUError: continue raise UserWarning(f'"{dtime_str}" не распознана в локали "{lcl}", ни по одной из масок "{IcuDateMasks}" !!!') def _find_locale(datetime_str: str) -> (str, 'icu.Locale'): if any((s in datetime_str) for s in S_Rus): # рус locale for (s, r) in MonthReplace: if s in datetime_str: datetime_str = datetime_str.replace(s, r, 1) # 'правильные' имена месяцев break return datetime_str, Ru else: return datetime_str, En if __name__ == '__main__': print(str_to_datetime('04/июн/12 11:56')) # 2012-06-04 11:56:00 print(str_to_datetime('15/Jan/19 2:49')) # 2019-01-15 02:49:00 print(str_to_datetime('15/Фев/19 20:42:01')) # 2019-02-15 20:42:01 print(str_to_datetime('Пт, 02 ноя 2018 14:26:00')) # 2018-11-02 14:26:00

Комментариев нет:

Отправить комментарий