April 2032
|
|
|
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
|
12/5/07 03:25 am
пыхыпыхаю
Этот простой код на PHP парсит дату в формате W3C (http://www.w3.org/TR/NOTE-datetime)
$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
if ( preg_match( $pat, $date_str, $match ) ) {
list( $year, $month, $day, $hours, $minutes, $seconds) =
array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
}
Что называется, найдите ошибку. Комменты скринятся. Update: таймзона там отдельно ниже обрабатывается, просто я код постить для простоты не стал. Update2: код регекспа тоже верный, во всяком случае, для целей этого вопроса ;) Update3: всё расскринено, результаты тут.
12/4/07, 09:33 pm
Может, я неправильно понял в 5 ночи, но здесь было $match[6] - ':32'
Если так - тогда $pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
12/6/07, 08:16 am
Твой регексп неправильный, потому как требует наличия секунд (а точнее -- наличия : после минут).
Я, кстати, в начале точно так же его поменял, а потом понял, в чём дело. Вместо $match[6] надо брать $match[7], и всё.
12/5/07, 01:57 am
блин. ну нельзя же так, с утра прям... :))
12/5/07, 03:10 am
У меня получилось нечто типа: $pat = "/(\d{4})(-(\d{2}))?(-(\d{2}))?(T(\d{2}))?(:(\d{2}))?(:(\d{2}))?(\.\d{2})?((([-+])(\d{2}):(\d{2}))|(Z))?/"; if ( preg_match( $pat, $data, $match ) ) { list( $year, $month, $day, $hours, $minutes, $seconds) = array( $match[1], $match[3], $match[5], $match[7], $match[9], $match[11]);
Во всяком случае он у меня примеры из спецификации парсит корректно. В оригинале меня очень сильно смущает кучок: (?:([-+])(\d{2}):?(\d{2})|(Z))? Зачем вначале ? и как можно здесь объединять : и [-+] ?
12/6/07, 08:38 am
Да, это существенно более хороший регексп, в том смысле, что парсит все примеры из стандарта.
12/5/07, 03:14 am
$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):((\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/"; ?
12/6/07, 08:18 am
((\d{2})) нафига двойные скобки-то?
12/6/07, 08:24 am
согласна - не нужны стормозила обещаю исправиться
12/5/07, 03:21 am
Не матчит формат без времени (2007-12-04). Секунды матчит с : (надо было 7е скобки брать). Игнорирует timezone.
12/6/07, 08:19 am
таймзона там потом обрабатывается, я просто эту часть кода не привёл.
остальное совершенно верно!
12/5/07, 07:24 am
а ошибка именно одна? ;) сразу видно, что целая часть секунд на самом деле в match[7], а может быть же еще дробная часть. ну и зону выкинули нафиг, а почему? ;)
12/6/07, 08:21 am
Да, оригинально мой вопрос был про $match[7] (а не $match[6]). Но это действительно не единственная ошибка в коде — скорее, это первая очевидная.
Таймзону они там ниже обрабатывают, я просто код постить не сталю
12/5/07, 01:56 pm
- Отвергаются даты, конкретизированные меньше чем до минут.
- Отвергаются даты, конкретизированные до долей секунды.
- Допускаются произвольные месяцы, дни, часы, минуты, секунды и смещения от UTC, а не только реально существующие в природе.
- Допускаются смещения, записанные без двоеточия, в нарушение стандарта (фича для совместимости с ISO 8601? Почему только в смещении?)
- Допускается произвольный мусор до и после даты.
- В поле секунд попадёт также двоеточие-разделитель (видимо, это авторский ответ).
- (стиль) Левую альтернативу смещения от UTC для ясности приоритетов стоило бы заскобочить.
- perldoc perlre и perldoc perlunicode не определяют
\d достаточно чётко, чтобы можно было утверждать, что туда не попадут цифры, отличные от [0-9] . (Я параноик?)

12/6/07, 08:23 am
Это правильный и, пожалуй, наиболее полный ответ. Не хватает только примера правильного, с вашей точки зрения, кода.
12/6/07, 09:25 am
Проблемы конкретизации решаются добавлением скобок (с соответствующим изменением индексов подвыражений) или (?:…)? в правильных местах.
Проблема валидации полей решается дополнительным if ’ом внутри. Потому что мы все знаем, что происходит, когда пытаются валидировать всё и вся только регекспом.
Проблема двоеточия в смещении — решается или не решается после анализа тех данных, которые будут через это прогоняться. Если данных без двоеточия много, оставляем как есть. Если мало — добавляем в образец (:)? и warning. Если доказано, что их быть не должно — втыкаем в образец двоеточие.
Проблема мусора решается заключением образца в ^…$ .
Проблема двоеточия в секундах решается заменой $match[6] на $match[7] или шестых скобок (…) на скобки без захвата (?:…) .
Пункты 7–8 содержат решение прямым текстом. Rationale к пункту 7 — разные движки регулярных выражений имеют разные понятия, что приоритетнее, альтернатива или конкатенация.
В коде сформулирую чуть позже.

12/6/07, 02:11 pm
function is_leap_year($year)
{
return (($year % 400) == 0) || ((($year % 100) != 0) && (($year % 4) == 0));
}
$month_days = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
function month_days($year, $month)
{
global $month_days;
return $month_days[$month] + (($month == 2 && is_leap_year($year)) ? 1 : 0);
}
// returns false if $s is not a valid W3C date/time,
// or an array of date/time elements:
// 0 year int 0 to 9999
// 1 month NULL or int 1 to 12
// 2 day NULL or int 1 to 31
// 3 hour NULL or int 0 to 23
// 4 minute NULL or int 0 to 59
// 5 second NULL or float [0, 60)
// 6 zone NULL or int offset from UTC in minutes
function parse_datetime($s)
{
// 1 2 3
$pattern = "/^([0-9]{4})(?:-([0-9]{2})(?:-([0-9]{2})" .
// 4 5 6
"(?:T([0-9]{2}):([0-9]{2})(?::([0-9]{2}(?:\.[0-9]+)?))?" .
// 7 89 10
"(Z|(?:(([-+])[0-9]{2}):([0-9]{2}))))?)?)?$/";
if (!preg_match($pattern, $s, $matches)) return false;
array_shift($matches);
list($year, $month, $day,
$hour, $minute, $second,
$zone, $zone_sign, $zone_hours, $zone_minutes) = $matches;
$result = array(NULL, NULL, NULL, NULL, NULL, NULL, NULL);
$year = intval($year);
$result[0] = $year;
if (!isset($month)) return $result;
$month = intval($month);
if ($month < 1 || $month > 12) return false;
$result[1] = $month;
if (!isset($day)) return $result;
$day = intval($day);
if ($day < 1 || $day > month_days($year, $month)) return false;
$result[2] = $day;
if (!isset($hour)) return $result;
$hour = intval($hour);
if ($hour < 0 || $hour > 23) return false;
$minute = intval($minute);
if ($minute < 0 || $minute > 59) return false;
$zone_hours = intval($zone_hours);
$zone_minutes = intval($zone_minutes);
// Insert zone validation here -- IMO matching with a static list is too strict,
// and even checking that -12 <= $zone_hours <= +14 is not entirely correct
if ($zone_minutes < 0 || $zone_minutes > 59) return false;
$result[3] = $hour;
$result[4] = $minute;
$result[6] = $zone_hours * 60 + intval($zone_sign . $zone_minutes);
if (!isset($second)) return $result;
$second = (float)($second);
if ($second < 0 || $second >= 60) return false;
$result[5] = $second;
return $result;
}
12/6/07, 08:51 am
Писать регексп так, чтоб не пропускал 2007-02-29, но пропускал 2007-02-28 смысла не вижу. Это проще проверить потом.
Разницы же между 36м января и 29м февраля 2007 года нету.
12/6/07, 08:02 am
list( $year, $month, $day, $hours, $minutes, $seconds) = array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]); 1 либо меня регексп чтобы $match[6] хранила секунды без ":" 2 либо использовать в list $match[7] - чтобы получить значение секунд
12/6/07, 09:09 am
Ознакомился с комментариями. Мозг взорван, спасибо. :)
|