1. Introduction

Time is an illusion. Lunchtime doubly so.

— Douglas Adams
The Hitchhiker's Guide to the Galaxy

Tick is a comprehensive Clojure(Script) library designed to make it easier to write programs that involve time and date calculations:

  • Functions to manipulating time, easily and succinctly (stable)

  • Powerful functions for slicing and dicing time intervals (stable)

  • Implementation of Allen’s interval algebra (alpha)

  • Support for iCalendar serialization (work-in-progress)

In many business domains, dates are as fundamental as numbers and strings. It’s often desirable to have date-heavy business logic portable across platforms. Tick supports both Clojure and ClojureScript, with an identical API.

Tick is implemented using (a very thin wrapper over) the api of java.time and an understanding of the concepts behind java.time will be very useful when working with tick, because tick entities are java.time entities (Instant, LocalTime etc). Where tick doesn’t provide the api you need, you can look at the java.time api to see if there alternatives. If you cannot find the help you need in the tick documentation, it is quite likely that someone will have had the same query and had it resolved on Stack Overflow.

1.1. Status

tick.core (the main API)is stable. The namespaces under tick.alpha.*, such as tick.alpha.interval are alpha status. By alpha, we mean that the library’s API may change in future. Do let us know if you come across any unexpected behaviour or bugs anywhere in tick.

1.2. License

Tick is copyrighted by JUXT LTD. and licensed as free software under the open-source MIT License.

The MIT License (MIT)

Copyright © 2016-2018 JUXT LTD.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1.3. Comparison to other time libraries

Java 8 time

Java 8’s java.time API is both influenced by, and an improvement on, Joda Time.

Unlike older JDK dates and calendars, instances in java.time are immutable so can be considered values in Clojure. For this reason, there is no reason to wrap these values. Consequently, there is full interoperability between tick and java.time. Where tick does not provide a part of java.time’s functionality, java.time can be called directly in either Clojure or Clojurescript.

Caution
Because tick is built on java.time, Clojure programs must run on Java 8 or higher.

clj-time and cljs-time

Most Clojure applications use clj-time which is based on Joda Time. However, cljs-time objects are mutable goog.date objects which in turn wrap JavaScript Date objects.

This works OK as a proxy for Instant, but is not a great foundation for local dates etc.

The author of cljs-time, Andrew McVeigh, has said he would ideally move cljs-time off goog.date but is unlikely to do so at this point. For one thing, there could be more than a few current users relying on the JS Date nature of the cljs-time objects.

Taking a fresh look at the date/time landscape, we now have java.time (JSR-310) and implementations in both Java and Javascript and so it is possible to create tick, which combines the excellent JSR-310 with an expressive, cross-platform Clojure(Script) API.

For some use cases it is possible to write cross-platform code with clj/s-time, conditionally requiring clj-time or cljs-time in a cljc file. In our experience though, the fact that cljs-time doesn’t have complete fidelity with clj-time often comes to be a problem.

2. Setup

Get the latest from Clojars and add to your project.clj, build.boot or deps.edn.

Here is a one-liner to drop into a node repl with tick:

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.764" } tick/tick {:mvn/version "RELEASE"} }}' -m cljs.main  -re node  --repl

2.1. Serialization

There are many use cases for de/serialization of dates, including simply being able to copy and paste within the REPL. Tick bundles time-literals Clojure(Script) library, so having require’d tick, in your code or at the repl you can type

#time/period "P1D"

which is read as a java.time.Period (or js-joda Period in ClojureScript).

To avoid tick modifying the printer for java.time objects (if you already employ a custom set of literals for example), set the following jvm property

:jvm-opts ["-Dtick.time-literals.printing=false"]

To read and write edn data containing these literals in Clojure(Script) and for more information generally, see the tagged literals Readme

2.2. Clojurescript

Tick versions 0.4.24-alpha and up require minimum Clojurescript version of 1.10.741

Tick currently uses the js-joda library, which aims to replicate the three-ten-backport project.

There is a plan to move to the new platform Javascript Date API, called Temporal

NPM Setup

If you are using Shadow-cljs then there is no npm setup you need to do.

If you are using webpack (cljs :bundle option) in your build, first add the transitive npm dependencies to your package.json. Assuming you have tick in your deps.edn, from your project directory, run

clj -m cljs.main --install-deps

Now your package.json has the required npm libs added.

Non-NPM Setup

Add js-joda and js-joda-locale-en-us and js-joda-timezone to your dependencies.

Optional Timezone & Locale data for reducing build size

The use of timezones and locales is optional to allow a smaller build size. The way they are implemented in JsJoda means that they are accessed via side-effecting require. This is ugly, but there is no other option available.

Minified, gzipped js-joda (what gets pulled in if you use anything of tick) is around 43k (or 205k unzipped). For comparison, a basic Cljs app with React is about 330k unzipped.

So by using Tick you might come close to doubling your build size!

But is that a problem? That depends, but really 330k is my no means small to start with, so likely for the majority of potential users it would not be an issue at all. cljc.java-time is pitted against another 'lightweight' Cljs date/time library in this experiement which shows a web app using it does not load more slowly and over multiple visits will download less data overall.

Moving the implementation to Tempo will mean the build includes only the compiled Clojurescript of tick itself.

Timezone is an extra 26k, and Locale (just en-US) is an extra 45k

Timezones

If you want to work with timezones, something like this, for example:

(tick/zone "Europe/London")

add the following require:

[tick.timezone]

Note that this is pulling in all of the history of timezones as well. If you don’t need historic data and you want to reduce build size, js-joda provides pre-build packages for just the more recent data.

Also note that in the tick.core ns is clock which is initially set to the current default zone - whatever that happens to be at the time the ns is loaded. clock is used for ambient where+now information such as when getting the current time. If no zone data has been loaded at the time tick.core is evaluated then the current default zone will be SYSTEM (see JsJoda docs for info). If you would rather the zone be a proper IANA zone, then after require’ing tick.timezone

(set! tick.core/*clock* (cljc.java-time.clock/system-default-zone))

for example. This avoids the need to ensure that tick.timezone is loaded before tick.core

Formatting & Parsing

If you want to create custom formatters from patterns, such as "dd MMM yyyy", add this require:

[tick.locale-en-us]

Locale data is needed for custom date formatters which need particular symbols, such as M for month. Due to the size and complexity of using the js-joda-locale, the authors of js-joda-locale have created prebuilt locale packages, for specific locales. en-US is one which is currently packaged for cljs and can be used as suggested above.

3. API

Tick provides the tick.core namespace, containing the functions that make up the library’s stable API.

When you are using tick in programs, it is a recommended idiom that you require tick's api under the t alias as follows:

(require '[tick.core :as t])
Caution
Try to restrict your use of tick to the tick.core namespace and tick.alpha.* namespaces. Functions in other namespaces, which may not be marked private, are not part of the official API and could change.

4. Times & dates

In this chapter we introduce times & dates and how we can manipulate them.

4.1. Introduction

If we asked a stranger for the time they’d likely respond with the just time of day, for example, "a quarter to eight". It would be uncommon for them to tell you the date and timezone also, unless you asked for it. This is the same time that we learn to read as children, on analog watches and wall-clocks.

So let’s start with asking tick for the time of day, with the time function. We demonstrate this with our first example:

Example 1. Getting the time

To get the current time, call (t/time) with no arguments:

(t/time)
Note
If you have enabled JavaScript, you’ll see a button labelled Eval on the right of the code snippet. Press this button to instruct your browser to evaluate the tick code and display the result. You can clear the result by pressing the button labelled Clr. Many of the code examples in this documentation can be evaluated like this.

But so we can continue with our time-telling story, let’s get a specific time of "a quarter to eight":

Example 2. Getting a specific time

To get a specific time of day, call (t/time) with a string argument:

(t/time "19:45")

If we wanted to know the name of the day today, we might ask "what’s the day today". In tick, we’d use the day function.

Example 3. Getting the name of the day

We can get the name of the day today like this:

(t/day-of-week (t/today))

Or, even just:

(t/day-of-week)

Or, the day tomorrow:

(t/day-of-week (t/tomorrow))

The time "a-quarter-to-eight tomorrow" is useful, but if we wanted to record events we’d soon want to ask the date too. In tick, dates are specific calendar dates in the Gregorian calendar and include a day, month and year. We can ask the date with the date function:

Example 4. Getting today’s date

To get today’s date, we use the date function without arguments:

(t/date)

Alternatively we can call today:

(t/today)
Note
On the Java platform a tick date corresponds to a java.time.LocalDate instance, while in a JavaScript environment it corresponds to js-joda’s LocalDate.

Like with time, we can get particular dates by calling date with a string argument:

Example 5. Getting a specific date

To get June 21st (or 21st June! [1]) in 2018:

(t/date "2018-06-21")

Now we can ask for both time and date, we can combine them into a date-time.

We can use the function date-time with no arguments to get the current time at the current date:

Example 6. Getting the current date-time

To get the current date-time, call t/date-time with no arguments:

(t/date-time)

As with time and date, we can use a string argument with date-time, as shown in Example 7.

Example 7. Getting a specific date-time

To get the time of the Armistice of Compiègne, use ISO 8601:

(t/date-time "1918-11-11T11:00")

A date-time is the time at a specific location on a specific calendar date. Since noon is established as the point at which the Sun crosses the meridian, and since the Earth is spherical and rotating, noon is the same time for everyone. Consequently, the world is split into time-zones, each at an offset to Coordinated Universal Time (UTC).

If we wish to compare times in different places, we need to capture the local offset, as shown in Example 8.

Example 8. Getting a specific date-time with a local offset.

The Armistice of Compiègne was agreed at 11:00 am Paris time. On November 11th, 1918, Paris was one hour ahead of UTC. To capture this offset we can use offset-date-time:

(t/offset-date-time "1918-11-11T11:00:00+01:00")

There is a problem with using time offsets—they can change for a given time zone, especially since many time zones practice Daylight Savings Time (DST). To capture the actual time zone, rather than the offset in effect on a given date, we can use zoned-date-time, as shown in Example 9.

Why not local-date?

Java and js-joda name classes representing dates and date-times with a prefix of 'Local'. Why doesn’t tick maintain this convention? The reason is that date-times (and dates especially) are always local, so the use of this prefix is superfluous. However, in Java, it is useful to distinguish between java.time.LocalDate and java.util.Date, or in JavaScript, between js-joda’s LocalDate and JavaScript’s built-in Date. In these contexts, the 'Local' prefix makes sense.

In, tick, instances of Java’s java.util.Date and JavaScript’s Date are termed insts, so the term date is unambiguous (i.e. always local).

Example 9. Getting a specific date-time in a time zone.

The Armistice of Compiègne was agreed at 11:00 am Paris time. In the summer, Paris time moves one hour forward for Daylight Savings Time (DST). Although Paris did use Daylight Savings Time in 1918, the clocks had already moved back (at midnight on 8th October). To capture the time zone, along with its various rules for calculating offsets, we can use zoned-date-time:

(t/zoned-date-time
 "1918-11-11T11:00:00Z[Europe/Paris]")

Rather than using offset-date-times and zoned-date-times, you should use instants when you want UTC-based date-times and don’t care about time-zones and 'local' time.

Since a zoned-date-time (and offset-date-time) captures the offset from UTC, we can convert these into instants, as show in Example 10.

Note
On the Java platform, an instant is a java.time.Instant and replaces the flawed java.util.Date. In a JavaScript environment, js-joda provides an identical class.
Example 10. Converting an offset-date-time to an instant

To convert an offset-date-time to an instant, call instant:

For example:

(t/instant (t/offset-date-time "1918-11-11T11:00:00+01:00"))

If you want to get an instant representing the current time in UTC, call instant without arguments.

Example 11. Get the current instant in UTC

To get the current instant, do this:

(t/instant)

Alternatively, you can just call now:

(t/now)

If you do need a java.util.Date or JavaScript Date, for instance, for interoperating with an existing library, use the inst function.

Example 12. Converting an instant to an inst

To convert the current instant to an inst:

(t/inst (t/now))

In this case, the same could be achieved with the zero-argument form of inst:

(t/inst)

That’s it for our introduction. Now we’ll return to constructing times and dates.

4.2. Construction

Time values are constructed with new-time.

Example 13. Creating a time value

To create the time 11 o’clock am:

(t/new-time 11 0)

new-time also has other forms to increase precision, for example, with seconds…

(t/new-time 23 59 59)

…and with nanoseconds

(t/new-time 23 59 59 999999)
What’s the difference between time and new-time?

Until now we’ve been using time to create time values, for example, (t/time "11:00"), but now we have started to use the new-time function. What’s going on?

Values are created using constructor functions in tick. There are also conversion functions, which are named after the value they convert to. For example, the time function is a conversion function which converts are string to a time value.

In tick, the names of constructor functions are prefixed with new- to prevent naming clashes with conversion functions.

These conventions have been taken from a blog article from Stuart Sierra on how to name Clojure functions.

Similarly, dates are constructed with new-date, a function which has 3 forms. The first, and most common form, requires 3 arguments: the year, month and day (of the month).

Example 14. Creating a date value

JUXT was incorporated on March 22nd, 2013. We can create this date with new-date like this:

(t/new-date 2013 3 22)

Likewise, year-months are constsructed from the year and the month:

Example 15. Creating a year-month value
(t/new-year-month 2013 3)

4.3. Reification

While t/time and t/new-time return a time, you may want to provide a date for that time later on. We can reify with t/on and t/at.

reify

[ree-uh-fahy, rey-] [2]

verb (used with object), re·i·fied, re·i·fy·ing.

  1. to convert into or regard as a concrete thing: to reify a concept.

You can think of a time (or a date) as being a partially defined date-time.

Example 16. Reifying a date-time from dates and times

If we have a date of 1918-11-11, we can construct a date-time by giving the time of 11am with at:

(-> (t/date "1918-11-11") (t/at "11:00"))

Alternatively, if we have the time we can add the date:

(-> (t/time "11:00") (t/on "1918-11-11"))

We can also use reification to provide the location, with in. This allows us to take a local date-time and produce either a zoned-date-time or an offset-date-time.

Example 17. Reifying a zoned-date-time from dates and times

To construct the zoned-date-time of the Armistice of Compiègne with time, on and in:

(-> (t/time "11:00") (t/on "1918-11-11") (t/in "Europe/Paris"))

Alternatively, we can use t/offset-by in place of t/in, to produce an offset-date-time.

(-> (t/time "11:00") (t/on "1918-11-11") (t/offset-by 2))

4.4. Conversion

With instants, insts (java.util.Date, JavaScript’s Date), zoned-date-times and offset-date-times, it’s easy to get stuck with the wrong type. Therefore, tick provides functions to convert between them.

To convert between any of these types, simply call the eponymous function corresponding to the destination type with the source type as an argument.

Caution
When converting between Instants (which are always UTC) and other data types, please be aware that current default zone of the browser or jvm will affect the result.

For example:

(t/date (t/instant "1999-12-31T00:00:00Z"))

The result maybe be 1999-12-31 or not, depending on your browser timezone (or timezone of the jvm).

To get the date (or other fields) from an Instant in UTC, we must first go via a UTC ZonedDateTime"

(->
   (t/instant "1999-12-31T00:59:59Z")
   (t/in "UTC")
   (t/date))
Example 18. Converting between types

To convert between an instant and a zoned-date-time:

(t/zoned-date-time (t/now))

To convert between a zoned-date-time and an instant:

(t/instant (t/zoned-date-time))

To convert between an instant and an inst:

(t/inst (t/now))

It’s also possible to convert from strings to their destination types, which will involve parsing the string in to its most appropriate type prior to conversion.

In Java, types are converted according to the rules in Table 1.

Table 1. Converting between Java 8 types
Convert between to instant to offset-date-time to zoned-date-time to inst

from instant

identity

OffsetDateTime/ofInstant

ZonedDateTime/ofInstant

Date/from

from offset-date-time

.toInstant

identity

.toZonedDateTime

.toInstant, Date/from

from zoned-date-time

.toInstant

.toOffsetDateTime

identity

.toInstant, Date/from

from inst

.toInstant

.toInstant, OffsetDateTime/ofInstant

.toInstant, ZonedDateTime/ofInstant

identity

from String

parse

OffsetDateTime/parse

ZonedDateTime/parse

parse, then Date/from

4.5. Extraction

Culturally, we understand time via calendars and it is often desirable to extract certain fields from time values.

The day-of-week function extracts the day (of the week) from a time value, such as date, as shown in Example 19.

Example 19. Extracting fields from a date
(t/day-of-week (t/date "2018-07-09"))
(t/month (t/date "2018-07-09"))
(t/year (t/date "2018-07-09"))

Days of the week, and months of the year, are available as constants listed in Table 2.

Table 2. Tick constants and their corresponding host types
Tick Java JavaScript

tick.core/MONDAY

java.time.DayOfWeek.MONDAY

DayOfWeek.MONDAY

tick.core/TUESDAY

java.time.DayOfWeek.TUESDAY

DayOfWeek.TUESDAY

tick.core/WEDNESDAY

java.time.DayOfWeek.WEDNESDAY

DayOfWeek.WEDNESDAY

tick.core/THURSDAY

java.time.DayOfWeek.THURSDAY

DayOfWeek.THURSDAY

tick.core/FRIDAY

java.time.DayOfWeek.FRIDAY

DayOfWeek.FRIDAY

tick.core/SATURDAY

java.time.DayOfWeek.SATURDAY

DayOfWeek.SATURDAY

tick.core/SUNDAY

java.time.DayOfWeek.SUNDAY

DayOfWeek.SUNDAY

tick.core/JANUARY

java.time.Month.JANUARY

Month.JANUARY

tick.core/FEBRUARY

java.time.Month.FEBRUARY

Month.FEBRUARY

tick.core/MARCH

java.time.Month.MARCH

Month.MARCH

tick.core/APRIL

java.time.Month.APRIL

Month.APRIL

tick.core/MAY

java.time.Month.MAY

Month.MAY

tick.core/JUNE

java.time.Month.JUNE

Month.JUNE

tick.core/JULY

java.time.Month.JULY

Month.JULY

tick.core/AUGUST

java.time.Month.AUGUST

Month.AUGUST

tick.core/SEPTEMBER

java.time.Month.SEPTEMBER

Month.SEPTEMBER

tick.core/OCTOBER

java.time.Month.OCTOBER

Month.OCTOBER

tick.core/NOVEMBER

java.time.Month.NOVEMBER

Month.NOVEMBER

tick.core/DECEMBER

java.time.Month.DECEMBER

Month.DECEMBER

We can use these constants to compare with = as shown in Example 20.

Example 20. Comparing the day of the week from a date

Is the date 2018-07-09 is a Monday?

(= (t/day-of-week (t/date "2018-07-09")) t/MONDAY)

But is the month May?

(= (t/month (t/date "2018-07-09")) t/MAY)

The ITimeLength protocol provides functions to extract data from Durations & Periods.

Some examples

Code Description (t/seconds (t/new-duration 10 :seconds))
Seconds of a Duration

(t/nanos (t/new-duration 10 :seconds))

Nanos of a Duration

4.6. Comparison

TBD

4.7. Modification

TBD

4.8. Truncation

TBD

5. Durations & periods

A Duration instance stores time as an amount of seconds, for example 5.999999999 seconds.

A Period instance stores amounts of years, months and days, for example -1 years, 20 months and 100 days

The javadocs refer to these entities as time-based and date-based, respectively.

The reason for having both representations is that the Period units are variable length (leap years, DST etc) but the time-based ones are not.

So for example a Duration of 48 hours will not the same span as a Period of 2 days in all contexts.

Note that threeten-extra has an additional PeriodDuration entity

5.1. Construction

Code Description Return type
(t/new-duration 1 :seconds)

Duration of a second

java.time.Duration
(t/new-duration 100 :days)

Duration of 100 days

java.time.Duration
(t/new-period 100 :days)

Period of 100 days

java.time.Period
(t/new-period 2 :months)

Period of 2 months

java.time.Period

Days, Months, Years…

Instances of other java.time types are readily constructed with tick.

Example Description Return type
(day "mon")

Monday

java.time.DayOfWeek
(month "August")

August

java.time.Month
(month 12)

December

java.time.Month
(year-month "2012-12")

December 2012

java.time.YearMonth
(year 1999)

The year 1999

java.time.Year

5.2. Derivation

  • Add durations to durations

You can use "between" to get durations and periods from instants and dates.

(t/between (t/new-date 2023 05 10) (t/new-date 2023 05 15)) ;; => #time/period "P5D"
(t/between #inst "2023-05-10" #inst "2023-05-15") ;; => #time/duration "PT120H"

5.3. Comparison

Tick implements the basic comparison functions =,<,>,⇐ and >= for durations:

(t/< (t/of-hours 5) (t/of-hours 10)) ;; => true

5.4. Misc

Note
TODO Don’t forget you can create zone-offsets from durations!
Note
TODO Don’t forget you can create instants from durations - this is often needed when you get Unix times (e.g. JWT OAuth2 tokens)

The problem with numeric times is that there are cases where the units are in seconds and cases where milliseconds are used. If tick were to convert numbers to times, it would be a source of confusion and bugs if the units were not clear. For this reason, you cannot convert numbers to times. However, you can first create the duration from the number, specifying the units explicitly, and then convert the duration to an instant (or inst).

(t/instant (t/new-duration 1531467976048 :millis))
(t/inst (t/new-duration 1531468976 :seconds))

6. Clocks

In tick, clocks are used for getting the current time, in a given time-zone. You should prefer using clocks to making direct calls to (System/currentTimeMillis), because this then allows you and others to plugin alternative clocks, perhaps for testing purposes.

You create a clock that tracks the current time.

(t/clock)

With an argument, you can fix a clock to always report a fixed time.

(t/clock "1999-12-31T23:59:59")

6.1. Construction

Code Description Return type
(t/clock)

Return a clock that will always return the current time

java.time.Clock

6.2. Derivation

Just like times and dates, you can time-shift clocks forward and backward using the >> and << functions respectively.

Shift a clock to run 2 hours slow.

(t/<< (t/clock) (t/new-duration 2 :hours))
Code Description Return type
(t/<< (t/clock) (t/new-duration 2 :minutes))

Return a clock running 2 minutes slow

java.time.Clock
(t/>> (t/clock) (t/new-duration 2 :minutes))

Return a clock running 2 minutes fast

java.time.Clock

6.3. Mutable Clocks

Sometimes when testing it’s handy to have a mutable clock.

Tick does not provide that because there is already Mock Clock.

That is written in Java. A JS implementation would be possible to do.

6.4. Comparison

Note
TBD

6.5. Atomic clocks?

In Clojure, an atom is a holder of a value at a particular time. Similarly, a tick atom is a clock holding the clock’s time, which is constantly changing.

You create this atom with (atom). Naturally, you can get the instant of the atom’s clock by dereferencing, e.g. @(atom)

user> (def clk (t/atom))
user> (println @clk)
#object[java.time.Instant 0x2e014670 2018-02-28T07:52:52.302Z]
(some time later)
user> (println @clk)
#object[java.time.Instant 0x6e5b1dca 2018-02-28T08:01:50.622Z]

You can also create an atom with a clock.

(let [clk (t/atom (t/clock))]
  @clk)
Code Description Return type
(t/atom)

Return a clock that tracks the current time

java.time.Clock

6.6. Substitution

A clock can be used to callibrate tick to a particular time and time-zone, if system defaults are not desired.

As I’m currently writing this in London, on my system I get the following when I use '(zone)'.

(t/zone)

=> #object[java.time.ZoneRegion 0x744a6545 "Europe/London"]

However, if we wanted to test in New York, we can set the clock to exist in that time-zone:

(t/with-clock (-> (t/clock) (t/in "America/New_York"))
  (t/zone))

=> #object[java.time.ZoneRegion 0x5a9d412 "America/New_York"]

7. Intervals

In tick, an interval is a span of time defined by two points in time, the first being before the second.

Intervals are maps containing both a tick/beginning and a tick/end entry. This flexible design allows any Clojure map to be treated as an interval.

The start and end of an interval should have the same type.

Interval functions are currently alpha status and so reside in the tick.alpha.interval namespace, rather than the main tick.core namespace.

For example, bring in the interval functions like so:

(require '[tick.alpha.interval :as t.i])

7.1. Construction

Obviously, the Clojure’s literal syntax for maps can be used to create intervals.

Here we use a literal map syntax to construct an interval representing the last 5 minutes of 2018 (in UTC).

{:tick/beginning (t/instant "2018-12-31T23:55:00Z")
 :tick/end (t/instant "2019-01-01T00:00:00Z"})

Alternatively, we can use the t.i/new-interval function which takes the two boundaries of the interval as its arguments.

(t.i/new-interval
  (t/instant "2018-12-31T23:55:00Z")
  (t/instant "2019-01-01T00:00:00Z"))

7.2. Derivation

Dates, months and years can also be considered to be themselves ranges, and can be converted to intervals with the t.i/bounds function. The start and end are both inclusive. This means for example that the end of one day meets the start of the next day.

To return today as an interval:

(t.i/bounds (t/today))

The arguments to t.i/new-interval do not have to be instants, they can be any time supported by tick.

To return a 2-day interval spanning midnight this morning to midnight two days from today:

(t.i/new-interval (t/today) (t/tomorrow))

7.3. Comparison

Two intervals can be compared against each other with the t.i/relation function. Allen’s interval algebra tells us there are 13 possible relations between two intervals.

Example 21. Interval relations

Consider the time-span represented by the word 'yesterday' and compare it to the time-span represented by the word 'tomorrow'. Since yesterday is before tomorrow, with a gap between them, we say that yesterday precedes tomorrow:

(t.i/relation (t/yesterday) (t/tomorrow))

If the two intervals touch each other, in the case of 'today' and 'tomorrow', then we say the first interval (today) meets the second interval (tomorrow).

(t.i/relation (t/today) (t/tomorrow))

To see other possible relations, use the slider in the diagram below to move the top interval along:

abc

7.4. Collections

It is often useful to group intervals into collections and have functions operate on those collections.

For example, you may want to gather together:

  • all the time intervals when you were working last week

  • system outages over a given period

  • public holidays and weekends this year

Note
Discuss ordered sequences of disjoint intervals.

7.5. Demonstration

8. Calendars

8.1. Construction

8.2. Derivation

8.3. Comparison

9. Formatting

If it is de/serialization of java.time objects that is needed, then the time-literals library is the right tool for that.

Tick includes a small formatting api over that provided by jsr-310

In ClojureScript, require ns [tick.locale-en-us] to create custom formatters

 (require '[tick.core :as t])

 (t/format :iso-zoned-date-time (t/zoned-date-time))

 (require '[tick.locale-en-us]) ; only need this require for custom format patterns
 ; and it's only needed for cljs, although the ns is cljc
 (t/format (t/formatter "yyyy-MMM-dd") (t/date))

10. Cookbook

10.1. Introduction

This cookbook aims to give some examples of tick being used in different circumstances ranging from the very basic usage to more complex examples.

10.2. Times & dates

Tick is flexible with the way in which times and dates are created; ergo, increasing efficiency. Times and dates can be easily stripped down to smaller modules of time, likewise they can be built up into complete instants.

Caution
Extracting the date from an Instant (or other fields, such as time) you will get a local result.

For example:

(t/date (t/instant "1999-12-31T00:59:59Z"))

The result maybe be #time/date"1999-12-31" or not, depending on your browser timezone (or timezone of the jvm).

To get the date (or other fields) from an Instant in UTC, we must first go via a UTC ZonedDateTime"

(->
   (t/instant "1999-12-31T00:59:59Z")
   (t/in "UTC")
   (t/date))

Create time

A specific time can be produced in multiple ways with varying degrees of precision:

(t/time "12:34")
(t/time "12:34:56.789")
(t/new-time 12 34)
(t/new-time 12 34 56 789000000)

Get the time

To get the current time:

(t/time)
(t/new-time)

Or the time from an instant:

(t/time (t/instant "1999-12-31T00:59:59Z"))

Get the current time in another time-zone:

(t/time (t/in (t/now) "Australia/Darwin"))

Get a specific unit of time:

(t/hour (t/instant "1999-12-31T23:59:59Z"))
(t/minute (t/instant "1999-12-31T23:59:59Z"))
(t/second (t/instant "1999-12-31T23:59:59Z"))

Create a date

Creating dates is done in much the same way as creating time.

(t/date "2000-01-01")
(t/new-date 2000 01 01)

Get the date

To get the current date:

(t/date)
(t/new-date)

Or the date from an instant:

(t/date (t/instant "1999-12-31T23:59:59Z"))

Get the date in another time-zone:

(t/date (t/in (t/instant "1999-12-31T23:59:59Z") "Australia/Darwin"))

Get a specific part of the date:

(t/year (t/instant "1999-12-31T23:59:59Z"))
(t/month (t/instant "1999-12-31T23:59:59Z"))
(t/day-of-month (t/instant "1999-12-31T23:59:59Z"))

Build up times and dates

A unique feature of tick is that you can treat individual units of time as modular, making it easy to build up and break down time into components.

Break up an instant:

(defn instant-breakdown
  "Takes an instant of time and breaks it down into units."
  [t]
  {:day  (t/day-of-week t)
   :month  (t/month t)
   :dd (t/day-of-month t)
   :MM (t/int (t/month t))
   :yyyy (t/int (t/year t))
   :mm (t/minute t)
   :HH (t/hour t)
   :ss (t/second t)})

We can treat the individual units of time as building blocks:

Table 3. Tick Time Blocks
Time Date Zone

(t/time)

(t/date)

(t/zone)

(t/hour)

(t/minute)

(t/second)

(t/year)

(t/month)

(t/day-of-month)

-

-

-

(t/millisecond)

(t/microsecond)

(t/nanosecond)

-

-

-

-

Make up a time

If we want it to be half-past the current hour:

(t/new-time (t/hour (t/instant)) 30)

Or about lunch time:

(t/new-time 13 (t/minute (t/instant)))

Make up a date-time

(t/at (t/date "2018-01-01") (t/time "13:00"))
(t/on (t/time "13:00") (t/date "2018-01-01"))
(-> (t/tomorrow)
    (t/at (t/midnight)))
(-> (t/noon)
    (t/on (t/yesterday)))

Make up a Zoned-Date-Time

(-> (t/tomorrow)
    (t/at (t/midnight))
    (t/in "Europe/Paris"))
(-> (t/tomorrow)
    (t/at (t/midnight))
    (t/in (t/zone)))

Time and Date manipulation

Give a date a set time in the future:

(t/>> (t/date "2000-01-01") (t/new-period 1 :months))
(t/>> (t/date "2000-01-01") (t/new-period 4 :weeks))
(t/>> (t/date "2000-01-01") (t/new-period 30 :days))
(t/>> (t/date "2000-01-01") (t/+ (t/new-period 5 :days)
                                (t/new-period 1 :weeks)
                                (t/new-period 10 :months)))

Or past:

(t/<< (t/date "2000-01-01") (t/new-period 1 :years))

Move around in time:

(t/>> (t/time "12:00") (t/new-duration 5 :minutes))
(t/<< (t/time "12:00") (t/new-duration 5 :hours))
(t/>> (t/time "12:00") (t/+ (t/new-duration 5 :seconds)
                           (t/new-duration 5 :millis)
                           (t/new-duration 5 :micros)
                           (t/new-duration 5 :nanos)))

Increasing a time by a duration of day magnitude will leave the time alone - 12:00 in 5 days is still 12:00 (ignoring daylight savings)

(t/>> (t/time "12:00") (t/new-duration 5 :days))

Truncate time to a desired precision:

(t/truncate (t/time "10:30:59.99") :minutes)

Give the am pm time:

(defn twelve-hour-time
  "Takes a time and gives the 12 hour display"
  [t]
  (let [minute (t/minute t)
        hour (t/hour t)]
    (cond
      (= (t/noon) t)
      "12:00 NOON"

      (>= hour 13)
      (format "%02d:%02d PM" (- hour 12) minute)

      (>= hour 12)
      (format "%02d:%02d PM" hour minute)

      (>= hour 1)
      (format "%02d:%02d AM" hour minute)

      (= hour 0)
      (format "12:%02d AM" minute))))
Note
"12 noon is by definition neither ante meridiem (before noon) nor post meridiem (after noon), then 12 a.m. refers to midnight at the start of the specified day (00:00) and 12 p.m. to midnight at the end of that day (24:00)" - NPL

10.3. Instants and Inst

tick’s default convention is java.time.Instant but caters for projects that use java.util.Date by the conversions above. It is recommended when using tick to keep as an instant for as long as possible.

Creation

To get the current instant:

(t/instant)
(t/now)

Create a specific instant:

(t/instant "2000-01-01T00:00:00.001Z")

Conversions between Inst and Instant

Convert inst to and from instant:

(t/instant (t/inst))
(t/inst (t/instant))

10.4. Time Zones & Offset

Extract a zone from a java.time.ZonedDateTime:

(t/zone (t/zoned-date-time "2000-01-01T00:00:00Z[Europe/Paris]"))
(t/zone)

Create a java.time.ZonedDateTime in a particular time zone:

(t/in (t/instant "2000-01-01T00:00:00.00Z") "Australia/Darwin")

Give the OffsetDateTime instead of ZonedDateTime:

(t/offset-date-time (t/zoned-date-time "2000-01-01T00:00:00Z[Australia/Darwin]"))

Specify the offset for a LocalDateTime:

(t/offset-by (t/date-time "2018-01-01T00:00") 9)

10.5. Intervals

An interval in time is a duration that has a specified beginning and end.

Create an interval

There are multiple ways an interval can be created in tick:

Specify the beginning and the end:

(t.i/new-interval (t/date-time "2000-01-01T00:00")
                (t/date-time "2001-01-01T00:00"))
{:tick/beginning (t/date-time "2000-01-01T00:00")
 :tick/end (t/date-time "2001-01-01T00:00")}
(t.i/bounds (t/year 2000))

All of the above result in the same interval:

(= (t.i/new-interval (t/date-time "2000-01-01T00:00")
                   (t/date-time "2001-01-01T00:00"))
   (t.i/bounds (t/year 2000))
   {:tick/beginning (t/date-time "2000-01-01T00:00")
    :tick/end (t/date-time "2001-01-01T00:00")})

Interval Manipulation:

The duration of an interval can be modified using extend.

Extend an instant to a interval

(t.i/extend (t/instant "2000-01-01T00:00:00.00Z")
  (t/new-period 3 :weeks))

Extend an interval:

(t.i/extend (t.i/bounds (t/year 2000)) (t/new-period 1 :years))

Shorten an interval:

(t.i/extend (t.i/bounds (t/year 2000)) (t/new-period -1 :months))

The beginning of an interval can be modified whilst preserving the duration.

Shift the interval back in time:

(t/<< (t.i/bounds (t/year 2000)) (t/new-period 6 :months))

Or forward in time:

(t/>> (t.i/bounds (t/today)) (t/new-duration 1 :half-days))

10.6. Arithmetic & Shifting operations

The tick library lends itself to doing additions, subtractions and divisions of time chunks and durations. Below are some examples of how time can be treated as a quantity which can be operated on.

Note that the function to find the amount of time between two dates is t/between. Using the legacy Date object it was common to get the epoch offset of two dates and subtract one from the other - so very much arithmetic. tick however sticks to the java.time naming convention of between. Also note that this epoch-offset arithmetic would only ever be applied to 2 dates, not 3, 4, 5 …​ For this reason, t/+ and t/- only work for amounts of time, not dates, in that they can take more than 2 arguments, and so are analagous to their clojure.core equivalents.

Simple maths

Operating on an instant it will return another instant in time.

Shifting forward:

(t/>> (t/now)
     (t/new-duration 15 :minutes))

Shifting backward:

(t/<< (t/now)
     (t/new-duration 10 :days))

An interval has a beginning and an end, operating on it will return a modified interval.

Shifting interval end forward:

(t.i/extend {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
           :tick/end (t/instant "2018-01-10T00:00:00.00Z")}
  (t/new-period 10 :weeks))

Shifting interval end backward:

(t.i/extend {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
           :tick/end (t/instant "2018-01-10T00:00:00.00Z")}
  (t/new-duration -1 :days))

An interval can be divided into smaller intervals:

Divide the day by 24, to get hour long intervals:

(map #(apply t.i/new-interval %)
     (t.i/divide-by 24 {:tick/beginning (t/instant "2000-01-01T00:00:00.00Z")
                      :tick/end (t/instant "2000-01-02T00:00:00.00Z")}))

Or just divide the day by a duration of 1 hour to get the same result:

(= (t.i/divide-by (t/new-duration 1 :hours)
                {:tick/beginning (t/instant "2000-01-01T00:00:00.00Z")
                 :tick/end (t/instant "2000-01-02T00:00:00.00Z")})
   (t.i/divide-by 24
                {:tick/beginning (t/instant "2000-01-01T00:00:00.00Z")
                 :tick/end (t/instant "2000-01-02T00:00:00.00Z")}))

Durations can be treated like independent chunks of time. They can be extended, shrunk and divided.

Addition:

(t/+ (t/new-duration 1 :hours)
     (t/new-duration 10 :minutes))

Subtraction:

(t/- (t/new-duration 1 :hours)
     (t/new-duration 10 :minutes))

Division:

(t.i/divide (t/new-duration 1 :hours)
          (t/new-duration 1 :minutes))

10.7. Countdown timers

Creating a countdown timer greatly depends on the length of time being counted and the accuracy required.

For a simple timer, usually only hours minutes and seconds are required:

(defn countdown-HH-mm-ss
  [end-time]
  (let [duration (tick/duration
                  {:tick/beginning (tick/instant)
                   :tick/end end-time})
        hours (tick/hours duration)
        minutes (tick/minutes (tick/- duration
                                      (tick/new-duration hours :hours)))
        seconds (tick/seconds (tick/- duration
                                      (tick/new-duration minutes :minutes)
                                      (tick/new-duration hours :hours)))]
    (if (tick/< (tick/instant) end-time)
      (format "%02d:%02d:%02d"
              hours minutes seconds)
      "Time's up!")))

For longer durations, counting to high precision is unnecessary. If we are counting down the weeks, knowing how many seconds remain is for the most part meaningless.

(defn countdown-weeks
  [end-time]
  (let [duration (tick/duration
                  {:tick/beginning (tick/instant)
                   :tick/end end-time})
        weeks (long (tick/divide duration (tick/new-duration 7 :days)))
        days (t/days (t/- duration
                          (t/new-duration (* weeks 7) :days)))
        hours (tick/hours (tick/- duration
                                  (t/new-duration (+ days (* weeks 7)) :days)))]
    (if (tick/< (tick/instant) end-time)
      (format "%d weeks, %d days, %d hours"
              weeks days hours)
      "Time's up!")))

If you do not know the units of time that are going to be counted down, you may require a more general countdown function.

(defn countdown-generic
  "Gives a map of the countdown with units of time as keys."
  [end-time]
  (let [duration (tick/duration
                  {:tick/beginning (tick/instant)
                   :tick/end end-time})
        weeks (long (tick/divide duration (tick/new-duration 7 :days)))
        days  (t/days (t/- duration
                           (t/new-duration (* weeks 7) :days)))
        hours (tick/hours (tick/- duration
                                  (t/new-duration (+ days (* weeks 7)) :days)))
        minutes (tick/minutes (tick/- duration
                                      (t/new-duration (+ days (* weeks 7)) :days)
                                      (t/new-duration hours :hours)))
        seconds (tick/seconds (tick/- duration
                                      (t/new-duration (+ days (* weeks 7)) :days)
                                      (t/new-duration hours :hours)
                                      (t/new-duration minutes :minutes)))
        millis (tick/millis (tick/- duration
                                    (t/new-duration (+ days (* weeks 7)) :days)
                                    (t/new-duration hours :hours)
                                    (t/new-duration minutes :minutes)
                                    (t/new-duration seconds :seconds)))]
    (if (tick/< (tick/instant) end-time)
      {:counting true
       :weeks weeks
       :days days
       :hours hours
       :minutes minutes
       :seconds seconds
       :milliseconds millis}
      {:counting false})))

It may be required that the time since an event is calculated. In this can be done in a very similar way to counting down:

(defn count-up
  "Gives the time since an event in the most appropriate units of time"
  [event]
  (let [duration (tick/duration
                  {:tick/beginning event
                   :tick/end (tick/instant)})
        years (long (tick/divide duration (tick/new-duration 365 :days)))
        months (long (tick/divide duration (tick/new-duration (/ 365 12) :days)))
        weeks (long (tick/divide duration (tick/new-duration 7 :days)))]
    (cond
      (> (t/days duration) 365)
      (format "%d years" years)

      (and (<= (t/days duration) 365) (> (t/days duration) (/ 365 12)))
      (format "%d months" months)

      (and (<= (t/days duration) (/ 365 12)) (> (t/days duration) 7))
      (format "%d weeks" weeks)

      (and (<= (t/days duration) 7) (> (t/days duration) 1))
      (format "%d days" (t/days duration))

      (and (<= (t/days duration) 1) (> (t/hours duration) 1))
      (format "%d hours %d" (t/hours duration))

      (and (<= (t/hours duration) 1) (> (t/minutes duration) 1))
      (format "%d minutes %d" (t/minutes duration))

      (and (<= (t/minutes duration) 1) (> (t/seconds duration) 1))
      (format "%d seconds" (t/seconds duration))

      (tick/< (tick/instant) event)
      "Event hasn't happened yet")))
Caution
These timers have lower accuracy at higher precisions - they do not account for leap seconds or years.

10.8. Miscellaneous

These examples don’t have a home yet.

Check if an expiration has passed:

(let [expiry (t/instant "2018-01-01T00:00:00.00Z")]
  (t/> (t/now)
       expiry))

Return a sequence of dates between two given dates a with a specified jump between each.

For instance, to get a sequence of the first day of each month in a given year:

(let [intvl (t.i/bounds (t/year))]
  (t/range
    (t/beginning intvl)
    (t/end intvl)
    (t/new-period 1 :months)))

Get the time difference between two instances:

(t/between (t/now) (t/epoch))

10.9. Tick Reference

This section gives, in alphabetical order, examples of how to use each function in the tick api library.

+ - Sum amounts of time:

(t/+ (t/new-duration 10 :hours)
     (t/new-duration 10 :minutes))

- - Subtract amounts of time:

(t/- (t/new-duration 12 :hours)
     (t/new-duration 10 :hours))

<< - Go back in time a duration:

(t/<< (t/now) (t/new-period 10 :weeks))

>> - Go forward in time a duration:

(t/>> (t/now) (t/new-duration 10 :hours))

< - Before?

(t/< (t/instant "2000-01-01T00:00:00.00Z") (t/instant "2018-01-01T00:00:00.00Z"))

> - After?

(t/> (t/instant "2000-01-01T00:00:00.00Z") (t/instant "2018-01-01T00:00:00.00Z"))

<= - Before or same time?

(t/<= (t/instant "2000-01-01T00:00:00.00Z") (t/instant "2018-01-01T00:00:00.00Z"))

>= - After or same time?

(t/>= (t/instant "2000-01-01T00:00:00.00Z") (t/instant "2018-01-01T00:00:00.00Z"))

ago - Give the time a duration ago:

(t/ago (t/new-duration 1 :hours))

am - Create an interval spanning the morning of a given date:

(t.i/am (t/date "2018-01-01"))

at - Make date-time from a date (given date at "time"):

(-> (t/date "2000-01-01") (t/at "00:00"))

atom - Create an holder for the current time:

(let [clk (t/atom)]
  (t/time @clk))

beginning - Give the beginning of an interval:

(t/beginning (t/today))

between - Give a value for the duration between two instances:

(t/between (t/instant "2000-01-01T00:00:00.00Z") (t/instant "2018-01-01T00:00:00.00Z"))

bounds - Give the beginning and end of an interval:

(t.i/bounds (t/yesterday))

clock - Create a system clock or fixed clock:

(t/clock)
(t/clock (t/instant "2018-01-01T00:00:00.00Z"))

coincident? - Are two intervals the same?

(t/coincident? (t/today) (t/today))

complement - Give the inverse of a list of intervals.

(t.i/complement [(t/instant "2010-01-01T00:00:00.00Z")
	       (t/instant "2010-02-01T00:00:00.00Z")
	       (t/instant "2010-03-01T00:00:00.00Z")])

concur - Get the common interval (if any) of two intervals.

(t.i/concur (t/today) (t.i/new-interval (t/yesterday) (t/tomorrow)))

concurrencies - A sequence of times when intervals overlap:

(t.i/concurrencies (t.i/pm (t/today)) (t.i/new-interval (t/today) (t/tomorrow)))

conj - Return new collection with the original collection including a new date:

(t.i/conj [(t/yesterday) (t/today)] (t/tomorrow))

date - Get the date:

(t/date)
(t/tomorrow)
(t/date (t/instant "2018-01-01T00:00:00.00Z"))
(t/date "2018-01-01")

date-time - Get the date-time:

(t/date-time)
(t/date-time (t/instant "2018-01-01T00:00:00.00Z"))
(t/date-time "2018-01-01T00:00")

day-of-month - Get the numerical day of the month:

(t/day-of-month)
(t/day-of-month "2018-01-01")
(t/day-of-month (t/tomorrow))
(t/day-of-month (t/instant "2018-01-01T00:00:00.00Z"))

day-of-week - Get the day of the week

(t/day-of-week)
(t/day-of-week (t/yesterday))
(t/day-of-week (t/instant "2018-01-01T00:00:00.00Z"))
(t/day-of-week "2018-01-01")

days - Convert a duration into days

(t/days (t/new-duration 24 :hours))

dec - Give the previous year:

(t/dec (t/year))

difference - Return the first collection without the second collection elements.

(t.i/difference [(t/yesterday) (t/today) (t/tomorrow)] [(t/today)])

divide - Divide a duration by another duration:

(t.i/divide (t/new-duration 1 :days) (t/new-duration 1 :hours))

divide-by - Split an interval into even sections of time:

(t.i/divide-by 10 {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
	         :tick/end (t/instant "2018-01-10T00:00:00.00Z")})

duration - Give the duration of an interval:

(t/duration {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
	     :tick/end (t/instant "2018-01-10T00:00:00.00Z")})

end - Give the end instance of an interval:

(t/end {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
	:tick/end (t/instant "2018-01-10T00:00:00.00Z")})

epoch - Give the epoch:

(t/epoch)

extend - Extend an interval by a duration:

(t.i/extend {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
           :tick/end (t/instant "2018-01-10T00:00:00.00Z")}
  (t/new-period 10 :weeks))

fields - Get the field for a given time value:

(t/fields (t/time))

group-by - Group a collection of intervals by a given function.

(t.i/group-by t/year
            (map #(apply t.i/new-interval %)
                 (t.i/divide-by (t/new-duration 1 :days)
                              (t.i/new-interval (t/new-date 2000 12 29)
                                              (t/new-date 2001 1 2)))))

hence - Return an instant 15 minutes from now:

(t/hence (t/new-duration 15 :minutes))

hour - Give the hour of the day:

(t/hour (t/now))

hours - Give a duration in hours:

(t/hours (t/new-duration 2 :days))

in - Give an instance in a time zone:

(t/in (t/instant "2018-01-01T10:00:00.00Z") "Australia/Darwin")

inc - Give the next year:

(t/inc (t/year))

inst - Convert to java.util.Date:

(t/inst)
(t/inst (t/instant "2018-01-01T00:00:00.00Z"))

instant - Convert to java.time.Instant:

(t/instant (t/inst))
(t/instant (t/zoned-date-time "2018-01-01T00:00:00.000+09:30[Australia/Darwin]"))

int - Give the year as an integer:

(t/int (t/year))

intersection - Give the intersection of two sequences:

(t.i/intersection [(t/year)]
                [(t/date "2019-01-01")
                 (t/date "2020-01-01")
                 (t/date "2021-01-01")])

intersects? - Does an interval intersect with another? Return nil if not, or the intersection if so.

(t.i/intersects? [(t/year)]
               (t/inc (t/year)))
(t.i/intersects? [(t/year)]
                (t/today))

long - return an instant as a long:

(t/long (t/instant))

max - Give the last chronological date from multiple unordered dates:

(t/max (t/today) (t/tomorrow) (t/yesterday) (t/new-date 2018 11 11))

micros - Give a duration in microseconds:

(t/micros (t/new-duration 5 :minutes))

microsecond - Give the microsecond of an instant:

(t/microsecond (t/now))

midnight - Time at midnight:

(t/midnight)

midnight? - Is it midnight?

(t/midnight? (t/date-time))

millis - Give a duration in milliseconds:

(t/millis (t/new-duration 5 :minutes))

millisecond - Give the millisecond of an instant.

(t/millisecond (t/now))

min - Give the first chronological date from an unordered list of dates:

(t/min (t/today) (t/tomorrow) (t/yesterday) (t/new-date 2018 11 11))

minutes - Give a duration in minutes:

(t/minutes (t/new-duration 5 :hours))

minute - Give the minute of an instant:

(t/minute (t/now))

month - Get the month:

(t/month)
(t/month "2018-11-11")

months - Get the number of months in a duration:

(t/months (t/new-period 10 :months))

nanos - Get the number of nanoseconds in a duration:

(t/nanos (t/new-duration 5 :minutes))

nanosecond - Get the nanosecond of the time:

(t/nanosecond (t/now))

new-date - Created a new java.time.LocalDate

(t/new-date 2000 01 01)
(t/new-date)

t/new-duration - Create a new duration.

(t/new-duration 10 :minutes)
Note
new-duration units are from nanos to days. For date-based units of time, see new-period.

new-interval - Create a new interval: a map with a :tick/beginning and a :tick/end

(t.i/new-interval (t/instant "2018-01-01T00:00:00.00Z") (t/instant "2019-01-01T00:00:00.00Z"))

new-period - Create a new duration:

(t/new-period 10 :weeks)
Note
new period is in units greater than days. For time-based units, see new-duration.

new-time - Create a new java.time.LocalTime

(t/new-time)
(t/new-time 12 00)

noon - Give the time at noon.

(t/noon)

normalize - From a time ordered sequence of disjointed intervals, return a sequence of interval groups:

(count (t.i/normalize [(t.i/new-interval (t/instant "2000-01-01T00:00:00.00Z")
	     		             (t/instant "2000-01-02T00:00:00.00Z"))
                     (t.i/new-interval (t/instant "2000-01-02T00:00:00.00Z")
	      		             (t/instant "2000-02-02T00:00:00.00Z"))
                     (t.i/new-interval (t/instant "2000-06-01T00:00:00.00Z")
	      		             (t/instant "2000-06-09T00:00:00.00Z"))]))

now - Give now as an instant:

(t/now)

offset-by - Give a java.time.OffsetDateTime of a specified instant with a specified offset:

(t/offset-by (t/now) 1)

offset-date-time - Convert a java.time.Instant to a java.time.OffsetDateTime

(t/offset-date-time (t/zoned-date-time "2000-01-01T00:00:00Z[Australia/Darwin]"))
(t/offset-date-time)

on - Give a LocalDateTime for a given time on a given date:

(t/on (t/new-time 11 00) (t/new-date 2000 01 01))

ordered-disjoint-intervals? - Are the given intervals time-ordered and disjointed?

(t.i/ordered-disjoint-intervals? [(t.i/new-interval (t/instant "2000-01-01T00:00:00.00Z")
			       			(t/instant "2000-01-02T00:00:00.00Z"))
				(t.i/new-interval (t/instant "2000-01-02T00:00:00.00Z")
						(t/instant "2000-02-02T00:00:00.00Z"))])

pm - Give an interval covering the afternoon of a given date:

(t.i/pm (t/today))

range - Give a lazy sequence of times from start to finish.

(t/range (t/date-time "2000-01-01T12:00")
	 (t/date-time "2000-01-01T12:05")
	 (t/new-duration 1 :minutes))

relation - Show the relation of two intervals:

(t.i/relation {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
	     :tick/end (t/instant "2019-01-01T00:00:00.00Z")}
            {:tick/beginning (t/instant "2018-06-01T00:00:00.00Z")
	     :tick/end (t/instant "2019-06-01T00:00:00.00Z")})
(t.i/relation (t/today) (t/yesterday))
Note
see above for more.

reset! - Reset an atom clock with one 5 mins slow.

(let [clk  (t/atom)
      clk2 (t/atom (t/<< (t/clock) (t/new-duration 5 :minutes)))]
     (t/reset! clk clk2))

reset-vals! - Reset an atom clock with one 5 mins slow, and give the value of before and after.

(let [clk  (t/atom)
      clk2 (t/atom (t/<< (t/clock) (t/new-duration 5 :minutes)))]
     (t/reset-vals! clk clk2))

scale - Increase an interval by a given duration:

(t.i/scale {:tick/beginning (t/instant "2018-01-01T00:00:00.00Z")
	  :tick/end (t/instant "2019-01-01T00:00:00.00Z")}
         (t/new-duration 10 :minutes))

seconds - Give a duration in seconds:

(t/seconds (t/new-duration 5 :minutes))

second - Give the second of time:

(t/second (t/now))

time - Give the time of an instant:

(t/time (t/now))

today - Give todays date:

(t/today)

tomorrow - Give tomorrows date:

(t/tomorrow)

truncate - Lessen precision of an instant:

(t/truncate (t/instant) :days)

union - Join two time-ordered sequences together into one ordered sequence

(t.i/union [(t/today)] [(t/yesterday) (t/tomorrow)])

unit-map - A map of units of time that tick works with:

(keys t/unit-map)
(:minutes t/unit-map)

unite - Unite concurrent intervals ordered by beginning:

(t.i/unite [(t.i/new-interval (t/instant "2000-01-01T00:00:00.00Z")
	 		  (t/instant "2000-01-02T00:00:00.00Z"))
	  (t.i/new-interval (t/instant "2000-01-01T00:00:00.00Z")
	  		  (t/instant "2000-02-02T00:00:00.00Z"))])

units - Give the appropriate units for a duration:

(t/units (t/new-duration 1000000001 :nanos))

with - Return a date with an altered field:

(t/with (t/today) :day-of-month 1)

with-clock - macro to evaluate code in the context of a given clock:

(let [clk (t/>> (t/clock) (t/new-duration 5 :minutes))]
  (t/with-clock clk (t/time)))

year - Give the year:

(t/year)
(t/year (t/date "2000-01-01"))

year-month - Give the year and month:

(t/year-month)
(t/year-month (t/date "2000-01-01"))

years - Give a duration in years.

(t/years (t/new-period 10 :years))

yesterday - Give yesterdays date:

(t/yesterday)

zone - Give the time zone:

(t/zone)
(t/zone (t/zoned-date-time "2000-01-01T00:00:00Z[Australia/Darwin]"))

zone-offset - Give the java.time.ZoneOffset with a specified offset:

(t/zone-offset 1 30 59)

zoned-date-time - Create a java.time.ZonedDateTime:

(t/zoned-date-time)
(t/zoned-date-time "2000-01-01T00:00:00Z[Australia/Darwin]")

References


1. iso-8601,Dates are so often subject to regional conventions. Therefore in tick, we always parse and format dates according to ISO 8601.
2. From reify at dictionary.com

Copyright © 2018, JUXT LTD. Version: 0.5.0-RC5. Last modified on 2022-04-20.