这是我到目前为止得到的:
/**
* Parse a duration between 2 date/times in seconds
* and to convert that duration into a formatted string
*
* @param integer $time_start start time in seconds
* @param integer $time_end end time in seconds
* @param string $format like the php strftime formatting uses %y %m %w %d %h or %i.
* @param boolean $chop chop off sections that have 0 values
*/
public static function FormatDateDiff($time_start = 0, $time_end = 0, $format = "%s", $chop = false) {
if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);
list($year_start,$month_start,$day_start) = explode('-',date('Y-m-d',$time_start));
list($year_end,$month_end,$day_end) = explode('-',date('Y-m-d',$time_end));
$years = $year_end - $year_start;
$months = $month_end - $month_start;
$days = $day_start - $day_end;
$weeks = 0;
$hours = 0;
$mins = 0;
$secs = 0;
if(mktime(0,0,0,$month_end,$day_end) < mktime(0,0,0,$month_start,$day_start)) {
$years -= 1;
}
if($days < 0) {
$months -= 1;
$days += 30; // this is an approximation...not sure how to figure this out
}
if($months < 0) $months += 12;
if(strpos($format, '%y')===false) {
$months += $years * 12;
}
if(strpos($format, '%w')!==false) {
$weeks = floor($days/7);
$days %= 7;
}
echo date('Y-m-d',$time_start).' to '.date('Y-m-d',$time_end).": {$years}y {$months}m {$weeks}w {$days}d<br/>";
}
(不完整且不准确(
我似乎无法正确计算数学。天真地将其划分是行不通的,因为闰年和不同的月份长度。
逻辑也需要根据格式字符串进行更改。例如,使用格式字符串%y year %m month %d day
将 04-Feb-2010 传递到 28-Jun-2011(作为 unix 时间戳(应该输出1 year 4 month 24 day
但如果省略%y year
,则需要在月份中添加 12 个月,即输出应16 month 24 day
。
也应该处理时间...但我还没有到那个地步。
这些date_diff解决方案都无法处理数周。而且我不知道我怎么能把它破解到date_diff
,所以这对我来说并不是一个真正的解决方案。
此外,$diff->format
没有按照我的要求去做...如果省略"更大的单位",则给出总月和日。例:
>>> $start = new DateTime('04-Feb-2010')
>>> $end = new DateTime('28-Jun-2011')
>>> $diff = $start->diff($end)
>>> $diff->format('%m months, %d days')
'4 months, 24 days'
应该是16 months, 24 days
,正如我之前所说。在你完全理解之前,请不要再这么快就把我的问题当作骗子来关闭。如果可以调整其他问题的解决方案来解决这个问题,很好,但请解释如何,因为我不明白。
需要明确的是,
- 如果省略
%y
,则应将年份滚动到月份中 - 如果省略
%m
,则应将月份滚动到天数中 - 如果省略
%w
,则应将周滚动到天数中 - 如果省略
%h
,则应将小时数转换为分钟 - 如果省略
%m
,则应将分钟滚动为秒
如果省略"较小的单位",则可以在有意义的地方对下一个最大的单位进行舍入或地板。
我不期望得到一个公认的答案,但这里是让date_diff做几周的方法。
<?php
$january = new DateTime('2010-01-01');
$february = new DateTime('2011-02-20 3:35:28');
$interval = $february->diff($january);
$parts = $interval->format('%y %m %d %h %i %s %a');
$weeks = 0;
list($years, $months, $days, $hours, $minutes, $seconds, $total_days) = explode(' ', $parts);
if ($days >= 7) {
$weeks = (int)($days / 7);
$days %= 7;
}
echo "$years years, $months months, $weeks weeks, $days days, $hours hours, $minutes minutes $seconds seconds";
// 1 years, 1 months, 2 weeks, 5 days, 3 hours, 35 minutes 28 seconds
也许有了它,您可以将其集成到您的函数中,以进行滚动和处理用户给定的格式。
如果未给出较大的单位,则可以从最大的单位开始,然后将其应用回下一个较小的单位。(即 1 年 1 个月没有年份应将 12 加回月(。 如果格式中不包含"月",则可以使用总天数来处理月份具有不同天数的事实。
我会使用DateTime::d iff((来做这件事:
$start = new DateTime('2012-01-01 12:00:00');
$end = new DateTime('2012-01-20 06:59:59');
$diff = $start->diff($end);
echo $diff->format('%d days, %h hours, %m minutes, %s seconds');
请参阅 DateInterval::format(( 了解有关格式的更多信息。
$absolute = false; //default, returns years, months, days, etc. True would return seconds
$difference = date_diff($dateTimeStart, $dateTimeEnd, $absolute);
echo $difference->format("%y Year(s) %m Month(s) ...");
我会先尝试一下,如果它不适合您的目的,那么您可以尝试使用日历函数来获取每个月的正确天数。
我认为您需要决定自己的计算周数的逻辑。就个人而言,我会说$weeks = $difference->d / 7;
但没有严格正确的方法来计算一个月的一部分经过的周数。想想日历,我们经常说第一周、第二周、第三周,但除非这个月从星期日(或星期一,或星期六,取决于公司、宗教等(开始,否则我们实际上在这些描述中并不是绝对的。你可以绝对地说,自月初以来有3个星期天(例如(,而不是三个星期。28日,四周过去了吗?如果这个月从周三开始,那么是五个月呢?
此外,如果需要自定义格式(35 个月(,则始终可以直接访问成员并连接格式字符串。
一个有趣问题的尝试。它不是 100% 工作,但它非常接近。
该函数首先计算日期差异所需的总秒数和月数。然后,使用按升序排序的$tokens
数组,它遍历这些标记并尝试将其与输入$format
匹配。如果找到$format
,则从月数或秒总数中减去适当的值。
月或数年内具有大于零的值,但$format
字符串未指定这些参数中的任何一个。如果是这样,该函数将滚动适当的天数,以便得出正确的计算。
我已经简要测试了它,它适用于此线程中的所有示例。您可以在代码板上测试它,看看是否可以破解它! :)
我会对此进行改进感兴趣。
<?php
function calc( $start, $end, $format = '%s', $chop = false)
{
$tokens = array( '%y', '%m', '%w', '%d', '%h', '%i', '%s');
if( !is_a( $start, 'DateTime') || !is_a( $end, 'DateTime'))
{
return;
}
$diff = $start->diff( $end);
$months = ($diff->y * 12) + $diff->m;
$secs = ($diff->d * 24 * 3600) +
($diff->h * 3600) +
($diff->i * 60) +
($diff->s);
$output = array();
while( $token = array_shift( $tokens))
{
$token_present = !(strpos( $format, $token) === false);
switch( $token)
{
case '%y':
if( ($months / 12) > 0 && $token_present)
{
$output[$token] = floor( $months / 12);
$months -= $output[$token] * 12;
}
break;
case '%m':
if( $months > 0 && $token_present)
{
$output[$token] = $months;
$months = 0;
}
break;
case '%w':
// Rollover between (months or years) and seconds
if( (!isset( $output['%y']) || !isset( $output['%m'])) && $months > 0)
{
$days = $diff->format( '%a');
// Need a fix for leap year probably.
$days -= (isset( $output['%y'])) ? ($output['%y'] * 365) : 0;
$days -= $diff->d;
$secs += ($days * 24 * 60 * 60);
}
$val = (7 * 24 * 60 * 60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%d':
$val = (24 * 60 * 60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%h':
$val = (60 * 60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%i':
$val = (60);
if( ($secs / $val) > 0 && $token_present)
{
$output[$token] = floor( $secs / $val);
$secs -= $output[$token] * $val;
}
break;
case '%s':
if( $secs > 0 && $token_present)
{
$output[$token] = $secs;
}
break;
}
}
// Filter out blank keys and replace their tokens in the $format string
$filtered = $chop ? array_filter( $output) : $output;
$format = str_replace( array_diff( array_keys($output), array_keys($filtered)), '', $format);
return str_replace( array_keys( $filtered), array_values( $filtered), $format);
}
$start = new DateTime('04-Feb-2010');
$end = new DateTime('28-Jun-2011');
echo calc( $start, $end, "%m months %d days'n"); // 16 months 24 days
$january = new DateTime('2010-01-01');
$february = new DateTime('2011-02-20 3:35:28');
echo calc( $january, $february, '%y years %m months %w weeks %d days %h hours %i minutes %s seconds'); // 1 years 1 months 2 weeks 5 days 3 hours 35 minutes 28 seconds
我想我已经明白了:
if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);
$start_dt = new DateTime();
$end_dt = new DateTime();
$start_dt->setTimestamp($time_start);
$end_dt->setTimestamp($time_end);
$has_time = preg_match('`%[his]`',$format) > 0;
if(!$has_time) {
$start_dt->setTime(0,0,0);
$end_dt->setTime(0,0,0);
}
$interval = $end_dt->diff($start_dt);
$parts = $interval->format('%y %m %d %h %i %s %a');
$weeks = 0;
list($years, $months, $days, $hours, $mins, $secs, $total_days) = explode(' ',$parts);
if(strpos($format,'%y')===false) {
$months += $years * 12;
}
if(strpos($format,'%m')===false) {
$days = $total_days;
if(strpos($format,'%y')!==false) {
$start_dt->add(new DateInterval('P'.$years.'Y'));
$interval = $end_dt->diff($start_dt);
$days = $interval->days;
}
}
if(strpos($format,'%w')!==false) {
$weeks = (int)($days/7);
$days %= 7;
}
if(strpos($format,'%d')===false) {
$hours += $days * 24;
}
if(strpos($format,'%h')===false) {
$mins += $hours * 60;
}
if(strpos($format,'%i')===false) {
$secs += $mins * 60;
}
(仅供参考,我只是在最后做一些基本str_replacing,将其放入我的自定义格式字符串中(
编辑:还有1个我不知道如何处理的情况...当请求年和日而不是月时,输出将是错误的。我想知道我是否应该用 365 修改总天数......这是一种罕见的情况。
编辑2:解决了!我们只需将多少年添加到开始日期,然后重新计算总天数。