查找两个date之间的天数
如何find使用PHP的两个date之间的天数?
$now = time(); // or your date as well $your_date = strtotime("2010-01-01"); $datediff = $now - $your_date; echo floor($datediff / (60 * 60 * 24));
如果您使用PHP 5.3 >
,这是计算差异的最准确的方法:
$date1 = new DateTime("2010-07-06"); $date2 = new DateTime("2010-07-09"); $diff = $date2->diff($date1)->format("%a");
将您的date转换为unix时间戳,然后从另一个中减去一个。 这会给你几秒钟的差异,你除以86400(一天中的秒数)给你在该范围内的大致天数。
如果您的date格式为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);
使用ceil
将达到下一整天的天数。 如果您想要获得这两个date之间的完整天数,请使用floor
。
如果您的date已经是unix时间戳格式,那么您可以跳过转换,只需执行$days_between
部分即可。 对于更奇特的date格式,你可能需要做一些自定义的分析才能正确的。
从5.3版本开始, 新的date/时间函数被添加到了不同的地方:
$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);
结果如下:
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 )
希望它有帮助!
TL; DR 不使用UNIX时间戳。 不要使用time()
。 如果你这样做, 准备好 98.0825%的可靠性会让你失望。
大多数使用UNIX时间戳的答案都会产生两个假设,这些假设放在一起会导致出现错误结果的情况和可能难以跟踪的细微错误 ,甚至在成功部署后甚至可能出现几天,几周或几个月。
第一个错误是没有考虑到当被问及“从昨天过去多less天”时,如果现在和“昨天”所表示的时间之间不满一整天 ,计算机可能如实回答零 。
通常,在将“day”转换为UNIX时间戳时,获得的是该特定日子的午夜的时间戳。
于是在十月一日和十月十五日的中午之间,已经过去了十五天。 但是10月1日13:00到10月15日14时55 分之间,已经过去了十五天零五分钟 ,大多数使用floor()
或者隐式整数转换的方法都会报告一天的数据 。
那么,“多less天前YMD H:我:S”呢? 会产生错误的答案 。
第二个错误是等同于一天86400秒。 这几乎总是正确的 – 经常发生的事情往往忽略了它没有的时间。 但夏令时开始时,两个连续的午夜之间的距离(秒) 肯定不是86400,至less是每年两次。 比较DST边界上的两个date将再次产生错误的答案。
所以,即使你使用强制所有date时间戳的“黑客”,例如午夜(当你只指定日/月/年而不是每小时/分钟时,这也是由各种语言和框架隐含的) ,广泛使用的公式
(unix_timestamp(DATE2) - unix_timestamp(DATE1)) / 86400
或时间(time() – strtotime($ somedate))/ 86400
将在DATE1和DATE2处于同一DST段中时返回,例如17; 但可能会回到17.042,更糟糕的是16.958。 使用floor()或任何隐式截断整数将然后转换本应该是17到16。
而且事情变得更加丑陋,因为这样的代码不能跨平台移植 ,因为其中一些可能会应用闰秒,有些可能不适用 。 在那些平台上,两个date之间的差异不会是86400,而是86401,也可能是86399.那么在5月份工作并且通过所有testing的代码将在明年6月中断,12.99999天被认为是12天而不是13天。两个date在2015年的工作将不会在2017年 – 相同的date,而不是一年闰年。 但在2018-01-01和2017-01-01之间,在那些关心的平台上,366天将通过,而不是365,使2017年闰年。
所以如果你真的想使用UNIX时间戳:
-
明智地使用
round()
函数,而不是floor()
。 -
作为select,不要计算D1-M1-YYY1和D2-M2-YYY2之间的差异。 这些date将被认为是D1-M1-YYY1 00:00:00和D2-M2-YYY2 00:00:00。 而是在D1-M1-YYY1 22:30:00和D2-M2-YYY2 04:30:00之间进行转换。 你总是会得到约二十小时的剩余时间。 这可能会变成二十一小时或十九个,也许十八个小时五十九分钟三十六秒。 不pipe。 这是一个很大的利润 ,将留在那里,在可预见的未来保持积极。 现在你可以用
floor()
安全floor()
截断它。
正确的解决办法是
-
使用时间库(date时间,碳,无论); 不要推出自己的
-
使用真正的恶意dateselect编写全面的testing用例 – 跨DST边界,闰年,闰秒等等,以及常见的date。 理想情况下(呼叫date时间很快 !)从string中依次组装起来,生成四年(一天)的date,并确保第一天和第一天之间的差异稳步增加。 这将确保如果低级例程中的任何变化和闰秒修复尝试造成破坏,至less你会知道 。
-
定期与testing套件的其余部分一起运行这些testing。 他们是毫秒的问题,并且可以节省你几个小时的头部划伤。
无论您的解决scheme,testing它
下面的函数funcdiff
在现实世界的场景中实现了其中一种解决scheme(正如它所接受的那样)。
<?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 November..."); $last = array(); for ($year = $yearFrom; $year < $yearTo; $year++) { $dom[2] = 28; 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";
结果是,
This function failed 280 of its 14603 tests
恐怖故事:“节省时间”的代价
这实际上是在几个月前发生的。 一个巧妙的程序员决定通过在几个地方插入臭名昭着的“(MidnightOfDateB-MidnightOfDateA)/ 86400”代码来节省几微秒的时间,最多花费大约三十秒。 这是非常明显的优化,他甚至没有logging它,优化通过了集成testing,潜伏在代码几个月,都没有被注意到。
这发生在一个计算几个最畅销推销员工资的程序中,其中至less有一个比一个整个五人小组的程序员团队更有影响力。 几个月前的一天,由于原因很less,这个问题就发生了 – 其中一些人的一整天发了胖的佣金。 他们绝对不会感到好笑。
无限糟糕的是,他们失去了这个程序的信心(这个信心已经很小),并没有被devise成窃取他们的信息, 而是假装 – 并且获得了 – 一个完整的,详细的代码审查,testing用例以外行人的方式运行和评论在接下来的几个星期内,红地毯治疗)。
我能说些什么呢?从好的方面来说,我们摆脱了很多技术债务,并且能够重写和重构几块在90年代摇摆不定的面条混乱的意大利面条。 现在程序无疑运行得更好了,当有什么东西看起来很腥的时候,还有更多的debugging信息可以快速调入。 我估计只要这最后一件事情在可预见的将来每月可能会节省一两个人日。
不利的一面是,整个公司大约花费20万欧元,加上面子,毫无疑问还有一些讨价还价的能力(因此还有更多的钱)。
负责“优化”的那个人在一年前改变了工作,在灾难发生之前,还是有人要求他赔偿损失。 对于上层的人来说,这是“最后一个人的错”,这看起来像是一个让我们清理干净的设备,最后我们仍然在狗窝里而其中一个队正在计划退出。
百分之九十九,“86400黑客”将完美地工作。 (例如在PHP中, strtotime()
将忽略DST,并在十月的最后一个星期六的中午和下一个星期一的strtotime()
之间报告2 * 24 * 60 * 60秒,尽pipe这显然不是真的 …两个错误会愉快地使一个正确的)。
这一点,女士们先生们,当然没有。 就像安全气囊和安全带一样,您可能永远都不需要DateTime
或Carbon
的复杂性(以及易用性)。 但是,当你可能 (或者你必须certificate你有这个想法的那一天)的那一天,会成为黑夜中的一个小偷。 做好准备。
易于使用date_diff
$from=date_create(date('Ym-d')); $to=date_create("2013-03-15"); $diff=date_diff($to,$from); print_r($diff); echo $diff->format('%R%a days');
- 更多信息,请访问: http : //blog.innovsystems.com/php/get-number-days-between-two-dates-php
面向对象的风格:
$datetime1 = new DateTime('2009-10-11'); $datetime2 = new DateTime('2009-10-13'); $interval = $datetime1->diff($datetime2); echo $interval->format('%R%a days');
程序风格:
$datetime1 = date_create('2009-10-11'); $datetime2 = date_create('2009-10-13'); $interval = date_diff($datetime1, $datetime2); echo $interval->format('%R%a days');
那么,select的答案是不是最正确的,因为它会在UTC以外失败。 根据时区( 列表 ),可能会有时间调整创build“无”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**
所以正确的解决scheme将使用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**
用这个:)
$days = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24); print $days;
现在起作用了
$start = '2013-09-08'; $end = '2013-09-15'; $diff = (strtotime($end)- strtotime($start))/24/3600; echo $diff;
我在我的composer php项目中使用碳作为这个和类似的目的。
这将是如此简单:
$dt = Carbon::parse('2010-01-01'); echo $dt->diffInDays(Carbon::now());
如果你有几秒钟的时间(IE的unix时间戳),那么你可以简单地减去时间和除以86400(每天秒)
$datediff = floor(strtotime($date1)/(60*60*24)) - floor(strtotime($date2)/(60*60*24));
如果需要的话:
$datediff=abs($datediff);
function howManyDays($startDate,$endDate) { $date1 = strtotime($startDate." 0:00:00"); $date2 = strtotime($endDate." 23:59:59"); $res = (int)(($date2-$date1)/86400); return $res; }
如果你想在开始和结束date之间回应所有的日子,我想出了这个:
$startdatum = $_POST['start']; // starting date $einddatum = $_POST['eind']; // end date $now = strtotime($startdatum); $your_date = strtotime($einddatum); $datediff = $your_date - $now; $number = floor($datediff/(60*60*24)); for($i=0;$i <= $number; $i++) { echo date('dmY' ,strtotime("+".$i." day"))."<br>"; }
// Change this to the day in the future $day = 15; // Change this to the month in the future $month = 11; // Change this to the year in the future $year = 2012; // $days is the number of days between now and the date in the future $days = (int)((mktime (0,0,0,$month,$day,$year) - time(void))/86400); echo "There are $days days until $day/$month/$year";
如果你正在使用MySql
function daysSince($date, $date2){ $q = "SELECT DATEDIFF('$date','$date2') AS days;"; $result = execQ($q); $row = mysql_fetch_array($result,MYSQL_BOTH); return ($row[0]);
}
function execQ($q){ $result = mysql_query( $q); if(!$result){echo ('Database error execQ' . mysql_error());echo $q;} return $result;
}
这是我的改进版本,显示1年2个月25天(s)如果第二个参数通过。
class App_Sandbox_String_Util { /** * Usage: App_Sandbox_String_Util::getDateDiff(); * @param int $your_date timestamp * @param bool $hr human readable. eg 1 year(s) 2 day(s) * @see http://stackoverflow.com/questions/2040560/finding-the-number-of-days-between-two-dates * @see http://qSandbox.com */ static public function getDateDiff($your_date, $hr = 0) { $now = time(); // or your date as well $datediff = $now - $your_date; $days = floor( $datediff / ( 3600 * 24 ) ); $label = ''; if ($hr) { if ($days >= 365) { // over a year $years = floor($days / 365); $label .= $years . ' Year(s)'; $days -= 365 * $years; } if ($days) { $months = floor( $days / 30 ); $label .= ' ' . $months . ' Month(s)'; $days -= 30 * $months; } if ($days) { $label .= ' ' . $days . ' day(s)'; } } else { $label = $days; } return $label; } }
尝试使用碳
$d1 = \Carbon\Carbon::now()->subDays(92); $d2 = \Carbon\Carbon::now()->subDays(10); $days_btw = $d1->diffInDays($d2);
也可以使用
\Carbon\Carbon::parse('')
使用给定的时间戳string创build碳date的对象。
$early_start_date = date2sql($_POST['early_leave_date']); $date = new DateTime($early_start_date); $date->modify('+1 day'); $date_a = new DateTime($early_start_date . ' ' . $_POST['start_hr'] . ':' . $_POST['start_mm']); $date_b = new DateTime($date->format('Ym-d') . ' ' . $_POST['end_hr'] . ':' . $_POST['end_mm']); $interval = date_diff($date_a, $date_b); $time = $interval->format('%h:%i'); $parsed = date_parse($time); $seconds = $parsed['hour'] * 3600 + $parsed['minute'] * 60; // display_error($seconds); $second3 = $employee_information['shift'] * 60 * 60; if ($second3 < $seconds) display_error(_('Leave time can not be greater than shift time.Please try again........')); set_focus('start_hr'); set_focus('end_hr'); return FALSE; }
这工作!
$start = strtotime('2010-01-25'); $end = strtotime('2010-02-20'); $days_between = ceil(abs($end - $start) / 86400);