различия. Самое главное состоит в том, что класс Date
вообще игнорирует время, то есть работает с точностью до одного дня. Кроме того, класс Date
строже контролирует ошибки, чем класс Time
: попытка обратиться к 31 июня (или к 29 февраля невисокосного года) приведет к исключению. Код даже «знает» о различных датах перехода на григорианский календарь в Италии и Англии (в 1582 и 1752 году соответственно) и может обнаружить «несуществующие» даты, появившиеся в результате такого перехода. Эта стандартная библиотека — паноптикум интересного и местами загадочного кода. К сожалению, у нас нет места для более подробного разговора о ней.
7.19. Взаимные преобразования объектов Date, Time и DateTime
В Ruby есть три основных класса для работы с датами и временем: Time
, Date
и DateTime
. Опишем их особенности:
Time
преимущественно обертывает соответствующие функции из стандартной библиотеки языка С. Они, как правило, опираются на точку отсчета в UNIX и потому не способны представлять моменты времени раньше 1970 года.
Date
создан для преодоления недостатков класса Time
. Он без труда справляется с датами в более отдаленном прошлом — например, позволяет представить день рождения Леонардо да Винчи (15 апреля 1452 года), и, кроме того, знает о реформе календаря. Но у него есть свои слабые места: он работает только с датами, игнорируя время.
DateTime
наследует Date
и пытается компенсировать отсутствующие в нем возможности. Он может представлять даты не хуже Date
и время не хуже Time
. Часто его способ представления даты и времени оказывается наилучшим.
Однако не думайте, что объект DateTime
— это просто объект Date
, к которому механически присоединен объект Time
. На самом деле в классе DateTime
отсутствуют такие методы, как usec
, dst?
и некоторые другие.
Итак, у нас есть три класса. К сожалению, не существует стандартного способа преобразовать один из них в любой другой. По мере развития Ruby подобные шероховатости будут устраняться. А пока обойдемся методами, приведенными в листинге 7.2. Спасибо Кирку Хейнсу (Kirk Haines).
class Time
def to_date
Date.new(year, month, day)
rescue NameError
nil
end
def to_datetime
DateTime.new(year, month, day, hour, min, sec)
rescue NameError
nil
end
end
class DateTime
def to_time
Time.local(year,month,day,hour,min,sec)
end
end
class Date
def to_time
Time.local(year,month,day)
end
end
Эти методы пропускают наверх все исключения, кроме NameError
. Зачем нужно его перехватывать? Потому что могло случиться так, что программа не затребовала (с помощью директивы require
) библиотеку date
(напомним, что классы Date
и DateTime
входят в эту стандартную библиотеку, а не являются системными). В таком случае методы to_datetime
и to_date
возвращают nil
.
7.20. Извлечение даты и времени из строки
Дата и время могут быть представлены в виде строки самыми разными способами: в полной или сокращенной форме, с разной пунктуацией, различным порядком компонентов и т.д. Из-за такого разнообразия очень сложно написать код, интерпретирующий символьную строку как дату. Рассмотрим несколько примеров:
s1 = '9/13/98 2:15am'
s2 = '1961-05-31'
s3 = '11 July 1924'
s4 = 'April 17, 1929'
s5 = '20 July 1969 16:17 EDT'
s6 = 'Mon Nov 13 2000'
s7 = 'August 24, 79' # День разрушения Помпеи.
s8 = '8/24/79'
К счастью, большую часть работы за нас уже проделали. В модуле ParseDate
есть единственный класс с таким же именем, а в нем — единственный метод parsedate
. Он возвращает массив компонентов даты в следующем порядке: год, месяц, день, час, минута, секунда, часовой пояс, день недели. Вместо полей, которые не удалось распознать, возвращается nil
.
require 'parsedate.rb'
include ParseDate
p parsedate(s1) # [98, 9, 13, 2, 15, nil, nil, nil]
p parsedate(s2) # [1961, 5, 31, nil, nil, nil, nil, nil]
p parsedate(s3) # [1924, 7, 11, nil, nil, nil, nil, nil]
p parsedate(s4) # [1929, 4, 17, nil, nil, nil, nil, nil]
p parsedate(s5) # [1969, 7, 20, 16, 17, nil, 'EDT', nil]
p parsedate(s6) # [2000, 11, 13, nil, nil, nil, nil, 1]
p parsedate(s7) # [79, 8, 24, nil, nil, nil, nil, nil]
p parsedate(s8,true) # [1979, 8, 24, nil, nil, nil, nil, nil]
Последние две строки иллюстрируют назначение второго параметра parsedate
, который называется guess_year
. Из-за привычки записывать год двумя цифрами может возникнуть неоднозначность. Последние две строки интерпретируются по-разному; при разборе