Учитывая диапазон дат, как рассчитать количество выходных частично или полностью в этом диапазоне? - PullRequest
1 голос
/ 19 мая 2009

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

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

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

Черт возьми, теперь это будет включать целочисленное деление на 7 и некоторую логику, чтобы добавить 1 в зависимости от остатка, но я не могу понять, что ...

дополнительные баллы за ответы в Python ;-)

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

Вот мой окончательный код.

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

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

Ответы [ 4 ]

5 голосов
/ 19 мая 2009

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

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

Воскресенье: 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) будет проще понять.

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

2 голосов
/ 12 мая 2010

Мой общий подход к такого рода вещам: не начинайте бездельничать, пытаясь переопределить свою собственную логику дат - это сложно, т.е. ты испортишь это для крайних случаев и выглядишь плохо. Подсказка: , если у вас в программе есть арифметика с модом 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

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

1 голос
/ 19 мая 2009

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

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)

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

0 голосов
/ 19 мая 2009

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

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

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

...