2010-02-02 1 views
3

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

1) определяют расчётного, в котором данная дата выпадает

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

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

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

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

  • Даже - 01/04/09
  • Odd - 01/11/09

Каждый платить период начинается на воскресенье неделю и идет на две недели. Например, используя стандартные даты выше, первый даже период оплаты начинается с 01.04.09 и заканчивается 01/17/09. Первый нечетный период оплаты начинается с 01/11/09 и заканчивается 01/24/09. Как вы можете видеть, есть некоторые совпадения. У нас есть тысячи сотрудников, поэтому их нужно немного разбить.

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

+0

Можете ли вы объяснить фиксированные стандартные даты (нечетные/четные) немного больше. – David

+0

Я читаю это, как и начинается в первое воскресенье года, а Одд начинается во второе воскресенье года? (спасибо @Rubens) – user7116

+0

Я обновил его немного, чтобы лучше объяснить. –

ответ

3

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

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(); 
} 
+0

Похоже на это! Я собираюсь проверить это, и я вернусь к вам. –

+0

Вот и все! Отличный код. Большое спасибо. –

+0

Я думал, что тебе не нужны жестко закодированные даты? –

0

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

+0

Можете ли вы разместить пример того, что вы имеете в виду? –

+1

Хммм, похоже, эта функция будет медленнее и медленнее с годами. Есть ли способ сделать это, который немного улучшится? –

+0

Профиль профайла это, но мой пример начался с 1930 года и делал гораздо более сложные выборы вокруг чередующихся недель/дат/dow менее чем за 150 миллисекунд в простой dev-боксе. Если его программное обеспечение будет активно через 80 лет, он должен испытать хорошую производительность на основе моих номеров. Но хорошая мысль. –

0

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

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

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

Итак, для номера 1: начните с 1/4/2009 или 1/11/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

Это работает, но, как вы говорите, оно неуклюже. Мой текущий метод тоже неуклюж, но это немного меньше. Я надеюсь обновить. –

+0

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

+0

Точка взята. Если ничего лучше не появится, вы получите очки. –

1

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

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

Я бы добавил константу для количества дней в периоде. –

0

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

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; 
} 
Смежные вопросы