Учитывая диапазон дат, как рассчитать количество выходных дней частично или полностью в этом диапазоне?

Учитывая диапазон дат, как рассчитать количество выходных дней частично или полностью в этом диапазоне?

(Несколько определений по запросу: «выходные» означают субботу и воскресенье. Диапазон дат включен, т. Е. Дата окончания является частью диапазона «полностью или частично» означает, что любая часть выходных дней, попадающих в диапазон дат, означает, что весь уик-энд считается.)

Чтобы упростить, я предполагаю, что вам действительно нужно знать продолжительность и какой день недели начальный день …

Я чертовски хорошо, теперь он будет включать в себя выполнение целочисленного деления на 7 и некоторую логику, чтобы добавить 1 в зависимости от остатка, но я не могу решить, что …

дополнительные баллы за ответы в Python 😉

редактировать

Вот мой последний код.

Выходные дни – пятница и суббота (так как мы рассчитываем, что ночи остались), а дни начинаются с 0 по 0. Я использовал алгоритм onebyone и макет кода Тома. Большое спасибо людям.

def calc_weekends(start_day, duration): days_until_weekend = [5, 4, 3, 2, 1, 1, 6] adjusted_duration = duration - days_until_weekend[start_day] if adjusted_duration < 0: weekends = 0 else: weekends = (adjusted_duration/7)+1 if start_day == 5 and duration % 7 == 0: #Saturday to Saturday is an exception weekends += 1 return weekends if __name__ == "__main__": days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] for start_day in range(0,7): for duration in range(1,16): print "%s to %s (%s days): %s weekends" % (days[start_day], days[(start_day+duration) % 7], duration, calc_weekends(start_day, duration)) print 

Общий подход для такого рода вещей:

За каждый день недели выясните, сколько дней требуется до начала периода с этого дня «содержит выходные». Например, если «содержит выходные» означает «содержит как субботу, а воскресенье», то у нас есть следующая таблица:

Воскресенье: 8 Понедельник: 7 Вторник: 6 Среда: 5 Четверг: 4 Пятница: 3 Суббота: 2

Для «частично или полностью» мы имеем:

Воскресенье: 1 Понедельник: 6 Вторник: 5 Среда: 4 Четверг: 3 Пятница: 2 Суббота: 1

Очевидно, что это не должно быть закодировано как таблица, теперь, когда очевидно, как это выглядит.

Затем, учитывая день недели начала вашего периода, вычитайте [*] магическое значение из длины периода в днях (возможно, начало-начало + 1, чтобы включить оба забора). Если результат меньше 0, он содержит 0 выходных дней. Если он равен или больше 0, он содержит (по крайней мере) 1 уик-энд.

Тогда вам придется иметь дело с оставшимися днями. В первом случае это легко, один дополнительный уик-энд за 7 дней. Это также верно во втором случае для каждого начального дня, кроме воскресенья, для чего требуется еще 6 дней, чтобы включить еще один уик-энд. Таким образом, во втором случае для периодов, начинающихся в воскресенье, вы можете считать 1 выходные в начале периода, а затем вычесть 1 из длины и пересчитать с понедельника.

В более общем плане, то, что происходит здесь для «целых или частичных» выходных, заключается в том, что мы проверяем, начинаем ли мы на полпути через интересный бит («выходные»). Если это так, мы можем:

  • 1) Подсчитайте один, переместите дату начала в конец интересного бита и пересчитайте.
  • 2) Перенесите дату начала в начало интересного бита и пересчитайте.

В случае выходных дней есть только один специальный случай, который начинается на полпути, так что (1) выглядит хорошо. Но если вы получали дату как дату + время в секундах, а не день, или если вас интересовали 5-дневные рабочие недели, а не 2-дневные выходные, то (2) может быть проще понять.

[*] Если вы не используете неподписанные типы, конечно.

Мой общий подход к подобным вещам: не начинайте возиться с попыткой переопределить свою собственную логику даты – это сложно, т.е. вы привинтите его к краям и выглядите плохо. Подсказка: если у вас есть арифметика mod 7 в любой точке вашей программы или вы обрабатываете даты как целые числа в вашей программе: вы терпите неудачу . Если бы я увидел «принятое решение» где угодно (или даже близко) к моей кодовой базе, кому-то нужно было бы начать все заново. Это пробуждает воображение, что любой, кто считает себя программистом, проголосует за этот ответ.

Вместо этого используйте встроенную логику даты / времени, которая поставляется с Python:

Во-первых, получите список всех дней, которые вас интересуют:

 from datetime import date, timedelta FRI = 5; SAT = 6 # a couple of random test dates now = date.today() start_date = now - timedelta(57) end_date = now - timedelta(13) print start_date, '...', end_date # debug days = [date.fromordinal(d) for d in range( start_date.toordinal(), end_date.toordinal()+1 )] 

Затем отфильтруйте до тех дней, которые являются выходными. В вашем случае вас интересуют пятничные и субботние вечера, которые составляют 5 и 6. (Обратите внимание, что я не пытаюсь перевернуть эту часть в предыдущее понимание списка, так как это будет трудно проверить как правильно).

 weekend_days = [d for d in days if d.weekday() in (FRI,SAT)] for day in weekend_days: # debug print day, day.weekday() # debug 

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

 num_weekends = len(weekend_days) // 2 # if we start on Friday and end on Saturday we're ok, # otherwise add one weekend # # F,S|F,S|F,S ==3 and 3we, +0 # F,S|F,S|F ==2 but 3we, +1 # S|F,S|F,S ==2 but 3we, +1 # S|F,S|F ==2 but 3we, +1 ends = (weekend_days[0].weekday(), weekend_days[-1].weekday()) if ends != (FRI, SAT): num_weekends += 1 print num_weekends # your answer 

Чем короче, понятнее и понятнее, тем больше вы можете доверять своему коду, и можете справиться с более интересными проблемами.

Чтобы подсчитать все выходные, просто настройте количество дней, чтобы начать в понедельник, а затем разделите их на семь. (Обратите внимание, что если начальный день – это будний день, добавьте дни, чтобы перейти к предыдущему понедельнику, и если он находится в выходные дни, вычитайте дни, чтобы перейти к следующему понедельнику, так как вы уже пропустили эти выходные.)

 days = {"Saturday":-2, "Sunday":-1, "Monday":0, "Tuesday":1, "Wednesday":2, "Thursday":3, "Friday":4} def n_full_weekends(n_days, start_day): n_days += days[start_day] if n_days <= 0: n_weekends = 0 else: n_weekends = n_days//7 return n_weekends if __name__ == "__main__": tests = [("Tuesday", 10, 1), ("Monday", 7, 1), ("Wednesday", 21, 3), ("Saturday", 1, 0), ("Friday", 1, 0), ("Friday", 3, 1), ("Wednesday", 3, 0), ("Sunday", 8, 1), ("Sunday", 21, 2)] for start_day, n_days, expected in tests: print start_day, n_days, expected, n_full_weekends(n_days, start_day) 

Если вы хотите знать частичные выходные (или недели), просто посмотрите на дробную часть деления на семь.

Вам понадобится внешняя логика рядом с исходной математикой. Вам нужно иметь библиотеку календаря (или если у вас есть достаточное количество времени, чтобы реализовать ее самостоятельно), чтобы определить, что на выходных, в какой день недели вы начинаете, заканчиваете и т. Д.

Взгляните на класс календаря Python .

Без логического определения дней в вашем коде чистые математические методы терпят неудачу в случае с углом, например, в течение 1 дня или, я полагаю, что-либо ниже, чем полная неделя (или ниже 6 дней, если вы разрешаете частичные).