php 查找两个日期之间的天数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2040560/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 04:46:18  来源:igfitidea点击:

Finding the number of days between two dates

phpdatetime

提问by PHP Ferrari

How to find number of days between two dates using PHP?

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

回答by Adnan

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

echo round($datediff / (60 * 60 * 24));

回答by Saksham Gupta

If you're using PHP 5.3 >, this is by far the most accurate way of calculating the difference:

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

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

$diff = $later->diff($earlier)->format("%a");

回答by Aditya P Bhatt

From PHP Version 5.3 and up, new date/time functionshave been added to get difference:

从 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);

Result as below:

结果如下:

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
)

Hope it helps !

希望能帮助到你 !

回答by Tatu Ulmanen

Convert your dates to unix timestamps, then substract one from the another. That will give you the difference in seconds, which you divide by 86400 (amount of seconds in a day) to give you an approximate amount of days in that range.

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

If your dates are in format 25.1.2010, 01/25/2010or 2010-01-25, you can use the strtotimefunction:

如果您的日期格式为25.1.2010,01/25/20102010-01-25,则可以使用该strtotime函数:

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

$days_between = ceil(abs($end - $start) / 86400);

Using ceilrounds the amount of days up to the next full day. Use floorinstead if you want to get the amount of full days between those two dates.

使用ceil将天数四舍五入到下一个全天。floor如果您想获得这两个日期之间的全天数,请改用。

If your dates are already in unix timestamp format, you can skip the converting and just do the $days_betweenpart. For more exotic date formats, you might have to do some custom parsing to get it right.

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

回答by LSerni

TL;DRdo notuse UNIX timestamps. Do not use time(). If you do, be preparedshould its 98.0825% reliability fail you. Use DateTime (or Carbon).

TL; DR不能使用UNIX时间戳。不要使用time(). 如果您这样做,请做好准备,以防其 98.0825% 的可靠性让您失望。使用日期时间(或碳)。

The correct answeris the one given by Saksham Gupta (other answers are also correct):

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

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

Or procedurally as a one-liner:

或者在程序上作为单行:

/**
 * 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');
}

With a caveat: the '%a' seems to indicate the absolutenumber of days. If you want it as a signed integer, i.e. negative when the second date is before the first, then you need to use the '%r' prefix (i.e. format('%r%a')).

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



If you really must use UNIX timestamps, set the time zone to GMTto avoid mostof the pitfalls detailed below.

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



Long answer: why dividing by 24*60*60 (aka 86400) is unsafe

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

Most of the answers using UNIX timestamps (and 86400 to convert that to days) make two assumptions that, put together, can lead to scenarios with wrong resultsand subtle bugsthat may be difficult to track, and arise even days, weeks or months after a successful deployment. It's not that the solution doesn't work - it works. Today. But it might stop working tomorrow.

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

First mistake is not considering that when asked, "How many days passed since yesterday?", a computer might truthfully answer zeroif between the present and the instant indicated by "yesterday" less than one whole dayhas passed.

第一个错误是没有考虑到当被问到“从昨天开始过去了多少天?”时,如果在现在和“昨天”所指示的时刻之间过去了不到一整天,计算机可能会如实回答

Usually when converting a "day" to a UNIX timestamp, what is obtained is the timestamp for the midnightof that particular day.

通常在将“天”转换为 UNIX 时间戳时,获得的是该特定日期午夜的时间戳。

So between the midnights of October 1st and October 15th, fifteen days have elapsed. But between 13:00 of October 1st and 14:55 of October 15th, fifteen days minus 5 minuteshave elapsed, and most solutions using floor()or doing implicit integer conversion will report one day less than expected.

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

So, "how many days ago was Y-m-d H:i:s"? will yield the wrong answer.

那么,“Ymd H:i:s 是多少天前”?会产生错误的答案

The second mistake is equating one day to 86400 seconds. This is almost alwaystrue - it happens often enough to overlook the times it doesn't. But the distance in seconds between two consecutive midnights is surelynot 86400 at least twice a year when daylight saving time comes into play. Comparing two dates across a DST boundary will yield the wrong answer.

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

So even if you use the "hack" of forcing all date timestamps to a fixed hour, say midnight (this is also done implicitly by various languages and frameworks when you only specify day-month-year and not also hour-minute-second; same happpens with DATE type in databases such as MySQL), the widely used formula

因此,即使您使用强制所有日期时间戳为固定小时的“hack”,比如午夜(当您仅指定日-月-年而不是时-分-秒时,这也由各种语言和框架隐式完成; MySQL等数据库中的DATE类型也是如此),广泛使用的公式

 (unix_timestamp(DATE2) - unix_timestamp(DATE1)) / 86400

or

或者

 floor(time() - strtotime($somedate)) / 86400

will return, say, 17 when DATE1 and DATE2 are in the same DST segment of the year; but it may return 17.042, and worse still, 16.958. The use of floor()or any implicit truncation to integer will then convert what should have been a 17 to a 16. In other circumstances, expressions like "$days > 17" will return truefor 17.042 even if this indicates that the elapsed day count is 18.

例如,当 DATE1 和 DATE2 处于同一年的 DST 段时,将返回 17;但它可能会返回 17.042,更糟的是,会返回 16.958。使用floor()或任何隐式截断为整数会将本应为 17 的值转换为 16。在其他情况下,“$days > 17”等表达式将返回true17.042,即使这表示经过的天数是 18。

And things grow even uglier since such code is not portableacross platforms, because some of them may apply leap seconds and some might not. On those platforms that do, the difference between two dates will not be 86400 but 86401, or maybe 86399. So code that worked in May and actually passed all tests will break next June when 12.99999 days are considered 12 days instead of 13. Two dates that worked in 2015 will not work in 2017 -- the samedates, and neither year is a leap year. But between 2018-03-01 and 2017-03-01, on those platforms that care, 366days will have passed instead of 365, making 2018 a leap year (which it is not).

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

So if you really want to use UNIX timestamps:

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

  • use round()function wisely, not floor().

  • as an alternative, do not calculate differences between D1-M1-YYY1 and D2-M2-YYY2. Those dates will be really considered as D1-M1-YYY1 00:00:00 and D2-M2-YYY2 00:00:00. Rather, convert between D1-M1-YYY1 22:30:00 and D2-M2-YYY2 04:30:00. You will alwaysget a remainder of about twenty hours. This may become twenty-one hours or nineteen, and maybe eighteen hours, fifty-nine minutes thirty-six seconds. No matter. It is a large marginwhich will stay there and stay positive for the foreseeable future. Nowyou can truncate it with floor()in safety.

  • 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()安全地截断它。

The correctsolution though, to avoid magic constants, rounding kludges and a maintenance debt, is to

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

  • use a time library (Datetime, Carbon, whatever); don't roll your own

  • write comprehensive test casesusing really evil date choices - across DST boundaries, across leap years, across leap seconds, and so on, as well as commonplace dates. Ideally (calls to datetime are fast!) generate four whole years' (and one day)worth of dates by assembling them from strings, sequentially, and ensure that the difference between the first day and the day being tested increases steadily by one. This will ensure that if anything changes in the low-level routines and leap seconds fixestry to wreak havoc, at least you will know.

  • run those tests regularly together with the rest of the test suite. They're a matter of milliseconds, and may save you literally hoursof head scratching.

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

  • 使用真正邪恶的日期选择编写全面的测试用例——跨越 DST 边界、跨越闰年、跨越闰秒等等,以及普通日期。理想情况下(对 datetime 的调用速度很快!)通过从字符串中按顺序组装日期来生成整整四年(和一天)的日期,并确保第一天和被测试的那天之间的差异稳定增加 1。这将确保如果低级例程和闰秒修复中的任何更改试图造成严重破坏,至少您会知道

  • 定期与测试套件的其余部分一起运行这些测试。它们只需要几毫秒,并且可以为您节省数小时的挠头时间。



Whatever your solution, test it!

无论您的解决方案是什么,请测试它!

The function funcdiffbelow implements one of the solutions (as it happens, the accepted one) in a real world scenario.

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";

The result is,

结果是,

This function failed 280 of its 14603 tests

Horror Story: the cost of "saving time"

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

This actually happened some months ago. An ingenious programmer decided to save several microseconds off a calculation that took about thirty seconds at most, by plugging in the infamous "(MidnightOfDateB-MidnightOfDateA)/86400" code in several places. It was so obvious an optimization that he did not even document it, and the optimization passed the integration tests and lurked in the code for several months, all unnoticed.

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

This happened in a program that calculates the wages for several top-selling salesmen, the least of which has a frightful lot more clout than a whole humble five-people programmer team taken together. One day some months ago, for reasons that matter little, the bug struck -- and someof those guys got shortchanged one whole day of fat commissions. They were definitely notamused.

这发生在一个计算几个最畅销销售人员工资的程序中,其中最少的一个比整个不起眼的五人程序员团队加在一起的影响力要大得多。几个月前的一天,由于一些无关紧要的原因,这个错误发生了——其中一些人被骗了一整天的丰厚佣金。他们绝对没有被逗乐。

Infinitely worse, they lost the (already very little) faith they had in the program notbeing designed to surreptitiously shaft them, and pretended - and obtained- a complete, detailed code review with test cases ran and commented in layman's terms (plus a lot of red-carpet treatment in the following weeks).

更糟糕的是,他们失去了(已经很少)相信该程序不是为了秘密地操纵他们而设计的,并假装 - 并获得- 一个完整的、详细的代码,测试用例运行并用外行的术语进行评论(加上很多在接下来的几周内进行红地毯治疗)。

What can I say: on the plus side, we got rid of a lot of technical debt, and were able to rewrite and refactor several pieces of a spaghetti mess that hearkened back to a COBOL infestation in the swinging '90s. The program undoubtedly runs better now, and there's a lot more debugging information to quickly zero in when anything looks fishy. I estimate that just this last one thing will save perhaps one or two man-days per month for the foreseeable future.

我能说什么呢:从好的方面来说,我们摆脱了很多技术债务,并且能够重写和重构意大利面的几块碎片,这些碎片可以追溯到摇摆不定的 90 年代的 COBOL 感染。该程序现在无疑运行得更好,并且有更多的调试信息可以在任何看起来可疑的情况下快速归零。我估计,在可预见的未来,仅这最后一件事每月可能会节省一两个人日。

On the minus side, the whole brouhaha costed the company about 200,000 up front - plus face, plus undoubtedly some bargaining power (and, hence, yet more money).

不利的一面是,整个吵架让公司预先损失了大约 200,000 美元——加上面子,加上毫无疑问的一些议价能力(因此,还有更多的钱)。

The guy responsible for the "optimization" had changed job a year ago, before the disaster, but still there was talk to sue him for damages. And it didn't go well with the upper echelons that it was "the last guy's fault" - it looked like a set-up for us to come up clean of the matter, and in the end, we're still in the doghouse and one of the team is planning to quit.

负责“优化”的那个人在一年前,也就是灾难发生之前换了工作,但仍然有人在谈论要起诉他赔偿损失。上层认为这是“最后一个人的错”——这似乎是一个让我们解决问题的安排,但最终,我们仍然在狗窝里并且其中一名团队计划退出。

Ninety-nine times out of one hundred, the "86400 hack" will work flawlessly. (For example in PHP, strtotime()will ignore DST and report that between the midnights of the last Saturday of October and that of the following Monday, exactly 2 * 24 * 60 * 60 seconds have passed, even if that is plainly not true... and two wrongs will happily make one right).

100 次中有 99 次,“86400 hack”将完美运行。(例如在 PHP 中,strtotime()将忽略 DST 并报告在 10 月的最后一个星期六的午夜和下一个星期一的午夜之间,正好过去了 2 * 24 * 60 * 60 秒,即使这显然不是真的......两个错误会很高兴地使一个正确)。

This, ladies and gentlemen, was oneinstance when it did not. As with air-bags and seat belts, you will perhaps never reallyneed the complexity (and ease of use) of DateTimeor Carbon. But the day when you might(or the day when you'll have to proveyou thought about this) will come as a thief in the night. Be prepared.

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

回答by Balaji D

Easy to using date_diff

易于使用 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');

回答by user2182143

Object oriented style:

面向对象风格:

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

Procedural style:

程序风格:

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

回答by PHP Ferrari

Used this :)

用过这个:)

$days = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24);
print $days;

Now it works

现在它起作用了

回答by Miguel Costa

Well, the selected answer is not the most correct one because it will fail outside UTC. Depending on the timezone (list) there could be time adjustments creating days "without" 24 hours, and this will make the calculation (60*60*24) fail.

好吧,所选答案不是最正确的答案,因为它会在 UTC 之外失败。根据时区 ( list) 可能会有时间调整,创建“没有”24 小时的日子,这将使计算 (60*60*24) 失败。

Here it is an example of it:

这是一个例子:

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**

So the correct solution will be using DateTime

所以正确的解决方案是使用 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**

回答by SM Imtiaz Hussain

Calculate the difference between two dates:

计算两个日期之间的差异:

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

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

echo $diff->format("%R%a days");

Output: +272 days

产出:+272 天

The date_diff() function returns the difference between two DateTime objects.

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