Как я могу получить правильный payperiod от даты? - PullRequest
3 голосов
/ 02 февраля 2010

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

1) определить период оплаты, в который попадает данная дата

//Something like this:
public static DateTime getPayPeriodStartDate(DateTime givenDate, string EvenOrOdd)
{ .. }

2) получить список периодов оплаты между двумя датами:

//Something like this:
public static List<DateTime> getPayPeriodsBetween(DateTime start, DateTime end, string EvenOrOdd)
{ .. }

Я использую пару дат в качестве фиксированных стандартов, на которых основываются любые будущие даты периода оплаты.Стандартные фиксированные даты для четных и нечетных номеров:

  • Четные - 01/04/09
  • Нечетные - 01/11 / 09

Каждый период оплаты начинается в воскресенье недели и длится две недели.Например, используя стандартные даты выше, первый четный период оплаты начинается 01.04.09 и заканчивается 17.01.09.Первый нечетный период оплаты начинается 01.11.09 и заканчивается 24.01.09.Как видите, есть некоторые совпадения.У нас тысячи сотрудников, поэтому необходимо их немного разделить.

У меня есть решение, основанное на количестве недель, но оно неуклюжее и его нужно «исправлять» каждый новый год.Мне интересно, как бы вы справились с этим.

Ответы [ 5 ]

3 голосов
/ 02 февраля 2010

Не полностью оптимизирован или протестирован, но вот что я придумал:

const int DaysInPeriod = 14;

static IEnumerable<DateTime> GetPayPeriodsInRange(DateTime start, DateTime end, bool isOdd)
{
    var epoch = isOdd ? new DateTime(2009, 11, 1) : new DateTime(2009, 4, 1);
    var periodsTilStart = Math.Floor(((start - epoch).TotalDays) / DaysInPeriod);

    var next = epoch.AddDays(periodsTilStart * DaysInPeriod);

    if (next < start) next = next.AddDays(DaysInPeriod);

    while (next <= end)
    {
        yield return next;
        next = next.AddDays(DaysInPeriod);
    }

    yield break;
}

static DateTime GetPayPeriodStartDate(DateTime givenDate, bool isOdd)
{
    var candidatePeriods = GetPayPeriodsInRange(givenDate.AddDays(-DaysInPeriod), givenDate.AddDays(DaysInPeriod), isOdd);
    var period = from p in candidatePeriods where (p <= givenDate) && (givenDate < p.AddDays(DaysInPeriod)) select p;
    return period.First();
}
1 голос
/ 02 февраля 2010

Я не тестировал много тестовых случаев, но я думаю, что это отвечает всем требованиям:

public static DateTime getPayPeriodStartDate(DateTime givenDate, string EvenOrOdd)
{
    DateTime newYearsDay = new DateTime(DateTime.Today.Year, 1, 1);
    DateTime firstEvenMonday = newYearsDay.AddDays((8 - (int)newYearsDay.DayOfWeek) % 7);
    DateTime firstOddMonday = firstEvenMonday.AddDays(7);
    TimeSpan span = givenDate - (EvenOrOdd.Equals("Even") ? firstEvenMonday : firstOddMonday);
    int numberOfPayPeriodsPast = span.Days / 14;
    return (EvenOrOdd.Equals("Even") ? firstEvenMonday : firstOddMonday).AddDays(14 * numberOfPayPeriodsPast);
}

public static List<DateTime> getPayPeriodsBetween(DateTime start, DateTime end, string EvenOrOdd)
{
    DateTime currentPayPeriod = getPayPeriodStartDate(start, EvenOrOdd);
    if (currentPayPeriod < start) currentPayPeriod = currentPayPeriod.AddDays(14);
    List<DateTime> dtList = new List<DateTime>();
    while (currentPayPeriod <= end)
    {
        dtList.Add(currentPayPeriod);
        currentPayPeriod = currentPayPeriod.AddDays(14);
    }
    return dtList;
}

Я уверен, что это можно улучшить.

0 голосов
/ 11 апреля 2014

Работает отлично. Я проверил.

 public static DateTime GetFirstDayOfWeek(DateTime dayInWeek)
   {

    CultureInfo _culture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
    CultureInfo _uiculture = (CultureInfo)CultureInfo.CurrentUICulture.Clone();

    _culture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;
    _uiculture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;

    System.Threading.Thread.CurrentThread.CurrentCulture = _culture;
    System.Threading.Thread.CurrentThread.CurrentUICulture = _uiculture;

    // CultureInfo defaultCultureInfo = CultureInfo.CurrentCulture;
    DayOfWeek firstDay = _culture.DateTimeFormat.FirstDayOfWeek;
    DateTime firstDayInWeek = dayInWeek.Date;

    // Logic Of getting pay period Monday(Odd monday)

    int i = Convert.ToInt32(firstDay);
    while (firstDayInWeek.DayOfWeek != firstDay)
        if (i % 2 != 0)
        { firstDayInWeek = firstDayInWeek.AddDays(-1); }
        else
        {
            firstDayInWeek = firstDayInWeek.AddDays(-2);
        }
    return firstDayInWeek;
}
0 голосов
/ 02 февраля 2010

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

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

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

Итак, для номера 1: начните с 04.01.2009 или 01.01.2009 (в зависимости от четной / нечетной недели оплаты) и добавьте 2 недели, пока значение данного даты не станет меньше даты, которую вы тестируете + 2 недель. Это начало периода.

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

Если вы использовали мой метод и использовали базу данных для размещения всей этой информации, она превращается в 2 простых запроса:

1) SELECT * FROM payperiods WHERE startdate<=givenDate ORDER BY startdate LIMIT 1

2) SELECT * FROM payperiods WHERE startdate>=givenDate AND enddate<=givenDate ORDER BY startdate

0 голосов
/ 02 февраля 2010

Мне нужно было сделать что-то подобное, и я смог сделать это очень легко с помощью LINQ. Просто создайте список для четных и нечетных и затем выполняйте запрос между датами из нечетных / четных по мере необходимости. Также я рекомендую перейти к emum для таких параметров, как EvenOrOdd, где у вас есть фиксированные значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...