根据开始日期确定日期是否是每隔一周的一部分

时间:2017-02-21 22:08:44

标签: php date datetime

我有一些代码在特定日期开始的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 个答案:

答案 0 :(得分:1)

自&#34;每隔一个&#34;在连续循环中进行评估,您可能只是跟踪日期:

$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的天数。通常,需要翻转从w + 1到w + d-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天,那就好像我们必须提前(X%14)天。所以现在我们将运行最多13次更新。现在停止 意味着我们的代码是琐碎的版本,由战略性地放置&#34;%14&#34;增强。我们从3652次更新到14次更新,间隔十年,我们希望最好的是7次更新。我们得到了几乎所有的爆炸声,只有很少的降压。

如果我们想要满足于最好的一切,我们继续(但请注意,额外的算术可能最终比从最差值13到更新中的保存更加昂贵,最多为零。单词,进行额外的检查意味着我们将最多保存13个更新;如果这些检查花费超过13个更新,我们最好不要不检查并盲目地进行检查。

因此,如果(1 = 9),则开始翻转(d%7)。如果(d%14)<8,则结束于(d%14)-1,否则为0。如果d%14为1,则开始(使用简化规则1:d%14&lt; 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)];
}

以上内容应正确翻转&#34;其他所有XXX&#34;无论d的值如何,最多可执行7次写操作。大概的成本大约是6-7次更新,所以如果我们在内存中这样做,那么平均而言,如果与&#34;%14&#34;进行比较,那么它并不值得。捷径。另一方面,如果我们将单独的SQL查询翻转到持久层,那么......

(你真的想看看我没有犯错......)