Разберите и создайте интервалы даты и времени ISO 8601, например PT15M в PHP.
Вопрос
Библиотека и веб-сервис, которые я использую, передают временные интервалы в Формат ISO 8601: PnYnMnDTnHnMnS
.Я хочу конвертировать такие форматы в секунды.И наоборот.С секундами гораздо проще считать.
Примеры значений интервала:
- ПТ1М или ПТ60С (1 минута)
- PT1H, PT60M или PT3600S (1 час)
Мне нужны две функции:проанализировать такие значения до секунд: iso8601_interval_to_seconds()
и от секунд на такие промежутки: iso8601_interval_from_seconds()
.
Последнее довольно просто, потому что это можно сделать как `"PT{$секунды}S", просто пропускать секунды в любое время.Может быть, это можно сделать лучше с помощью анализатора, который переключается на H(час) или M(минута)?
Первый сложнее, но, может быть, есть какой-то трюк с одним из многочисленных преобразователей строк в дату в PHP?Мне бы очень хотелось научиться использовать такую функцию для анализа интервалов.Или изучите альтернативу.
Решение
Похоже на PHP 5.3 ДатаИнтервал поддерживает это.
Если вы не можете использовать 5.3, я полагаю, любая такая функция преобразования будет знать, сколько секунд в году, месяце, дне, часе и минуте.Затем при преобразовании из секунд оно будет делиться в этом порядке, каждый раз принимая по модулю предыдущей операции, пока не останется менее 60 секунд.При преобразовании из интервального представления ISO 8601 должно быть просто проанализировать строку и соответствующим образом умножить каждый найденный элемент.
Другие советы
strtotime не будет работать напрямую с форматом ISO 8601 (например.P1Y1DT1S), но формат, который он понимает (1 YEAR1DAY1SECOND), не слишком далеко-это было бы довольно прямолинейным преобразованием.(маленький "хакерский" ...но это PHP для вас).
Спасибо, Ли, я не знал, что strtotime принимает этот формат.Это была недостающая часть моей головоломки.Возможно, мои функции смогут дополнить ваш ответ.
function parse_duration($iso_duration, $allow_negative = true){
// Parse duration parts
$matches = array();
preg_match('/^(-|)?P([0-9]+Y|)?([0-9]+M|)?([0-9]+D|)?T?([0-9]+H|)?([0-9]+M|)?([0-9]+S|)?$/', $iso_duration, $matches);
if(!empty($matches)){
// Strip all but digits and -
foreach($matches as &$match){
$match = preg_replace('/((?!([0-9]|-)).)*/', '', $match);
}
// Fetch min/plus symbol
$result['symbol'] = ($matches[1] == '-') ? $matches[1] : '+'; // May be needed for actions outside this function.
// Fetch duration parts
$m = ($allow_negative) ? $matches[1] : '';
$result['year'] = intval($m.$matches[2]);
$result['month'] = intval($m.$matches[3]);
$result['day'] = intval($m.$matches[4]);
$result['hour'] = intval($m.$matches[5]);
$result['minute'] = intval($m.$matches[6]);
$result['second'] = intval($m.$matches[7]);
return $result;
}
else{
return false;
}
}
Функция также поддерживает отрицательные форматы.-P10Y9MT7M5S вернет массив типа:[год] => -10 [месяц] => -9 [день] => 0 [час] => 0 [минута] => -7 [секунда] => -5 Если такое поведение нежелательно, передайте false в качестве второго параметра.Таким образом, функция всегда будет возвращать положительные значения. Символ мин/плюс по-прежнему будет доступен в ключе результата ['symbol'].
И небольшое обновление:Эта функция использует первую функцию для получения общего количества секунд.
function get_duration_seconds($iso_duration){
// Get duration parts
$duration = parse_duration($iso_duration, false);
if($duration){
extract($duration);
$dparam = $symbol; // plus/min symbol
$dparam .= (!empty($year)) ? $year . 'Year' : '';
$dparam .= (!empty($month)) ? $month . 'Month' : '';
$dparam .= (!empty($day)) ? $day . 'Day' : '';
$dparam .= (!empty($hour)) ? $hour . 'Hour' : '';
$dparam .= (!empty($minute)) ? $minute . 'Minute' : '';
$dparam .= (!empty($second)) ? $second . 'Second' : '';
$date = '19700101UTC';
return strtotime($date.$dparam) - strtotime($date);
}
else{
// Not a valid iso duration
return false;
}
}
$foo = '-P1DT1S';
echo get_duration_seconds($foo); // Output -86399
$bar = 'P1DT1S';
echo get_duration_seconds($bar); // Output 86401
Имейте в виду, что преобразование длительности, содержащей дни, месяцы и/или годы, в длительность, например секунды, невозможно выполнить точно, не зная фактической начальной даты/времени.
Например
1 SEP 2010: +P1M2D means +2764800 seconds
1 OCT 2010: +P1M2D means +2851200 seconds
Это потому, что в сентябре 30 дней, а в октябре 31 день.Та же проблема возникает при преобразовании годовых интервалов из-за високосных лет и високосных секунд.Високосные годы также усложняют преобразование месяцев - поскольку в високосные годы февраль на один день длиннее, чем в противном случае.Дни являются проблематичными в районах, где практикуется переход на летнее время: однодневный период, возникающий во время перехода на летнее время, фактически на 1 час длиннее, чем в противном случае.
При этом вы, конечно, можете сжимать значения, содержащие только Часы, минуты и секунды в значения, содержащие только секунды.Я бы посоветовал вам создать простой парсер для выполнения этой работы (или, возможно, рассмотреть регулярное выражение).
Просто имейте в виду подводные камни, описанные выше — там есть драконы.Если вы собираетесь иметь дело с днями, месяцами и/или годами, вам необходимо использовать один из встроенных механизмов, чтобы выполнить математические вычисления за вас в контексте известной даты/времени.Как отмечали другие:Класс DateInterval в сочетании с функциями, предоставляемыми классом DateTime, вероятно, является наиболее интуитивно понятным способом справиться с этой проблемой.Но это доступно только в версии PHP 5.3.0 или выше.
Если вам приходится работать с версией ниже 5.3.0, вы можете попытаться построить что-нибудь на основе этой маленькой жемчужины:
$startDateTime = '19700101UTC';
$duration = strtotime( $startDateTime.'+1Year1Day1Second' ) - strtotime($startDateTime);
print("Duration in Seconds: $duration");
strtotime
не будет работать напрямую с форматом ISO 8601 (например. P1Y1DT1S
), но формат, в котором он делает понимать (1Year1Day1Second
) не так уж и далеко — это было бы довольно простое преобразование.(немного "хакерски"...но это PHP для вас).
удачи!
function parsePTTime($pt_time)
{
$string = str_replace('PT', '', $pt_time);
$string = str_replace('H', 'Hour', $string);
$string = str_replace('M', 'Minute', $string);
$string = str_replace('S', 'Second', $string);
$startDateTime = '19700101UTC';
$seconds = strtotime($startDateTime . '+' . $string) - strtotime($startDateTime);
return $seconds;
}
Тесты:
PT1H - OK
PT23M - OK
PT45S - OK
PT1H23M - OK
PT1H45S - OK
PT23M45S - OK
PT1H23M45S - OK
То, что вы ищете, это DateTime::diff
Объект DateInterval, представляющий разницу между двумя датами или FALSE в случае сбоя, как показано ниже:
$datetime1 = new DateTime('2009-10-11');
$datetime2 = new DateTime('2009-10-13');
$interval = $datetime1->diff($datetime2);
echo $interval->format('%R%d days');
Просто используйте секунды вместо дат.
Это из http://www.php.net/manual/en/datetime.diff.php
Ссылку на DateInterval см. http://www.php.net/manual/en/class.dateinterval.php