Образцы для дат и времени

Задача
Вы хотите убедиться в том, что строка представляет собой дату или время.

Решение
Используйте образец для значения времени интересующего вас типа. Не забудьте о проверке таких деталей, как разделители элементов и длины этих элементов.

Обсуждение
Проверка корректности дат – это сплошная головная боль, так как они могут быть представлены во множестве форматов. Сравнение с образцом чрезвычайно полезно для удаления недействительных значений, но часто его бывает недостаточно для полной проверки: дата может содержать число там, где должен быть месяц, но это может быть число 13. В этом рецепте мы обсудим несколько образцов для распространенных форматов дат.

Формату дат ISO (CCYY-MM-DD) отвечает такой образец:

/^\d{4}-\d{2}-\d{2}$/

Он требует, чтобы разделителем частей даты был символ -. Для того чтобы наряду с дефисом разделителем мог служить и символ /, используйте между числовыми составляющими класс символов (слэш экранируется обратным слэшем, чтобы он не интерпретировался как конец конструктора образца):

/^\d{4}[-\/]\d{2}[-\/]\d{2}$/

А можно использовать другой ограничитель вокруг образца, тогда удастся избежать применения обратного слэша:

m|^\d{4}[-/]\d{2}[-/]\d{2}$|

Чтобы разрешить использование любого нечислового разделителя (а именно так действует MySQL, интерпретируя строки как даты), воспользуйтесь таким образцом:

/^\d{4}\D\d{2}\D\d{2}$/

Если необязательно, чтобы в каждой части присутствовали все четыре цифры (чтобы можно быть опустить начальные нули в значениях типа 03), будем искать числовые трехзначные непустые последовательности:

/^\d+\D\d+\D\d+$/

Конечно, это очень общий образец, и ему будут соответствовать и другие значения, например, номера социального страхования США, имеющие формат 012-34-5678.


Чтобы наложить ограничение на длины составляющих значений, потребовав, чтобы часть года имела не менее двух разрядов, часть дня и месяца – один или два разряда, используйте следующий образец:

/^\d{2,4}?\D\d{1,2}\D\d{1,2}$/

Для дат в форматах MM-DD-YY и DD-MM-YY применяются похожие образцы, только составляющие располагаются в другом порядке. Предлагаемый образец соответствует обоим форматам:

/^\d{2}-\d{2}-\d{2}$/

Если необходимо проверить значения отдельных составляющих даты, используйте в образце скобки и после установления соответствия извлекайте подстроки. Например, для дат в формате ISO можно сделать нечто подобное:

if ($val =~ /^(\d{2,4})\D(\d{1,2})\D(\d{1,2})$/)
{
($year, $month, $day) = ($1, $2, $3);
}

Библиотечный файл lib/Cookbook_Utils.pm дистрибутива recipes содержит несколько таких сравнений с образцом, оформленных как вызовы функции.

Если дата не соответствует образцу, они возвращают undef. В противном случае они возвращают ссылку на массив, содержащий выделенные значения года, месяца и дня, которые могут пригодиться для дальнейших проверок компонентов даты.


Например, функция is_iso_date() ищет даты в формате ISO:

sub is_iso_date
{
my $s = shift;
return undef unless $s =~ /^(\d{2,4})\D(\d{1,2})\D(\d{1,2})$/;
return [ $1, $2, $3 ]; # вернуть год, месяц, день
}
Использовать функцию можно так:
my $ref = is_iso_date ($val);
if (defined ($ref))
{
# $val соответствует образцу формата ISO;# проверять его составляющие, используя $ref->[0] - $ref->[2]
}
else
{
# $val не соответствует образцу формата ISO
}

Дополнительная проверка дат проводится достаточно часто, так как несмотря на то что образцы и помогают отбросить синтаксически неправильные значения, они не оценивают корректность отдельных составляющих. Необходимо выполнять проверку вхождения значений в диапазон.

Если вы хотите пропустить проверку составляющих и просто перезаписать значения, используйте подстановку. Например, чтобы преобразовать значения из формата MM-DD-YY в формат YY-MM-DD, сделайте следующее:

$val =~ s/^(\d+)\D(\d+)\D(\d+)$/$3-$1-$2/;

Значения времени организованы немного более упорядоченно, чем даты; обычно первыми записываются часы, а последними – секунды, при этом каждая часть состоит из двух цифр:

/^\d{2}:\d{2}:\d{2}$/

Можно несколько смягчить требования, разрешая представлять составляющую часов одним знаком или допуская отсутствие составляющей секунд:

/^\d{1,2}:\d{2}(:\d{2})?$/

Составляющие времени можно поместить в скобки, если вы планируете осуществлять для них отдельные проверки на принадлежность диапазону или, например, переформатировать значения, добавляя составляющую секунд 00 туда, где она отсутствует.


Но будьте внимательны и не забывайте о символе ?

в тех случаях, когда составляющая секунд является необязательной. Хотелось бы сделать так, чтобы все выражение :\d{2} в конце образца было необязательным, и при этом символ : не сохранялся бы в $3, если третий раздел времени все-таки присутствует. Для этого используем (?:образец), нотацию группировки, которая не сохраняет подобранную подстроку. В такой нотации можно заключать цифры в скобки, чтобы сохранить их. Тогда $3 будет содержать undef, если составляющая секунд отсутствует, а в противном случае будет хранить эту составляющую:

if ($val =~ /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/)
{
my ($hour, $min, $sec) = ($1, $2, $3);
$sec = "00" if !defined ($sec); # секунды отсутствуют, используем 00
$val = "$hour:$min:$sec";
}

Чтобы преобразовать значения из 12-часового формата с суффиксами AM и PM в 24-часовой формат, сделаем так:

if ($val =~ /^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(AM|PM)?$/i){
my ($hour, $min, $sec) = ($1, $2, $3);
# добавляем отсутствующие секунды
$sec = "00" unless defined ($sec);
# преобразуем 0 .. 11 -> 12 .. 23 для времен PM
$hour += 12 if defined ($4) && uc ($4) eq "PM";
$val = "$hour:$min:$sec";
}

Составляющие времени помещаются в $1, $2 и $3, при этом $3 устанавливается в undef, если значение не содержит секунд. Если есть суффикс, он попадает в $4. Если суффикс – это AM или его вообще нет (undef), значение интерпретируется как время до полудня (AM). Если же суффикс – это PM, значение интерпретируется как время после полудня.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела MySQL на эту тему:
Диагностическая утилита для LOAD DATA
Извлечение и перестановка столбцов файлов данных
Импорт XML в MySQL
Импорт с помощью LOAD DATA и утилиты mysqlimport
Импорт файлов в формате CSV

Вернуться в раздел: MySQL / 10. Импорт и экспорт данных