2017-02-21 24 views
0

У меня есть код, который проходит каждый день в пределах 10-летнего диапазона, начиная с определенной даты. Дата должна быть добавлена ​​только в том случае, если она соответствует выбранным критериям из формы.Определение даты является частью каждой недели в зависимости от даты начала.

В форме есть поля для месяцев и дней недели. В течение каждого буднего дня есть варианты для каждого, первого, второго и т. Д. Я добавляю «каждый другой» вариант в каждый будний день вместе с полем даты начала.

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

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

Код проверки:

// set start date 
$date = new \DateTime('2019-12-15'); 

// get start date week of year and modulus 
$start_week = date('W', $date->getTimestamp()); 
$start_mod = $start_week % 2; 

// set end time (10 years from start date) 
$end_time = strtotime('+10 years', $date->getTimestamp()); 

// init previous year and week modifier 
$prev_year = false; 
$week_modifier = 1; 

// each day in range 
while($date->getTimestamp() <= $end_time){ 

    // get year 
    $y = $date->format('Y'); 

    // previous year doesn't match current year 
    if($prev_year != $y){ 

     // previous year set 
     if($prev_year){ 

      // get number of weeks in year 
      $weeks_in_year = date('W', mktime(0, 0, 0, 12, 28, $prev_year)); 

      // year has odd number of weeks 
      if($weeks_in_year % 2){ 

       // increment week modifier 
       $week_modifier++; 

      } 

     } 

     // update previous year 
     $prev_year = $y; 

    } 

    // get week of year 
    $w = $date->format('W') + $week_modifier; 

    // check if meets every other criteria (based on start date) 
    $every_other = false; 
    if(($w % 2) == $start_mod){ 
     $every_other = true; 
    } 

    // print date if it is part of every other Tuesday 
    if($date->format('w') == 2 && $every_other){ 
     echo $date->format('Y-m-d'); 
     echo '<br/>'; 
    } 

    // increment day 
    $date->modify('+1 day'); 

} 

Примечание 1: 2020 в следующем году, в котором есть 53 недель.

Примечание 2: У меня была опечатка в этом тестовом коде, который был увеличивающимся модификатор недели вместо его инициализации в 0. Это имело бы больше смысла для меня, что этот код будет работать, если модификатор был инициализирован равным 0 , но вместо этого он работает только при инициализации нечетного числа.

+1

Я бы рассмотрите этот проект: https://github.com/simshaun/recurr, который очень хорош для обработки повторения, это показывает некоторые примеры строк RRULE, которые вы можете использовать: http://www.kanzaki.com/docs/ical/ rrule.html – Theo

+0

... вырезанная версия строки для каждого другого понедельника выглядит так: FREQ = WEEKLY; INTERVAL = 2; BYDAY = MO' (вы можете включать даты начала и окончания и многое другое) – Theo

+0

Спасибо @ Theo. Я не знал о Рекурре. Я определенно буду более внимательно рассматривать это для решения этой и нескольких других проблем с датами! –

ответ

1

Поскольку «каждый» оценивается в непрерывном цикле, вы можете просто отслеживать дни:

$odd = [ true, true, true, true, true, true, true ]; 

... 
// Flip the appropriate day of the week. 
$odd[date('w')] = !$odd[date('w')]; 
// Or start with all 1's, then $odd[date('w')] ^= 1; 

if ($odd[date('w')]) { 
    // This is the "not-other" day 
} 

Модульная арифметика

Этот день $w и мы отмечаем его:

$odd[$w] = !$odd[$w]; 

Теперь мы продвигаемся неизвестным количеством дней $d. Нам нужно правильно перевернуть все дни этого интервала.

Один из способов сделать это - это цикл в течение всех дней. Но ясно, что это не может быть необходимо - у нас есть семь дней в неделю, и они либо странные, либо даже; даже если мы перевернем их все, это будет семь обновлений. Велоспорт через один год будет 365 или 366 обновлений.

С одной стороны, решение 365-цикла имеет преимущество простота. На данный момент работает 7 флип вместо 3652? Если это не так, мы закончили. Так давайте предположим это; но эта оценка должна быть переделана для каждого проекта.

Обратите внимание, что если мы продвинулись на 1 день, нам не нужно ничего делать. Если мы продвигаемся на 2 дня, день [w + 1] должен быть перевернут. Если мы продвигаемся на 5 дней, дни от w + 1 до w + 4 должны быть перевернуты.В общем, дни от ш + 1 до ш + г-1 нужно быть перевернуто:

for ($i = 1; $i < $w+$d; $i++) { 
    $odd[$i % 7] = !$odd[$i % 7]; 
} 

Но теперь обратите внимание, что если мы продвигались на 15 дней, мы бы снова не нужно ничего делать, как если бы мы продвинулись в только один день, так как каждый день недели будет себя найти время переворачивается дважды:

d  need to flip w+... 
1  (none) 
2  1 
3  1, 2 
4  1, 2, 3 
5  1, 2, 3, 4 
6  1, 2, 3, 4, 5 
7  1, 2, 3, 4, 5, 6 
8  1, 2, 3, 4, 5, 6, 0 (or 7) 
9   2, 3, 4, 5, 6, 0 
10   3, 4, 5, 6, 0 
11    4, 5, 6, 0 
12     5, 6, 0 
13     6, 0 
14      0 
15  (none) 

так вот очень разумный компромисс: если мы должны продвигаться по Х дней, относиться к нему, как если бы мы должны были заранее (X% 14) дней. Итак, теперь у нас будет не более 13 обновлений. Остановка сейчас означает, что наш код является тривиальной версией, дополненной стратегически размещенным «% 14». Мы перешли с 3652 до 14 обновлений на десятилетний интервал, и самое лучшее, на что мы могли надеяться, - 7 обновлений. Мы получили почти весь вздох, за очень маленький доллар.

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

Таким образом, мы начинаем переворачивать в dIndex 1, если (1 < d% 14 < 9), или (d% 7), если (d% 14> = 9). И мы заканчиваем на (d% 14) -1, если (d% 14) < 8, 0 в противном случае. Если d% 14 равно 1, начало (с использованием упрощенного правила 1: d% 14 < 9) равно 1, конец равен 0, а так как 0 меньше 1, цикл даже не начнется. Это означает, что упрощенное правило должно работать:

// increase by d days 
d1 = (d%14) < 9 ? 1 : (d%7); 
d2 = (d%14) < 8 ? (d%14-1) : 7; 

for (dd = d1; dd <= d2; dd++) { 
    odd[(w+dd)%7)] = !odd[(w+dd)%7)]; 
} 

выше прелистните правильно «каждый XXX» бит делает максимум 7 пишет, независимо от значения д. Ориентировочная стоимость составляет около 6-7 обновлений, поэтому, если мы делаем это в памяти, в среднем это не стоит, в среднем, по сравнению с ярлыком «% 14». Если мы листать с разделенными запросов SQL в виде Постоянство слоя, с другой стороны ...

(А вы действительно хотите, чтобы проверить, что я не делал ошибок ...)

+0

Отлично! Очень простой и надежный. Благодарю. –

+0

@Lsemi Оказывается, это не сработает для моей ситуации, так как есть случаи, когда мне нужно увеличивать дату на месяц за раз. Но я, вероятно, смогу использовать вашу рекомендацию для чего-то в будущем. –

+0

В этом случае я думаю, что что-то может быть сделано с модульной арифметикой. Я вернусь к вам завтра. – LSerni

 Смежные вопросы

  • Нет связанных вопросов^_^