# time-machine: A library to mock the current time.

[ bsd3, control, library ] [ Propose Tags ]

A library to mock the current time and relevant IO functions by using a type class. You can get the great command of the current time in UTC, time zones, and the speed of time.

Versions [faq] 0.1.0 base (>=4.7 && <5), mtl, time, tz [details] BSD-3-Clause Copyright (C) 2017 TAKAHASHI Yuto TAKAHASHI Yuto TAKAHASHI Yuto Control https://github.com/y-taka-23/time-machine#readme head: git clone https://github.com/y-taka-23/time-machine by y_taka_23 at 2017-11-26T07:41:48Z NixOS:0.1.0 685 total (26 in the last 30 days) (no votes yet) [estimated by Bayesian average] λ λ λ Docs available Last success reported on 2017-11-26

## Modules

[Index]

#### Maintainer's Corner

For package maintainers and hackage trustees

[back to package description]

# time-machine

A library to mock the current time and relevant IO functions by using a type class. You can get the great command of the current time in UTC, time zones, and the speed of time.

module Main where

main :: IO ()
main = backTo (the future) $do t <- getCurrentTime liftIO . putStrLn$ "We are at " ++ show t

-- We are at 1985-10-26 08:24:00.035889 UTC


As you know, time-dependent IO actions are extremely hard to test. Assume, for example, the following simple action. It should return "Good morning" only in the morning, thus the results of its unit tests are fatally fragile.

getGreeting :: IO String
getGreeting = do
t <- getCurrentTime
if utctDayTime t <= 12 * 60 * 60
then return "Good morning"
else return "Hello"


This library aims to make such actions testable with minimal changes. Actually what you have to do is just:

1. Use MonadTime type class instead of IO
2. Wrap IO actions in liftIO if necessary

Here is the testable version of getGreeting. You can see that nothing is changed excepting the signature.

getGreeting :: (MonadTime m) => m String
getGreeting = do
t <- getCurrentTime
if utctDayTime t <= 12 * 60 * 60
then return "Good morning"
else return "Hello"


## Examples

### Mocking the Current Time

travelTo changes the result of getCurrentTime and relevant actions.

Other than pointing the target UTCTime explicitly, you have two ways to determine how to mock the current time. This library provides a small DSL to construct the destinations of your time travels.

-- By a date-time according to your local time zone
main = travelTo (oct 26 1985 am 1 24) $do getCurrentTime >>= (liftIO . print)  -- By a relative date main = travelTo (3 days ago)$ do
getCurrentTime >>= (liftIO . print)


For more detail, see the document.

### Mocking Time Zones

jumpTo switch the time zone which is used for calculating the local time. By this function, you can test time-zone-sensitive actions.

import qualified Data.Time.Zones as TZ

main = jumpTo "Asia/Shanghai" $do t <- getCurrentTime tz <- loadLocalTZ liftIO . print$ TZ.timeZoneForUTCTime tz t  -- CST


### Mocking the Speed of Time

accelerate changes the speed of time. In the following example, time flies 60 times faster than the real.

main = accelerate (x 60) $do getCurrentTime >>= (liftIO . print) -- (*) liftIO . threadDelay$ 1000 * 1000   -- wait a second
getCurrentTime >>= (liftIO . print)  -- around a minute after (*)


Moreover, as a special case of accelerate, halt stops the time. That is remarkably useful to fix the point of time during your tests.

main = halt $do getCurrentTime >>= (liftIO . print) -- (*) liftIO . threadDelay$ 1000 * 1000   -- wait a second
getCurrentTime >>= (liftIO . print)  -- exactly same as (*)


## Installation

The project is managed by Stack, so you can install it simply:

$git clone https://github.com/y-taka-23/time-machine.git$ cd time-machine
\$ stack install