Good day! I am writing a function that defines the time since the comment was posted, but there is a snag. in our time, in contrast to the American, where there is a single and plural number such as year and years, we get even with seconds the parking, example 1 second (back) 2 seconds, 5 seconds, and so on, or for example 1 month, 2 months , 5 months, or 1 year ago, 2 years, 5 years, etc.

I sat down to write and that's how not kosher it turns out:

function timeAgo($time_ago) { $cur_time = time(); $time_elapsed = $cur_time - $time_ago; $seconds = $time_elapsed ; $minutes = round($time_elapsed / 60 ); $hours = round($time_elapsed / 3600); $days = round($time_elapsed / 86400 ); $weeks = round($time_elapsed / 604800); $months = round($time_elapsed / 2600640 ); $years = round($time_elapsed / 31207680 ); $msg = null; // Seconds if($seconds <= 60){ if ($seconds == 1) $msg = "1 секунду"; else if ( $seconds < 5 ) $msg = "$seconds секунды"; else if ( $seconds <= 20 ) $msg = "$seconds секунд"; else if ( $seconds == 21 ) $msg = "21 секунду"; else if ( $seconds <= 24 ) $msg = "$seconds секунды"; else if ( $seconds <= 29 ) $msg = "$seconds секунд"; else if ( $seconds == 30 ) $msg = "30 секунд"; ... } ... return msg; } 

In general, dear progers, there may be a ready-made solution, or you can somehow simplify this matter, otherwise you can describe it for very long in all cases, and there is no readability ...

I would appreciate any suggestions on this topic.

3 answers 3

It is necessary to properly distribute 3 forms of words:

  • everything that ends in 1 - "second"
  • all that ends in 2 - 4 - "seconds"
  • everything else is "seconds"
  • in addition - all numbers from 5 to 20 - "seconds"

Here is a generic PHP function:

 function diff_time_string($start_date, $end_date = NULL, $words = NULL) { // Если конечная дата периода не указана, используется текущая дата и время if (!$end_date) { $end_date = time(); } // Unix-даты переводятся в текстовый формат if (is_numeric($start_date)) { $start_date = date('Ymd H:i:s', $start_date); } if (is_numeric($end_date)) { $end_date = date('Ymd H:i:s', $end_date); } // Через аргумент можно передать массив слов на другом языке if (!$words) { $words = [ 'y' => ['год', 'года', 'лет'], 'm' => ['месяц', 'месяца', 'месяцев'], 'd' => ['день', 'дня', 'дней'], 'h' => ['час', 'часа', 'часов'], 'i' => ['минута', 'минуты', 'минут'], 's' => ['секунда', 'секунды', 'секунд'], ]; } // Разница формируется в виде объекта класса DateInterval $interval = date_diff(date_create($start_date), date_create($end_date)); if (is_object($interval)) { $string = []; foreach ($words as $type => $variants) { // Нулевые значения не добавляются, если только они не идут после ненулевых if ($interval->$type > 0 || count($string)) { $number = $interval->$type; $word = $variants[2]; if ($number < 5 || $number > 20) { $number %= 10; if ($number == 1) { $word = $variants[0]; } elseif ($number >= 2 && $number <= 4) { $word = $variants[1]; } } $string[] = $interval->$type.' '.$word; } } return implode(' ', $string); } return FALSE; } 

Using

 echo diff_time_string(646834565); echo '<br>'; echo diff_time_string('2004-12-08 14:23:39'); echo '<br>'; echo diff_time_string('2016-01-30 18:45:11'); echo '<br>'; echo diff_time_string('2016-05-03 12:19:38'); 

Result

 25 лет 10 месяцев 2 дня 3 часа 59 минут 2 секунды 11 лет 4 месяца 25 дней 4 часа 51 минута 28 секунд 3 месяца 3 дня 0 часов 29 минут 56 секунд 6 часов 55 минут 29 секунд 
  • great solution :) - Vitaly Protasov
  • And how can you use the interval format here to format the output, for example, if the difference is less than an hour, shows only seconds, if the clock went, it shows how many hours and minutes, if the days are gone, then only days, if the month is only they, well, years only years ...? - Vitaly Protasov
  • I still can not find such a member in the date interval of the class $ interval -> $ type. Maybe you should somehow look at it? - Vitaly Protasov
  • one
    In $ interval -> $ type, instead of $ type, the keys of the $ words array are substituted in turn, that is, I check the properties of $ interval-> y, $ interval-> m, etc. For formatting, add after $ string [] = $ interval -> $ type. ' '. $ word; such a condition: if (count ($ string) == 2 || $ type! = 'h') {break; } - Darevill
  • Thank you, a very elegant solution came out! - Vitaly Protasov

On C, you can create an array of pointers to strings and return a pointer to a string.

 char sec_0[] = "секунд"; char sec_1[] = "секунда"; char sec_2[] = "секунду"; ...... char * str_sec[60] = {sec_0,sec_2, .....} ; char * get_str_sec(int sec) { return str_sec[sec]; } 

    We take the remainder of the division by 10 (for large numbers) or the number itself (if it is from 0 to 9) and compare it according to the rule:

    • 0, 5-9 - д
    • 1 - ду ; (21, 31, 41 the same rule works)
    • from 2 to 4 - ды , (from 22 to 24 and so on)

    With minutes and hours the same rule, only the endings are different.


    Not perfect, but like a raw demo example in php:

     $time = ['zeroOrMany' => ['sec' => 'секунд', 'min' => 'минут', 'hour' => 'часоов' ], 'one' => ['sec' => 'секунду', 'min' => 'минуту', 'hour' => 'час' ], 'few' => ['sec' => 'секунды', 'min' => 'минуты', 'hour' => 'часов' ], ]; function getSuffix($num, $type) { global $time; if ($num > 9) { $num = $num % 10; } if ($num == 0 || in_array($num, [5,6,7,8,9])) { return $time['zeroOrMany'][$type]; } else if ($num == 1) { return $time['one'][$type]; } else if (in_array($num, [2,3,4])) { return $time['few'][$type]; } } $num = 666; echo $num.' '.getSuffix($num, 'min'); 

    https://ideone.com/ikAedV

    global just an example ... do not use global variables