1. Introduction
Time is an illusion. Lunchtime doubly so.
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
.
There are some extra considerations when using tick from Clojurescript.
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:
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":
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.
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:
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:
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:
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.
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.
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.
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.
|
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.
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.
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
.
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)
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).
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:
(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
.
You can think of a time (or a date) as being a partially defined date-time.
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.
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 instant
s, inst
s (java.util.Date
, JavaScript’s Date
),
zoned-date-time
s and offset-date-time
s, 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))
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.
Convert between | to instant | to offset-date-time | to zoned-date-time | to inst |
---|---|---|---|---|
from instant |
identity |
|
|
|
from offset-date-time |
|
identity |
|
|
from zoned-date-time |
|
|
identity |
|
from inst |
|
|
|
identity |
from String |
parse |
|
|
parse, then |
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.
(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.
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.
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.
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:
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]")