我有两个表格日期:
Start Date: 2007-03-24
End Date: 2009-06-26
现在我需要通过以下形式找到这两者之间的区别:
2 years, 3 months and 2 days
如何在PHP中执行此操作?
我有两个表格日期:
Start Date: 2007-03-24
End Date: 2009-06-26
现在我需要通过以下形式找到这两者之间的区别:
2 years, 3 months and 2 days
如何在PHP中执行此操作?
当前回答
这将尝试检测是否给定了时间戳,并将返回未来的日期/时间作为负值:
<?php
function time_diff($start, $end = NULL, $convert_to_timestamp = FALSE) {
// If $convert_to_timestamp is not explicitly set to TRUE,
// check to see if it was accidental:
if ($convert_to_timestamp || !is_numeric($start)) {
// If $convert_to_timestamp is TRUE, convert to timestamp:
$timestamp_start = strtotime($start);
}
else {
// Otherwise, leave it as a timestamp:
$timestamp_start = $start;
}
// Same as above, but make sure $end has actually been overridden with a non-null,
// non-empty, non-numeric value:
if (!is_null($end) && (!empty($end) && !is_numeric($end))) {
$timestamp_end = strtotime($end);
}
else {
// If $end is NULL or empty and non-numeric value, assume the end time desired
// is the current time (useful for age, etc):
$timestamp_end = time();
}
// Regardless, set the start and end times to an integer:
$start_time = (int) $timestamp_start;
$end_time = (int) $timestamp_end;
// Assign these values as the params for $then and $now:
$start_time_var = 'start_time';
$end_time_var = 'end_time';
// Use this to determine if the output is positive (time passed) or negative (future):
$pos_neg = 1;
// If the end time is at a later time than the start time, do the opposite:
if ($end_time <= $start_time) {
$start_time_var = 'end_time';
$end_time_var = 'start_time';
$pos_neg = -1;
}
// Convert everything to the proper format, and do some math:
$then = new DateTime(date('Y-m-d H:i:s', $$start_time_var));
$now = new DateTime(date('Y-m-d H:i:s', $$end_time_var));
$years_then = $then->format('Y');
$years_now = $now->format('Y');
$years = $years_now - $years_then;
$months_then = $then->format('m');
$months_now = $now->format('m');
$months = $months_now - $months_then;
$days_then = $then->format('d');
$days_now = $now->format('d');
$days = $days_now - $days_then;
$hours_then = $then->format('H');
$hours_now = $now->format('H');
$hours = $hours_now - $hours_then;
$minutes_then = $then->format('i');
$minutes_now = $now->format('i');
$minutes = $minutes_now - $minutes_then;
$seconds_then = $then->format('s');
$seconds_now = $now->format('s');
$seconds = $seconds_now - $seconds_then;
if ($seconds < 0) {
$minutes -= 1;
$seconds += 60;
}
if ($minutes < 0) {
$hours -= 1;
$minutes += 60;
}
if ($hours < 0) {
$days -= 1;
$hours += 24;
}
$months_last = $months_now - 1;
if ($months_now == 1) {
$years_now -= 1;
$months_last = 12;
}
// "Thirty days hath September, April, June, and November" ;)
if ($months_last == 9 || $months_last == 4 || $months_last == 6 || $months_last == 11) {
$days_last_month = 30;
}
else if ($months_last == 2) {
// Factor in leap years:
if (($years_now % 4) == 0) {
$days_last_month = 29;
}
else {
$days_last_month = 28;
}
}
else {
$days_last_month = 31;
}
if ($days < 0) {
$months -= 1;
$days += $days_last_month;
}
if ($months < 0) {
$years -= 1;
$months += 12;
}
// Finally, multiply each value by either 1 (in which case it will stay the same),
// or by -1 (in which case it will become negative, for future dates).
// Note: 0 * 1 == 0 * -1 == 0
$out = new stdClass;
$out->years = (int) $years * $pos_neg;
$out->months = (int) $months * $pos_neg;
$out->days = (int) $days * $pos_neg;
$out->hours = (int) $hours * $pos_neg;
$out->minutes = (int) $minutes * $pos_neg;
$out->seconds = (int) $seconds * $pos_neg;
return $out;
}
示例用法:
<?php
$birthday = 'June 2, 1971';
$check_age_for_this_date = 'June 3, 1999 8:53pm';
$age = time_diff($birthday, $check_age_for_this_date)->years;
print $age;// 28
Or:
<?php
$christmas_2020 = 'December 25, 2020';
$countdown = time_diff($christmas_2020);
print_r($countdown);
其他回答
一便士一英镑:我刚刚回顾了几个解决方案,所有这些方案都使用floor()提供了一个复杂的解决方案,然后四舍五入到26年12个月零2天的解决方案中,原本应该是25年11个月零20天!!!!
这是我对这个问题的看法:可能不优雅,可能编码不好,但如果不计算LEAP年份,则提供了更接近答案的答案,显然闰年可以编码为,但在这种情况下-正如其他人所说,也许您可以提供以下答案:我已经包含了所有测试条件和print_r,以便您可以更清楚地看到结果的构造:在这里,
//设置输入日期/变量::
$ISOstartDate = "1987-06-22";
$ISOtodaysDate = "2013-06-22";
//我们需要将ISO yyyy-mm-dd格式分解为yyyy-mm-d格式,如下所示:
$yDate[]=爆炸('-',$ISOstartDate);print_r($yDate);
$zDate[]=爆炸('-',$ISOtodaysDate);print_r($zDate);
// Lets Sort of the Years!
// Lets Sort out the difference in YEARS between startDate and todaysDate ::
$years = $zDate[0][0] - $yDate[0][0];
// We need to collaborate if the month = month = 0, is before or after the Years Anniversary ie 11 months 22 days or 0 months 10 days...
if ($months == 0 and $zDate[0][1] > $ydate[0][1]) {
$years = $years -1;
}
// TEST result
echo "\nCurrent years => ".$years;
// Lets Sort out the difference in MONTHS between startDate and todaysDate ::
$months = $zDate[0][1] - $yDate[0][1];
// TEST result
echo "\nCurrent months => ".$months;
// Now how many DAYS has there been - this assumes that there is NO LEAP years, so the calculation is APPROXIMATE not 100%
// Lets cross reference the startDates Month = how many days are there in each month IF m-m = 0 which is a years anniversary
// We will use a switch to check the number of days between each month so we can calculate days before and after the years anniversary
switch ($yDate[0][1]){
case 01: $monthDays = '31'; break; // Jan
case 02: $monthDays = '28'; break; // Feb
case 03: $monthDays = '31'; break; // Mar
case 04: $monthDays = '30'; break; // Apr
case 05: $monthDays = '31'; break; // May
case 06: $monthDays = '30'; break; // Jun
case 07: $monthDays = '31'; break; // Jul
case 08: $monthDays = '31'; break; // Aug
case 09: $monthDays = '30'; break; // Sept
case 10: $monthDays = '31'; break; // Oct
case 11: $monthDays = '30'; break; // Nov
case 12: $monthDays = '31'; break; // Dec
};
// TEST return
echo "\nDays in start month ".$yDate[0][1]." => ".$monthDays;
// Lets correct the problem with 0 Months - is it 11 months + days, or 0 months +days???
$days = $zDate[0][2] - $yDate[0][2] +$monthDays;
echo "\nCurrent days => ".$days."\n";
// Lets now Correct the months to being either 11 or 0 Months, depending upon being + or - the years Anniversary date
// At the same time build in error correction for Anniversary dates not being 1yr 0m 31d... see if ($days == $monthDays )
if($days < $monthDays && $months == 0)
{
$months = 11; // If Before the years anniversary date
}
else {
$months = 0; // If After the years anniversary date
$years = $years+1; // Add +1 to year
$days = $days-$monthDays; // Need to correct days to how many days after anniversary date
};
// Day correction for Anniversary dates
if ($days == $monthDays ) // if todays date = the Anniversary DATE! set days to ZERO
{
$days = 0; // days set toZERO so 1 years 0 months 0 days
};
echo "\nTherefore, the number of years/ months/ days/ \nbetween start and todays date::\n\n";
printf("%d years, %d months, %d days\n", $years, $months, $days);
最终结果是:26年零个月零天
这就是我在2013年6月22日做生意的时间——哎呦!
DateInterval很好,但它有几个注意事项:
仅适用于PHP 5.3+(但这真的不再是一个好借口)仅支持年、月、日、小时、分钟和秒(无周)它计算上述所有+天的差异(你不能只计算月的差异)
为了克服这个问题,我编写了以下代码(由@enobrev答案改进而来):
function date_dif($since, $until, $keys = 'year|month|week|day|hour|minute|second')
{
$date = array_map('strtotime', array($since, $until));
if ((count($date = array_filter($date, 'is_int')) == 2) && (sort($date) === true))
{
$result = array_fill_keys(explode('|', $keys), 0);
foreach (preg_grep('~^(?:year|month)~i', $result) as $key => $value)
{
while ($date[1] >= strtotime(sprintf('+%u %s', $value + 1, $key), $date[0]))
{
++$value;
}
$date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]);
}
foreach (preg_grep('~^(?:year|month)~i', $result, PREG_GREP_INVERT) as $key => $value)
{
if (($value = intval(abs($date[0] - $date[1]) / strtotime(sprintf('%u %s', 1, $key), 0))) > 0)
{
$date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]);
}
}
return $result;
}
return false;
}
它运行两个循环;第一个算法通过暴力强制处理相对间隔(年和月),第二个算法通过简单的算法计算额外的绝对间隔(因此速度更快):
echo humanize(date_dif('2007-03-24', '2009-07-31', 'second')); // 74300400 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'minute|second')); // 1238400 minutes, 0 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'hour|minute|second')); // 20640 hours, 0 minutes, 0 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|day')); // 2 years, 129 days
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week')); // 2 years, 18 weeks
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week|day')); // 2 years, 18 weeks, 3 days
echo humanize(date_dif('2007-03-24', '2009-07-31')); // 2 years, 4 months, 1 week, 0 days, 0 hours, 0 minutes, 0 seconds
function humanize($array)
{
$result = array();
foreach ($array as $key => $value)
{
$result[$key] = $value . ' ' . $key;
if ($value != 1)
{
$result[$key] .= 's';
}
}
return implode(', ', $result);
}
对于php版本>=5.3:创建两个日期对象,然后使用date_diff()函数。它将返回phpDateInterval对象。请参阅文档
$date1=date_create("2007-03-24");
$date2=date_create("2009-06-26");
$diff=date_diff($date1,$date2);
echo $diff->format("%R%a days");
我不知道你是否在使用PHP框架,但很多PHP框架都有日期/时间库和助手来帮助你避免重新发明轮子。
例如,CodeIgniter具有timespan()函数。只需输入两个Unix时间戳,就会自动生成如下结果:
1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes
http://codeigniter.com/user_guide/helpers/date_helper.html
我想带来一个稍微不同的视角,这似乎没有被提及。
你可以用声明的方式解决这个问题(就像任何其他问题一样)。重点是问你需要什么,而不是如何到达那里。
在这里,你需要与众不同。但这有什么不同?这是一个间隔,正如在最受欢迎的答案中所提到的。问题是如何获取它。您可以不显式调用diff()方法,而是按开始日期和结束日期创建一个间隔,即按日期范围:
$startDate = '2007-03-24';
$endDate = '2009-06-26';
$range = new FromRange(new ISO8601DateTime($startDate), new ISO8601DateTime($endDate));
所有诸如闰年之类的复杂问题都已经解决了。现在,当您有一个固定开始日期时间的间隔时,您可以获得一个人类可读的版本:
var_dump((new HumanReadable($range))->value());
它输出的正是你所需要的。
如果您需要一些自定义格式,这也不是问题。您可以使用ISO8601格式化类,该类接受具有六个参数的调用:年、月、日、小时、分钟和秒:
(new ISO8601Formatted(
new FromRange(
new ISO8601DateTime('2017-07-03T14:27:39+00:00'),
new ISO8601DateTime('2018-07-05T14:27:39.235487+00:00')
),
function (int $years, int $months, int $days, int $hours, int $minutes, int $seconds) {
return $years >= 1 ? 'More than a year' : 'Less than a year';
}
))
->value();
它的产量超过一年。
有关此方法的更多信息,请查看快速入门条目。