查找两个日期之间的天数

PHP*_*ari 589 php datetime

如何使用PHP查找两个日期之间的天数?

Adn*_*nan 876

$now = time(); // or your date as well
$your_date = strtotime("2010-01-31");
$datediff = $now - $your_date;

echo round($datediff / (60 * 60 * 24));
Run Code Online (Sandbox Code Playgroud)

  • 忘记闰秒(不,实际上也考虑那些),但这不考虑夏令时变化!每年可以在这些边界上停留一整天.您需要使用DateTime类. (48认同)
  • 我认为返回负数天提供了相关信息.如果您希望将来的日期返回正整数,那么您应该使用`$ your_date- $ now`. (37认同)
  • 闰秒怎么样?并非所有日子都有24*60*60秒.这段代码可能足以用于实际目的,但在非常罕见的边缘情况下并不完全相同. (22认同)
  • @billynoah对不起,我再也没有回来更新我的评论.您必须小心夏令时区.如果您将日期与夏令时再次比较没有它的日期,而不是例如返回7天它返回6.9天.在场上回报6而不是7. (6认同)
  • strtotime()不仅在20年内失败,而且现在还无法使用.OP指定日期,而不是时间.日期可能很旧.这是一个更简单的答案:$ NumberDays = gregoriantojd($ EndM,$ EndD,$ EndY) - gregoriantojd($ StartM,$ StartD,$ StartY); (包含范围).与Datetime Class不同,Gregorian to Julian在v4版本中可用.有效范围是公元前4714年到公元9999年.请注意时髦的参数顺序(就像你需要php的警告). (4认同)
  • 请注意,使用涉及获取Unix时间戳(从1970年1月1日起的秒数)的技术将在二十年左右失败,此时签名的32位整数无法再保持该值.这可能现在看起来不是什么大不了的事,但最终它将成为一个巨大的问题.未来的代码.使用"Datetime"类中的方法进行调查. (3认同)
  • 我发现这个错误.$ datediff = strtotime("2014-04-09") - strtotime("2014-04-02"); echo floor($ datediff /(60*60*24)); 这将返回6而不是7! (2认同)
  • 这不是一个好习惯.在少数情况下会产生不正确的结果.谨慎使用! (2认同)
  • 如上所述,要非常警惕 DST,它会咬你。如果两个日期在两个不同的 DST 设置中,例如:2016-03-01(非 DST)和 2016-09-30(DST),则 strtotime 将使用每个日期的当前 DST 设置,差异将关闭 1 /24 或 .04166°。解决方案是使用 DateTime 对象(如其他解决方案中所述)或强制使用时间和时区,例如:`$diff = (strtotime('2016-09-30 00:00:00 GMT') - strtotime(' 2016-03-01 00:00:00 GMT')) / (6 * 60 * 24);` (2认同)

小智 500

如果您正在使用PHP 5.3 >,这是迄今为止计算差异的最准确方法:

$earlier = new DateTime("2010-07-06");
$later = new DateTime("2010-07-09");

$diff = $later->diff($earlier)->format("%a");
Run Code Online (Sandbox Code Playgroud)

  • 如果你需要_relative_天数(当'$ date1`在'$ date2`之前时为负数),那么使用`$ diff = $ date2-> diff($ date1) - > format("%r%a") ;而是. (16认同)
  • 请注意,因为我们讨论的是时间间隔而不是特定的时间点,格式语法与date()和strftime()语法不同.时间间隔语法可以在这里找到:http://php.net/manual/en/dateinterval.format.php (14认同)
  • 请记住,如果您的日期有时间,这将无法正常工作.例如,如果您的间隔为23:30小时......并且它们在不同的日期,则差异将为0. (13认同)
  • 或者在我的情况下,之间的天数是 $date2->diff($date1)->format("%a") - 1 (2认同)
  • 该函数在 1980 年至 2020 年之间的 14603 次测试中失败了 0 次。 (2认同)
  • @csr-nontol 在这种情况下请不要使用 `->d`,因为每个月的天数都会重置为零。34 天将得到 `->m = 1` 和 `->d = 3`。使用 `->a` 可以得到总天数。 (2认同)

Adi*_*att 157

从PHP 5.3及更高版本开始,添加了新的日期/时间函数以获得差异:

$datetime1 = new DateTime("2010-06-20");

$datetime2 = new DateTime("2011-06-22");

$difference = $datetime1->diff($datetime2);

echo 'Difference: '.$difference->y.' years, ' 
                   .$difference->m.' months, ' 
                   .$difference->d.' days';

print_r($difference);
Run Code Online (Sandbox Code Playgroud)

结果如下:

Difference: 1 years, 0 months, 2 days

DateInterval Object
(
    [y] => 1
    [m] => 0
    [d] => 2
    [h] => 0
    [i] => 0
    [s] => 0
    [invert] => 0
    [days] => 367
)
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你 !

  • 是的,这似乎比接受的答案更好,这在某些情况下不起作用.喜欢:$ from ='2014-03-01'; $到= '2014-03-31'; (8认同)
  • 此函数在 1980 年至 2020 年之间的 14603 次测试中失败了 0 次。 (2认同)

Tat*_*nen 127

将您的日期转换为unix时间戳,然后从另一个中减去一个.这将为您提供以秒为单位的差异,您可以将其除以86400(一天中的秒数),以便为您提供该范围内的大致天数.

如果您的日期是格式25.1.2010,01/25/2010或者2010-01-25,您可以使用以下strtotime功能:

$start = strtotime('2010-01-25');
$end = strtotime('2010-02-20');

$days_between = ceil(abs($end - $start) / 86400);
Run Code Online (Sandbox Code Playgroud)

使用ceil舍入到下一整天的天数.使用floor,如果你想获得这些两个日期之间的充分天量来代替.

如果您的日期已经采用unix时间戳格式,则可以跳过转换并执行该$days_between部分.对于更奇特的日期格式,您可能需要进行一些自定义解析才能使其正确.

  • DST怎么样? (9认同)
  • 让我详细说明一下:让我们说今天早上3点,欧洲几乎所有人都将时钟倒退了一个小时.这意味着今天还有3600秒,这应该反映在UNIX时间戳中.如果是,则表示今天将使用上述计算天数的方式计算两天.我甚至没有开始闰秒,因为PHP和UNIX似乎都没有考虑到这些(这是IMO实际上可以理解的)._TL; DR:并非所有日子都是86,400秒. (6认同)
  • @ toon81 - 我们使用[Unix时间戳](http://en.wikipedia.org/wiki/Unix_time)来避免这种混乱!;) (3认同)
  • @ toon81那个日期还有3600多秒.去往或来自DST时,UNIX时间戳没有任何变化. (2认同)
  • 如果 UNIX 时间戳没有发生任何变化,那么您同意我的看法:在观察 DST 的时区中,`$day` 下午 1 点的 UNIX 时间戳和 `$day+1` 下午 1 点的 UNIX 时间戳之间的差异并不总是 86400 秒. 它可能是 23 或 25 小时的秒数,而不是 24 小时。 (2认同)
  • 不要使用 UNIX 时间戳。原因见上面的评论。 (2认同)

LSe*_*rni 97

TL; DR不能使用UNIX时间戳.不要用time().如果您这样做,请准备好 98.0825%的可靠性使您失败.使用DateTime(或Carbon).

正确的答案是Saksham古普塔(其他答案也是正确的)给出的:

$date1 = new DateTime('2010-07-06');
$date2 = new DateTime('2010-07-09');
$days  = $date2->diff($date1)->format('%a');
Run Code Online (Sandbox Code Playgroud)

或者在程序上作为一个单行:

/**
 * Number of days between two dates.
 *
 * @param date $dt1    First date
 * @param date $dt2    Second date
 * @return int
 */
function daysBetween($dt1, $dt2) {
    return date_diff(
        date_create($dt2),  
        date_create($dt1)
    )->format('%a');
}
Run Code Online (Sandbox Code Playgroud)

需要注意的是:'%a'似乎表示绝对天数.如果您希望它作为有符号整数,即当第二个日期在第一个日期之前时为负,那么您需要使用'%r'前缀(即format('%r%a')).


如果您确实必须使用UNIX时间戳,请将时区设置为GMT以避免下面详述的大部分陷阱.


答案很长:为什么除以24*60*60(又名86400)是不安全的

大多数使用UNIX时间戳(以及86400将其转换为天数)的答案做出两个假设,这些假设放在一起会导致错误结果的场景和可能难以跟踪的细微错误,甚至在几天,几周或几个月后出现成功部署.这不是解决方案不起作用 - 它的工作原理.今天.但它明天可能会停止工作.

第一个错误是没有考虑到当被问及"从昨天过去多少天?"时,如果在当前和"昨天"指示的瞬间不到一整天之后,计算机可以如实地回答.

通常在将"日期"转换为UNIX时间戳时,获得的是该特定日期午夜的时间戳.

因此,在10月1日至10月15日的中午之间,已经过了十五天.但是在10月1日的13:00和10月15日的14:55之间,已经过了15天减去5分钟,并且大多数使用floor()或进行隐式整数转换的解决方案将报告比预期少一天.

所以,"多少天前是Ymd H:我:"?会得出错误的答案.

第二个错误是将一天等同于86400秒.这几乎总是正确的 - 它经常发生,足以忽略它没有的时间.但是,当夏令时发挥作用时,两个连续中午之间的距离以秒为单位肯定不会是每年至少两次86400.比较DST边界的两个日期将得出错误的答案.

因此,即使您使用强制所有日期时间戳到固定小时的"黑客",比如午夜(当您仅指定日 - 月 - 年而非小时 - 分 - 秒时,这也通过各种语言和框架隐式完成;同样在数据库(如MySQL)中使用DATE类型,这是一种广泛使用的公式

 (unix_timestamp(DATE2) - unix_timestamp(DATE1)) / 86400
Run Code Online (Sandbox Code Playgroud)

要么

 floor(time() - strtotime($somedate)) / 86400
Run Code Online (Sandbox Code Playgroud)

当DATE1和DATE2属于同一年度的DST段时,将返回17; 但它可能会返回17.042,更糟糕的是,16.958.使用floor()或任何隐式截断到整数将转换应该是17到16.在其他情况下,像"$ days> 17"这样的表达式将返回true17.042,即使这表示已经过了一天的计数是18岁.

事情变得更加丑陋,因为这样的代码不能跨平台移植,因为有些代码可能会应用闰秒而某些代码可能不会.在那些平台上,两个日期之间的差异不会是86400,但86401,或也许86399.所以代码,5月份的工作,实际上通过了所有测试将打破明年六月时12.99999天被认为是13.两个日期,而不是12天在2015年工作的将在2017年不起作用 - 相同的日期,并且这两年都不是闰年.但是在2018-03-01和2017-03-01之间,在那些关心的平台上,366天将过去而不是365,使2017年成为闰年.

所以,如果你真的想使用UNIX时间戳:

  • round()明智地使用功能,而不是floor().

  • 作为替代方案,不要计算D1-M1-YYY1和D2-M2-YYY2之间的差异.这些日期将被视为D1-M1-YYY1 00:00:00和D2-M2-YYY2 00:00:00.而是在D1-M1-YYY1 22:30:00和D2-M2-YYY2 04:30:00之间进行转换.你总会得到大约二十个小时的剩余时间.这可能变成二十一小时或十九,也许十八小时,五十九分三十六秒.不管.这是一个巨大的利润,将留在那里,并在可预见的未来保持积极.现在你可以floor()安全地截断它.

正确的解决方案,虽然,以避免魔术常量,舍入组装机和维修债务,是

  • 使用时间库(Datetime,Carbon,等等); 不要自己动手

  • 使用真正邪恶的日期选择编写全面的测试用例 - 跨越DST边界,跨越闰年,跨越闰秒等,以及常见日期.理想情况下(调用datetime 很快!)通过顺序组合它们来生成四个整年(和一天)的日期,并确保第一天和被测试日之间的差异稳定增加一.这将确保如果低级例程和闰秒修复中的任何更改尝试造成严重破坏,至少您会知道.

  • 定期与测试套件的其余部分一起运行这些测试.它们只需几毫秒,可以节省数小时的头部刮伤.


无论您的解决方案是什

funcdiff下面的函数在现实世界的场景中实现了一个解决方案(实际上是接受的解决方案).

<?php
$tz         = 'Europe/Rome';
$yearFrom   = 1980;
$yearTo     = 2020;
$verbose    = false;

function funcdiff($date2, $date1) {
    $now        = strtotime($date2);
    $your_date  = strtotime($date1);
    $datediff   = $now - $your_date;
    return floor($datediff / (60 * 60 * 24));
}
########################################

date_default_timezone_set($tz);
$failures   = 0;
$tests      = 0;

$dom = array ( 0, 31, 28, 31, 30,
                  31, 30, 31, 31,
                  30, 31, 30, 31 );
(array_sum($dom) === 365) || die("Thirty days hath September...");
$last   = array();
for ($year = $yearFrom; $year < $yearTo; $year++) {
    $dom[2] = 28;
    // Apply leap year rules.
    if ($year % 4 === 0)   { $dom[2] = 29; }
    if ($year % 100 === 0) { $dom[2] = 28; }
    if ($year % 400 === 0) { $dom[2] = 29; }
    for ($month = 1; $month <= 12; $month ++) {
        for ($day = 1; $day <= $dom[$month]; $day++) {
            $date = sprintf("%04d-%02d-%02d", $year, $month, $day);
            if (count($last) === 7) {
                $tests ++;
                $diff = funcdiff($date, $test = array_shift($last));
                if ((double)$diff !== (double)7) {
                    $failures ++;
                    if ($verbose) {
                        print "There seem to be {$diff} days between {$date} and {$test}\n";
                    }
                }
            }
            $last[] = $date;
        }
    }
}

print "This function failed {$failures} of its {$tests} tests between {$yearFrom} and {$yearTo}.\n";
Run Code Online (Sandbox Code Playgroud)

结果是,

This function failed 280 of its 14603 tests
Run Code Online (Sandbox Code Playgroud)

恐怖故事:"节省时间"的代价

这实际上发生在几个月前.一个聪明的程序员决定通过在几个地方插入臭名昭着的"(MidnightOfDateB-MidnightOfDateA)/ 86400"代码,最多花费大约30秒的计算来节省几微秒.这是一个非常明显的优化,他甚至没有记录它,并且优化通过了集成测试并潜伏了几个月的代码,都没有被注意到.

这发生在一个计算几个畅销销售人员的工资的计划中,其中最少的一个比一个不起眼的五人程序员团队更有影响力.几个月前的某一天,由于重要的原因,这个小虫突然袭来 - 其中一些人一整天都在减薪.他们绝对没有被逗乐.

无限更糟糕的是,他们失去了他们在程序中没有设计的秘密(已经非常少)的信念,并假装 - 并获得 - 一个完整,详细的代码审查与测试用例运行并以外行的方式评论(加上很多在接下来的几周内进行红地毯处理).

我能说什么:从好的方面来说,我们摆脱了大量的技术债务,并且能够重写和重构几个意大利面条的碎片,这些碎片在90年代的摇摆中回到了COBOL的侵扰.毫无疑问,该程序现在运行得更好,并且有更多的调试信息可以在任何看起来很可疑时快速归零.我估计,在可预见的未来,这最后一件事每月可能会节省一到两个人日.

在负面方面,整个喧嚣使公司在前面花费了大约20万欧元 - 加上面子,加上无疑具有一些议价能力(因此还有更多的钱).

负责"优化"的人在灾难发生前一年前改变了工作,但仍然有人要求起诉他要求赔偿.它与上层人员的关系并不顺利,因为它是"最后一个人的错" - 它看起来像是我们清理这件事的设置,最后,我们仍然在狗窝里其中一个团队正计划退出.

百分之九十九,"86400黑客"将完美无瑕地运作.(例如在PHP中,strtotime()将忽略DST并报告在十月的最后一个星期六和下一个星期一的中午之间,正好2*24*60*60秒已经过去,即使这显然不是真的 ......两个错误很乐意使一个人正确).

女士们,先生们,这是其中一个例子.与气囊和安全带,你也许永远不会真正需要的复杂性(和易用性)DateTimeCarbon.但是你可能会(或者你必须要证明你想到这一天的那一天)将在夜间成为一个小偷.做好准备.

  • 当我看到这些答案时,我认为我是愚蠢和疯狂,因为我总是使用DateTime,但是你让我明白使用DateTime是正确的方法.谢谢 (6认同)
  • 详细解释。+1 (4认同)
  • 有时我希望我们能喜欢答案而不是问题。 (4认同)

小智 16

易于使用date_diff

$from=date_create(date('Y-m-d'));
$to=date_create("2013-03-15");
$diff=date_diff($to,$from);
print_r($diff);
echo $diff->format('%R%a days');
Run Code Online (Sandbox Code Playgroud)


use*_*143 16

面向对象的风格:

$datetime1 = new DateTime('2009-10-11');
$datetime2 = new DateTime('2009-10-13');
$interval = $datetime1->diff($datetime2);
echo $interval->format('%R%a days');
Run Code Online (Sandbox Code Playgroud)

程序风格:

$datetime1 = date_create('2009-10-11');
$datetime2 = date_create('2009-10-13');
$interval = date_diff($datetime1, $datetime2);
echo $interval->format('%R%a days');
Run Code Online (Sandbox Code Playgroud)


PHP*_*ari 10

用这个:)

$days = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24);
print $days;
Run Code Online (Sandbox Code Playgroud)

现在它有效

  • 上面的评论很老,但是不正确.StackOverflow**允许你回答你自己的问题(即使你*问*你的问题).然而,在某人已经发布相同的解决方案之后自己回答这个问题却被认为是粗鲁的. (14认同)

Mig*_*sta 10

那么,所选答案不是最正确的答案,因为它会在UTC之外失败.根据时区(列表),可能会有时间调整创建"没有"24小时的天数,这将使计算(60*60*24)失败.

这是一个例子:

date_default_timezone_set('europe/lisbon');
$time1 = strtotime('2016-03-27');
$time2 = strtotime('2016-03-29');
echo floor( ($time2-$time1) /(60*60*24));
 ^-- the output will be **1**
Run Code Online (Sandbox Code Playgroud)

所以正确的解决方案是使用DateTime

date_default_timezone_set('europe/lisbon');
$date1 = new DateTime("2016-03-27");
$date2 = new DateTime("2016-03-29");

echo $date2->diff($date1)->format("%a");
 ^-- the output will be **2**
Run Code Online (Sandbox Code Playgroud)


小智 6

$start = '2013-09-08';
$end = '2013-09-15';
$diff = (strtotime($end)- strtotime($start))/24/3600; 
echo $diff;
Run Code Online (Sandbox Code Playgroud)


Ard*_*rda 6

为了这个和类似的目的,我在我的作曲家项目中使用Carbon.

它就像这样容易:

$dt = Carbon::parse('2010-01-01');
echo $dt->diffInDays(Carbon::now());
Run Code Online (Sandbox Code Playgroud)


Cha*_*ara 6

您可以通过简单地找到日期

<?php
$start  = date_create('1988-08-10');
$end    = date_create(); // Current time and date
$diff   = date_diff( $start, $end );

echo 'The difference is ';
echo  $diff->y . ' years, ';
echo  $diff->m . ' months, ';
echo  $diff->d . ' days, ';
echo  $diff->h . ' hours, ';
echo  $diff->i . ' minutes, ';
echo  $diff->s . ' seconds';
// Output: The difference is 28 years, 5 months, 19 days, 20 hours, 34 minutes, 36 seconds

echo 'The difference in days : ' . $diff->days;
// Output: The difference in days : 10398
Run Code Online (Sandbox Code Playgroud)


SM *_*ain 5

计算两个日期之间的差:

$date1=date_create("2013-03-15");
$date2=date_create("2013-12-12");

$diff=date_diff($date1,$date2);

echo $diff->format("%R%a days");
Run Code Online (Sandbox Code Playgroud)

产出:+272天

date_diff()函数返回两个DateTime对象之间的差。