Ruby strptime не генерирует ArgumentError для% Y /% m /% d с параметром '25 / 01/2017 ' - PullRequest
0 голосов
/ 27 июня 2018

Сегодня обнаружил странное поведение, которое, я надеюсь, кто-то может пролить немного света.

Я использую strptime для проверки дат в файле импорта. В этом случае я хочу выдать ошибку, если строка в файле содержит дату, которая не соответствует формату% Y /% m /% d (2017/01/25).

Я вызываю strptime следующим образом:

Date.strptime('25/01/2017', '%Y/%m/%d')

Я ожидаю, что это не удастся, так как 25 не соответствует критериям года. Однако это удается, предоставляя дату как:

0025, 01, 20

Если я поменяю месяц и день (25.01.2008), произойдет сбой, поскольку он обнаружит, что месяц недействителен.

Так что же дает? Кажется странным, что он не только создает этот ментально выглядящий год (0025), но еще более безумным является то, что он игнорирует «17» в конце строки без проблем.

Заранее спасибо! :)

1 Ответ

0 голосов
/ 27 июня 2018

Вы должны думать, что вы на самом деле сказали:

Date.strptime('25/01/2017', '%Y/%m/%d')

Вы говорите, что вам нужен год 0025, месяц 01 и день 20 (это лишает остальных). В итоге вы получите 0025-01-20.

Вы не можете полагаться только на Date.strptime, чтобы выполнить проверку для вас.

Лучше всего разобрать его с помощью регулярного выражения и выполнить проверку.

Для вашего формата возможное регулярное выражение (простой способ):

'25/01/2017'.match(/\d{4}\/\d{2}\/\d{2}/)

В вашем случае вы получите nil, потому что он не совпадает.

Если вы получите совпадение, вы получите: #<MatchData "2017/01/25">.

Проблема в том, что это не проверяет правильный формат даты. Вам все еще нужно проверить, может ли strptime проанализировать результат (как в ссылке, предоставленной Томом Лордом).

С другой стороны, вы можете проверить это также только с помощью регулярного выражения, которое может быть довольно сложным: (следующие проверки регулярных выражений yyyy/mm/dd формат):

^(?:(?:(?:(?:(?:[1-9]\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:[2468][048]|[13579][26])00))(\/)(?:0?2\1(?:29)))|(?:(?:[1-9]\d{3})(\/)(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[13-9]|1[0-2])\2(?:29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8])))))$

Тогда вы точно знаете, что дата в правильном формате, и вам не нужно проверять ее с помощью strptime.

Редактировать:

При работе со временем не забывайте всегда выполнять свои собственные проверки! Не полагайтесь на функцию. Проблема со временем состоит в том, что у вас есть много исключений, и даже если у вас есть ISO 8601 и, возможно, некоторые другие приложения могут его не соблюдать.

На основании комментария я хочу углубиться в strptime А пока я хочу вставить комментарий в исходный код (в функции date_s_strptime и data_core.c):

/*
 * call-seq:
 *    Date.strptime([string='-4712-01-01'[, format='%F'[, start=Date::ITALY]]])  ->  date
 *
 * Parses the given representation of date and time with the given
 * template, and creates a date object.  strptime does not support
 * specification of flags and width unlike strftime.
 *
 *    Date.strptime('2001-02-03', '%Y-%m-%d')   #=> #<Date: 2001-02-03 ...>
 *    Date.strptime('03-02-2001', '%d-%m-%Y')   #=> #<Date: 2001-02-03 ...>
 *    Date.strptime('2001-034', '%Y-%j')    #=> #<Date: 2001-02-03 ...>
 *    Date.strptime('2001-W05-6', '%G-W%V-%u')  #=> #<Date: 2001-02-03 ...>
 *    Date.strptime('2001 04 6', '%Y %U %w')    #=> #<Date: 2001-02-03 ...>
 *    Date.strptime('2001 05 6', '%Y %W %u')    #=> #<Date: 2001-02-03 ...>
 *    Date.strptime('sat3feb01', '%a%d%b%y')    #=> #<Date: 2001-02-03 ...>
 *
 * See also strptime(3) and #strftime.
 */

Вы также можете видеть строки вроде sat / feb, поэтому неудивительно, что парсер может справиться со строками. ПРОДОЛЖЕНИЕ - копаться в коде C

...