"Passing Go" в (Python) диапазоне дат - PullRequest
2 голосов
/ 15 июня 2010

Обновлено для удаления постороннего текста и неоднозначности.

Правила:
Работнику начисляется 8 часов оплачиваемого рабочего дня на следующий день после каждого квартала. Кварталы, а именно:

  • 1 января - 31 марта
  • 1 апреля - 30 июня
  • 1 июля - 30 сентября
  • 1 октября - 31 декабря

Проблема
Используя python, мне нужно определить внутренности следующей функции:

def acrued_hours_between(start_date, end_date): 
    # stuff
    return integer

В настоящее время я использую Python, и мне интересно, каким будет правильный подход к чему-то подобному.

Я предполагаю, что использование объектов DateTime и, возможно, модуля dateutil, помогло бы здесь, но мой мозг почему-то не решает эту проблему.

Обновление
Я полагаю, что расчет несколько прост, так как проблема в следующем:

"Сколько часов оплачиваемого нерабочего времени начислено с начала_даты до конца_даты?" учитывая вышеприведенные «правила».

Ответы [ 5 ]

5 голосов
/ 15 июня 2010

При редактировании ОП упоминается настоящая основная проблема:

"Сколько часов оплачиваемого времени отработано с даты X до даты Y?"

Я согласен, и я рассчитал бы это самым прямым и простым способом, например:

import datetime
import itertools

accrual_months_days = (1,1), (4,1), (7,1), (10,1)

def accruals(begin_date, end_date, hours_per=8):
  """Vacation accrued between begin_date and end_date included."""
  cur_year = begin_date.year - 1
  result = 0
  for m, d in itertools.cycle(accrual_months_days):
    if m == 1: cur_year += 1
    d = datetime.date(cur_year, m, d)
    if d < begin_date: continue
    if d > end_date: return result
    result += hours_per

if __name__ == '__main__':  # examples
  print accruals(datetime.date(2010, 1, 12), datetime.date(2010, 9, 20))
  print accruals(datetime.date(2010, 4, 20), datetime.date(2012, 12, 21))
  print accruals(datetime.date(2010, 12, 21), datetime.date(2012, 4, 20))

Прямая формула, конечно, была бы быстрее, но было бы сложно сделать это без ошибок - еслиничто иное, этот пример «исправить по проверке» может служить для автоматической калибровки более быстрого, проверяя, согласны ли они с большой выборкой пар дат (обязательно включите в последний все угловые случаи, такие как первый и последний дни кварталовконечно).

1 голос
/ 15 июня 2010

Это можно сделать с помощью простой старой целочисленной математики:

from datetime import date

def hours_accrued(start, end):
    '''hours_accrued(date, date) -> int

    Answers the question "How many hours of Paid Time Off
      are accrued from X-date to Y-date?"

    >>> hours_accrued(date(2010, 4, 20), date(2012, 12, 21))
    80
    >>> hours_accrued(date(2010, 12, 21), date(2012, 4, 20))
    48
    '''
    return ( 4*(end.year - start.year)
        + ((end.month-1)/3 - (start.month-1)/3) ) * 8
1 голос
/ 15 июня 2010

Я бы отсортировал все события для конкретного сотрудника по времени и смоделировал события в этом порядке, проверяя, чтобы доступные дни оплачиваемого отпуска никогда не опускались ниже нуля. Платный запрос на выходной - это событие со значением - (количество часов). 1 января есть событие со значением +8 часов.

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

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

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

0 голосов
/ 15 июня 2010

Установите кортеж для каждого диапазона дат (назовем их кварталы ). В кортеже хранится квартал (в качестве кардинального индекса или в качестве даты начала), максимальное количество накопленных часов за квартал и количество использованных часов в квартале. Вы захотите иметь набор кортежей, которые будут отсортированы, чтобы это работало, поэтому простой список, вероятно, не лучший вариант. Словарь мог бы быть лучшим способом приблизиться к этому с четвертью как ключ и максимальными / используемыми записями, возвращенными в кортеже, поскольку это может быть "отсортировано".

(Примечание: я посмотрел исходное объяснение и переписал свой ответ)

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

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

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

Если в любом квартале вычисленное время падает ниже нуля, немедленно произойдет сбой и отклоните запрос. В противном случае продолжайте до тех пор, пока у вас не закончатся кварталы.

Если все четверти вычислены, обновите исходный набор данных с копией и удовлетворите запрос.

0 голосов
/ 15 июня 2010

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

...