четверг, июля 03, 2008

Date, Time, Interval, and Duration

С представлением времени в большинстве систем творится полный бардак. Ладно, RDF с OWL’ем, там и без этого все плохо, но даже в стандартных библиотеках самых что ни на есть мейнстримных языков полный бардак. Ну что нужно курить чтобы в стандартный класс Date добавить преобразование к Юлианскому календарю? Ни разу не встречал человека, которому бы это потребовалось. Зато вычислять интервал между двумя событиями приходится вручную. Давайте попробуем спроектировать библиотеку для работы со временем правильно. Может хоть кому-нибудь живой пример вправит мозги.

Итак, что такое время для программиста? Это одна координатная прямая, на которой определена выделенная точка «сейчас», делящая прямую на «прошлое» и «будущее». Примитивные племена на этом и останавливаются, оперируя тремя понятиями: «было», «есть» и «будет». Современному человеку требуется больше: время должно быть измеримо. Это позволит производить вычисления (до сеанса осталось полчаса) и учитывать порядок событий ([сначала]он мне в глаз, а [затем] я ему в ухо) от которого и до причинно-следственной связи недалеко. Для того чтобы сделать время измеримым на прямую произвольным образом добавляют фиксированную выделенную точку «0». Исторически сложилось так, что в разных культурах эту точку добавляли по-своему. Это мог быть день коронования императора, начало определенного года, произвольно выбранный момент времени и даже время «сотворения мира». В мире UNIX, например, это полночь 1 января 1970 года.

С другой стороны люди привыкли к довольно неудобной для вычислений системе предстваления времени, связанной с циклами вращения Земли. Опять же, в разных культурах к этой проблеме подходили по разному. В результате год начинается в разное время и по разному делится на месяцы в зависимости от культуры. Более того, в разных культурах привычные способы записи времени сильно отличаются, даже если системы счисления времени совпадают. Та же американская ММ/ДД/ГГ и русская ДД.ММ.ГГ.

Из-за привычки людей к солнечному времени, в один и тот же момент время отличается в разных часовых поясах. Как будто этого мало, многие страны используют летнее/зимнее время для экономного использования светового дня. В результате, вычисление времени в определенной локации становится довольно неудобной задачей, по крайней мере для человека.

Поскольку продолжительность суток растет, каждые несколько лет к UTC добавляют секунду, чтобы компенсировать замедление. Это тоже усложняет точные расчеты, однако, пока не вызывает проблем, поскольку единой системы точного времени затрагивающую все — от GPS приемников до кофеварок не создано.

Теперь, когда требования ясны, займемся проектированием. Будет удобно, если внутри системы время будет хранится единообразно, в собственной системе координат, а преобразоваться в нужную будет только на входе и выходе. По крайней мере это избави программиста от ручных расчетов и преобразований форматов.

Первое, что нам требуется, это момент времени (Timestamp) заданный с максимальной возможной точностью в некоторой системе отсчета, хоть UNIX time. Этот класс является платформозависимым, поскольку умеет работать с системным таймером. Никаких преобразований или вычислений этот класс делать не должен. Очень часто требуется работать с моментами времени с точностью до минуты, дня или года, в этом случае максимальное разрешение тут только мешает. Для некоторой частых случаев можно определить классы Date и DateTime и точностью, соответственно, до дня и до секунды. Timestamp должен корректно поддерживать сравнение отметок времени с максимальной и ограниченной точностью.

Второе — интервал между двумя моментами времени (Interval) Интервал можно представить двумя моментами времени: начало интервала и конец интервала. Интервал должен уметь вычислять свою продолжительность. Кроме того, нужна возможность зная начало или конец интервала и продолжительность, получить интервал.

Третье — продолжительносить (Duration) Внутри продолжительность хранится в максимально возможной системной точностью.

Для каждой из трех сущностей потребуется два интерфейса: *Parser и *Formatter имплементации которых служат для преобразования сущностей из строки и к строке (или прочим типам). Кроме форматирования, их зона ответственности — преобразование к другим системам представления времени, например к тому же юлианскому календарю.

В основном, этого достаточно. Естественно, в некоторых языках можно добавить собственные удобности, например профайлинг в Ruby:

duration = Duration.measure do
...
end

2 комментария:

_kleptos_ комментирует...

http://docs.python.org/lib/module-datetime.html

Miroff комментирует...

Питоновская библиотека производит впечатление некоторой непродуманности и незавершенности. Там точно также перемешаны системное и пользовательское представление (tzinfo в datetime), точно также нет форматтеров и парсеров для даты/времени (strptime и strftime годятся ограниченно, только в пределах Григорианского календаря), нет понятия интервала (timedelta это продолжительность, а не интервал). По сути, есть только datetime в собственных попугаях, timedelta и необходимость описывать каждый отдельный формат самому.

Гораздо ближе к идеалу библиотеки Java, но там нет интервала и продолжительности.