2012-01-24 37 views
5

這裏是我到目前爲止有:如何計算兩天之間的差異作爲格式化的字符串?

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

(這是不完整的不準確)

我似乎無法得到正確的數學。由於閏年和月份長度不同,天真地劃分出來將不起作用。

邏輯還需要根據格式字符串進行更改。例如,傳遞04-FEB-2010至28君2011(如UNIX時間戳)與格式字符串%y year %m month %d day應該輸出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被忽略,分鐘應該被滾動到秒

如果「更小的單位」被省略,下一個最大的單位可以被舍入或地板有意義的地方。

+1

如果你有PHP 5.3.0或以上,這可以幫助你:[DateInterval ::格式(http://www.php.net/manual/en/dateinterval.format.php) – drew010

+2

重複[this post](http:// stackoverflow。com/questions/676824/how-to-calculate-the-difference-between-dates-using-php) – bowlerae

+0

@ drew010:不......不能處理數週。 – mpen

回答

1

我不指望一個接受的答案,但這裏是如何讓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個月)。如果格式中未包含「月」,則可以使用總天數來處理月份具有不同天數的事實。

+0

不錯!我會試着玩這個,看看我能不能做到我想要的。看起來很有希望。 – mpen

+0

希望你能在某個地方找到它。由於在處理觀測DST邊界,閏年/秒和每個月的不同日期等許多問題時,我總是試圖依靠現在的「內置」日期方法。 – drew010

+0

是的......我以前用'DateTime'擺弄過,但還是有一些問題,所以我說「擰它!我會自己寫的!」。證明這是錯誤的方法。你的很好,乾淨。我會接受:) – mpen

1
$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個月),您可以隨時直接訪問成員並連接格式字符串。

+0

'date_diff'不處理星期。你的意思是計算兩個日期之間的每個月的天數,並計算它們之類的東西?或者僅僅是開始和結束的月份..? – mpen

+1

你說得對,它沒有。對不起,我沒有注意到這個要求。 – Tim

+0

沒關係。這個要求是埋在代碼中的;我應該說得更清楚。但它不會處理「更大的單位」要求。 – mpen

1

我會用DateTime::diff()此:

$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()

+0

這不能處理數週。 – mpen

1

認爲我知道了:

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在結束時把它扔進我的自定義格式字符串)

編輯:還有一個場景我不知道該怎麼處理......當需要幾年和幾天時,輸出結果是錯誤的。我想知道我是否應該用365來修改總日期......這是一種罕見的情況。

編輯2:解決了!我們將在開始日期添加多年,然後重新計算總日期。

+1

好的解決方案馬克,乾淨,易於閱讀。看起來你幾乎在那裏。 – drew010

1

這是我對一個有趣的問題的嘗試。這不是100%的工作,但它非常接近。

該功能從計算日期差異所需的總秒數和月份開始。然後,使用按升序排序的$tokens數組,它遍歷這些令牌並嘗試將其與輸入$format匹配。如果找到$format,則從總月數或秒數中減去適當的值。

但是,有可能在幾個月或幾年中具有大於零的值,但$format字符串未指定這些參數中的任何一個。如果是這樣,該函數將滾動適當的天數以提出正確的計算。

我已經對它進行了簡要測試,它適用於本主題中的所有示例。你可以在codepad上進行測試,看看你是否可以打破它! :)我會對這方面的改進感興趣。

<?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 
+0

我弄壞了它:http://codepad.viper-7.com/lc16ez – mpen

+0

@Mark我發現這個錯誤,因爲我發佈了答案......我認爲這可以修復它:http://codepad.viper-7.com/T7qehe – nickb

+0

是的..這與我現在得到的結果是一樣的,但我認爲你仍然有一些舍入誤差,特別是在閏年。我不喜歡那裏的那個「365」;)我的解決方案目前在這個頁面的底部。 – mpen

相關問題