Если вам приходится разрабатывать систему подписок, биллинга или что-то другое оперирующее временными отрезками, то вам довольно скоро придёт в голову идея, что неплохо было бы иметь возможность менять текущее время для разрабатываемой системы. Ведь это значительно облегчит вам её тестирование (интеграционное как минимум). Менять системное время буквально — муторное занятие. Так давайте подумаем о том, как лучше реализовать «машину времени» для вашего сайта.
Что нужно
Для этого нам просто нужно изолировать зависимости системы от времени. Для .NET кода это значит заменить вызовы DateTime.Now
(Today, UtcNow,
и т.д.). Я предлагаю простой вариант реализации такого TimeService для .NET.
Запускаем
Итак, у вас есть сайт. Чтобы добавить «машину времени»:
1. Добавляем класс TimeService
. Он будет заменять нам DateTime
, когда нам будет надо узнать текущее время. Пример реализации я положил в gist.
2. Находим все места использования DateTime.Now
(Today
, UtcNow
) в нашем коде (утилиты для рефакторинга нам здесь будут очень полезны). И заменяем их на TimeService.Now и т.п.
3. Опционально делаем несложную страницу с календариком, которая будет вызывать сеттер TimeService.Now
или UtcNow
, или делать TimeService.Reset()
, когда нужно вернуть время к актуальному. Т.к. наш TimeService хранит сдвиг относительно времени в реестре — вы вполне можете в первое время обойтись regedt32.exe для изменения времени.
Всё. Теперь ваш сайт живёт в контексте времени из вашего TimeService
.
Синхронизация между процессами
Хранение значения сдвига времени в реестре позволяет использовать одно и тоже время в нескольких процессах (для web-garden — конфигураций). Или например, если в дополнение к вашему сайту, всем биллингом управляет отдельный системный сервис — время везде будет одинаковое.
В любом случае, чтобы всё пошло как по маслу, надо добавить доступ к нужной ветке реестра всем его пользователям (Network Service, пользователь IIS AppPool и т.д.). По-умолчанию машина времени хранит своё значение в HKEY_USERS.DEFAULTSoftwareTimeService
. Это место позволяет давать доступ любому пользователю и не сталкиваться с UAC (в отличие от HKLM).
Базы данных, файловая система
Нужно понимать, что наша машина времени способна влиять только на ограниченный набор подконтрольного нам кода. И учитывать это при разработке. Например, если вы где-то полагаетесь на временные значения с файловой системы, или вызовы GETDATE()/GETUTCDATE()
к СУБД, то тут вам необходимо продумать эти вопросы отдельно.
Надеюсь, эта статья будет полезна, как идея и как пример.
Усовершенствования и новые идеи — приветствуются.
Автор: bitmap