альтернатива strptime для сравнения дат?

Есть ли способ сравнить две даты без вызова strptime каждый раз в python? Я уверен, что с учетом моей проблемы другого пути нет, но я хочу убедиться, что проверил все варианты.

Я просматриваю очень большой файл журнала, в каждой строке есть дата, которую мне нужно сравнить, чтобы увидеть, находится ли эта дата в диапазоне двух других дат. Мне приходится преобразовывать каждую дату для каждой строки с помощью strptime, что вызывает большое узкое место;

Fri Sep  2 15:12:43 2016    output2.file

         63518075 function calls (63517618 primitive calls) in 171.409 seconds

   Ordered by: cumulative time
   List reduced from 571 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.003    0.003  171.410  171.410 script.py:3(<module>)
        1    0.429    0.429  171.367  171.367 scipt.py:1074(main)
        1    3.357    3.357  162.009  162.009 script.py:695(get_data)
  1569898   14.088    0.000  141.175    0.000 script.py:648(check_line)
  1569902    6.899    0.000   71.706    0.000 {built-in method strptime}
  1569902   31.198    0.000   64.805    0.000 /usr/lib64/python2.7/_strptime.py:295(_strptime)
  1569876   15.324    0.000   43.170    0.000 script.py:626(dict_add)
  4709757   23.370    0.000   23.370    0.000 {method 'strftime' of 'datetime.date' objects}
  1569904    1.655    0.000   18.799    0.000 /usr/lib64/python2.7/_strptime.py:27(_getlang)
  1569899    2.103    0.000   17.452    0.000 script.py:592(reverse)

Даты форматируются следующим образом;

current_date = 01/Aug/1995:23:59:53

И я сравниваю их вот так;

with open(logfile) as file:
    for line in file:
        current_date = strptime_method(line)
        if current_date => end_date:
            break

Есть ли альтернатива, когда дело доходит до сравнения дат?

Редактировать: Спасибо всем, в частности пользователю 2539738. Вот результаты, основанные на его/ее предложении, большая разница в скорости;

Fri Sep  2 16:14:59 2016    output3.file

         24270567 function calls (24270110 primitive calls) in 105.466 seconds

   Ordered by: cumulative time
   List reduced from 571 to 10 due to restriction <10>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.002    0.002  105.466  105.466 script.py:3(<module>)
        1    0.487    0.487  105.433  105.433 script.py:1082(main)
        1    3.159    3.159   95.861   95.861 script.py:702(get_data)
  1569898   21.663    0.000   77.138    0.000 script.py:648(check_line)
  1569876   14.979    0.000   43.408    0.000 script.py:626(dict_add)
  4709757   23.865    0.000   23.865    0.000 {method 'strftime' of 'datetime.date' objects}
  1569899    1.943    0.000   15.556    0.000 script.py:592(reverse)
        1    0.000    0.000    9.078    9.078 script.py:1066(print_data)
        1    0.021    0.021    9.044    9.044 script.py:1005(print_ip)
       10    0.001    0.000    7.067    0.707 script.py:778(ip_api)

person user1165419    schedule 02.09.2016    source источник
comment
Если входные записи журнала упорядочены по дате, вам, вероятно, не нужно проверять каждую запись журнала на соответствие диапазону дат, и вы, вероятно, можете выполнить, скажем, двоичный поиск, чтобы определить начальную и конечную записи для вашего диапазона. Просто мысли.   -  person alecxe    schedule 02.09.2016
comment
Что такое strptime_method? Какой-то свой код? Кроме того, вы используете time (функциональный модуль для обработки дат и времени) или datetime (модуль на основе классов для того же самого)?   -  person Vivian    schedule 02.09.2016
comment
@alecxe Это то, что я уже делаю сейчас. Он выйдет из цикла, если обнаружит, что дата выходит за пределы допустимого диапазона. Но если ваш диапазон довольно велик, то, как показывают мои результаты, это может занять много времени, в основном из-за метода strptime в каждой вызываемой строке.   -  person user1165419    schedule 02.09.2016
comment
@DavidHeyman strptime_method — это просто мой собственный код, я делаю несколько вещей со строкой в ​​функции. Я использую datetime для преобразования, поэтому я делаю это; current_time = datetime.datetime.strptime(date_from_line, "%d/%b/%Y:%H:%M:%S")   -  person user1165419    schedule 02.09.2016
comment
Хм. Вы пробовали многопроцессорность или многопоточность? Возможно, вы сможете сэкономить некоторое время, выполняя поиск первой и последней дат в диапазоне одновременно. Кроме того, все могло бы (возможно) обрабатываться быстрее, если бы каждый журнал находился в отдельном файле, а не в одном файле.   -  person Vivian    schedule 02.09.2016
comment
Возможно, вместо этого попробуйте библиотеку time, если она будет быстрее (я думаю, что она внутренне проще, хотя у меня нет возможности перепроверить исходный код прямо сейчас).   -  person Vivian    schedule 02.09.2016
comment
Одна из причин, по которой мне очень нравится формат даты/времени ISO 8601, заключается в том, что их можно сравнивать. как строки без необходимости преобразования во что-либо еще.   -  person Mark Ransom    schedule 02.09.2016


Ответы (2)


Я предполагаю, что current_date - это строка

Во-первых, сделать словарь

moDict = {"Aug":8, "Jan":1} #etc

Затем найдите год/месяц/день и т. д.

current_date = "01/Aug/1995:23:59:53"

Yr = int(current_date[7:11])
Mo = moDict[(current_date[3:6])]
Day = int(current_date[0:2])

m_date = datetime.datetime(Yr,Mo,Day)

И вы можете использовать это для сравнения

person Mohammad Athar    schedule 02.09.2016
comment
Я совершенно не удивлюсь, если узнаю, что strptime уже делает это внутри компании. Вы вообще тестировали скорость? - person Vivian; 02.09.2016
comment
@DavidHeyman Даже если strptime делает это внутри, он должен интерпретировать строку формата. С другой стороны, ему не нужно интерпретировать Python. :) - person Kaz; 02.09.2016

Поскольку ваши даты имеют формат фиксированной длины, их легко разобрать, и вам не нужно strptime для этого. Вы можете преобразовать их в формат даты/времени ISO 8601 и сравнить их непосредственно как строки!

mos = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}

def custom_to_8601(dt):
    return dt[7:11] + '-' + mos[dt[3:6]] + '-' + dt[0:2] + 'T' + dt[12:]

>>> custom_to_8601('01/Aug/1995:23:59:53')
'1995-08-01T23:59:53'

Возможно, было бы немного быстрее использовать join вместо конкатенации строк и опускать знаки препинания:

def comparable_date(dt):
    return ''.join([dt[7:11], mos[dt[3:6]], dt[0:2], dt[12:]])

>>> comparable_date('01/Aug/1995:23:59:53')
'1995080123:59:53'

Запуск cProfile на 1000000 повторений для меня дает следующие тайминги:

  • custom_to_8601: 0,978 секунды
  • comparable_date: 0,937 секунды
  • ваш исходный код с strptime: 15,492 секунды
  • более ранний ответ с использованием конструктора datetime: 1,134 секунды
person Mark Ransom    schedule 02.09.2016
comment
Спасибо, попробую и отпишусь о результатах! - person user1165419; 02.09.2016