docs
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
parent directory.. | ||||
<!DOCTYPE html PUBLIC ""
"">
<html><head><meta charset="UTF-8" /><title>Clojure.Java-Time</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Clojure.java-time</span> <span class="project-version">0.3.0</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="README.html"><div class="inner"><span>Clojure.Java-Time</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><a href="java-time.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>java-time</span></div></a></li><li class="depth-2"><a href="java-time.repl.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>repl</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#clojure-java-time" name="clojure-java-time"></a>Clojure.Java-Time</h1>
<p><a href="https://travis-ci.org/dm3/clojure.java-time"><img src="https://travis-ci.org/dm3/clojure.java-time.png?branch=master" alt="Build Status" /></a></p>
<p>A Clojure wrapper for Java 8 Date-Time API.</p>
<h2><a href="#rationale" name="rationale"></a>Rationale</h2>
<p>Main goals:</p>
<ul>
<li>Provide a consistent API for common operations with instants, date-times, zones and periods.</li>
<li>Provide an escape hatch from Java types to clojure data structures.</li>
<li>Avoid reflective calls.</li>
<li>Provide an entry point into Java-Time by freeing the user from importing most of the Java-Time classes.</li>
</ul>
<p>Why use Clojure.Java-Time over <a href="https://github.com/clj-time/clj-time">clj-time</a> or <a href="https://github.com/dm3/clojure.joda-time">Clojure.Joda-Time</a>?</p>
<ul>
<li>You don’t want to have a dependency on the Joda-Time library</li>
<li>You already use Java 8</li>
<li>You prefer as little Java interop code as possible</li>
</ul>
<p>This library employs a structured and comprehensive approach to exposing the Java 8 Date-Time API to the Clojure world. It’s very similar to Clojure.Joda-Time in its design goals and overall feeling, so if you ever used that you will feel at home!</p>
<h2><a href="#documentation" name="documentation"></a>Documentation</h2>
<ul>
<li><a href="http://dm3.github.io/clojure.java-time/">API</a></li>
</ul>
<h2><a href="#whats-different-in-java-time-api-" name="whats-different-in-java-time-api-"></a>What’s different in Java Time API?</h2>
<p>If you already used Joda Time before you might think: “What in the world could they do better?”. After all, Joda-Time already provides a pretty comprehensive set of tools for dealing with time-related concepts. Turns out, it’s a tad more complicated than it has to be. Also, a few concepts have faulty designs which lead to hard to fix bugs and misuse. You can see the birds-eye view of changes and some of the rationale on the authors’ (Stephen Colebourne) blog:</p>
<ul>
<li><a href="http://blog.joda.org/2009/11/why-jsr-310-isn-joda-time_4941.html">what’s wrong with Joda-Time</a>,</li>
<li><a href="http://blog.joda.org/2014/07/threeten-backport-vs-joda-time.html">when you should use Java-Time</a></li>
<li><a href="http://blog.joda.org/2014/11/converting-from-joda-time-to-javatime.html">what’s different in Java-Time</a>.</li>
</ul>
<p>You can also take a look at a <a href="http://time4j.net/tutorial/appendix.html">comprehensive comparison</a> by the <a href="http://time4j.net/">Time4J</a> authors.</p>
<h2><a href="#usage" name="usage"></a>Usage</h2>
<p>Add the following dependency to your <code>project.clj</code> or <code>build.boot</code>:</p>
<pre><code class="clj">[clojure.java-time "0.3.0"]
</code></pre>
<p>The <a href="http://dm3.github.io/clojure.java-time/">API</a> of the Clojure.Java-Time consists of one namespace, namely <code>java-time</code>. For the purposes of this guide, we will <code>use</code> the main namespace:</p>
<pre><code class="clj">(refer-clojure :exclude [range iterate format max min])
(use 'java-time)
</code></pre>
<h3><a href="#concept-run-through" name="concept-run-through"></a>Concept run-through</h3>
<p>Java Time API may seem daunting. Instead of a single <code>java.util.Date</code> you have a <code>ZonedDateTime</code>, <code>OffsetDateTime</code>, <code>LocalDateTime</code>, <code>Instant</code>, and other types. You would be well served by reading the official documentation for the <a href="https://docs.oracle.com/javase/tutorial/datetime/iso/index.html">Java Time API</a>, but we’ll also do a quick run-through here.</p>
<h4><a href="#local-dates" name="local-dates"></a>Local Dates</h4>
<p><code>LocalDate</code>, <code>LocalTime</code> and <code>LocalDateTime</code> are used to represent a date, time and date-time respectively without an offset or a timezone. The local time entities are used to represent human-based dates/times. They are a good fit for representing the time of various events:</p>
<ul>
<li><code>LocalDate</code> - birthday, holiday</li>
<li><code>LocalTime</code> - bus schedule, opening time of a shop</li>
<li><code>LocalDateTime</code> - start of a competition</li>
</ul>
<p>A local date/time can be created as you’d expect:</p>
<pre><code class="clj">(local-date 2015 10)
=> #<java.time.LocalDate 2015-10-01>
(local-time 10)
=> #<java.time.LocalTime 10:00>
(local-date-time 2015 10)
=> #<java.time.LocalDateTime 2015-10-01T00:00>
</code></pre>
<h4><a href="#zoned-dates" name="zoned-dates"></a>Zoned Dates</h4>
<p>There are two types which deal with zones: <code>OffsetDateTime</code> and <code>ZonedDateTime</code>. They do pretty much what you would expect from their name. You can think of the <code>Offset</code> time as a more concrete version of the <code>Zoned</code> time. For example, the same timezone can have different offsets throughout the year due to DST or governmental regulations.</p>
<pre><code class="clj">(offset-time 10)
=> #<java.time.OffsetTime 10:00+01:00>
(offset-date-time 2015 10)
=> #<java.time.OffsetDateTime 2015-10-01T10:00+01:00>
(zoned-date-time 2015 10)
=> #<java.time.ZonedDateTime 2015-10-01T10:00+01:00[Europe/London]>
</code></pre>
<p>Offset/Zone times only take the offset/zone as the last arguments for the maximum arity constructor. You can influence the zone/offset by using the <code>with-zone</code> or <code>with-offset</code> functions, like so:</p>
<pre><code class="clj">(with-zone (zoned-date-time 2015 10) "UTC")
=> #<java.time.ZonedDateTime 2015-10-01T00:00Z[UTC]>
(with-zone-same-instant (zoned-date-time 2015 10) "UTC")
=> #<java.time.ZonedDateTime 2015-09-30T23:00Z[UTC]>
(with-clock (system-clock "UTC")
(zoned-date-time 2015 10))
=> #<java.time.ZonedDateTime 2015-10-01T00:00Z[UTC]>
</code></pre>
<h4><a href="#instant" name="instant"></a>Instant</h4>
<p>An <code>Instant</code> is used to generate a time stamp representing machine time. It doesn’t have an offset or a time zone. You can think of it as of a number of milliseconds since epoch (<code>1970-01-01T00:00:00Z</code>). An instant is directly analogous to <code>java.util.Date</code>:</p>
<pre><code class="clj">user=> (instant)
#<java.time.Instant "2015-09-26T05:25:48.667Z">
user=> (java.util.Date.)
#inst "2015-09-26T05:25:50.118-00:00"
</code></pre>
<p>Every other date entity can be converted to an instant (local ones will require an additional zone information).</p>
<h4><a href="#period-and-duration" name="period-and-duration"></a>Period and Duration</h4>
<p>Java Time Period entities are considerably simpler than the Joda-Time periods. They are fixed containers of years, months and days. You can use them to represent any period of time with a granularity larger or equal to a single day. Duration, on the other hand, represents a standard duration less than or equal to a single standard (24-hour) day.</p>
<h3><a href="#caution" name="caution"></a>Caution</h3>
<p>The current incarnation of the library is quite slow while calling the 2-3 arity <code>zoned-date-time/offset-time/offset-date-time</code> constructors. If you need predictable latency on the first call, please warm the constructors you are going to use by using them in a ‘warm-up phase’, e.g.:</p>
<pre><code class="clj">(defn warm-up []
(zoned-date-time 2015 1 1)
(zoned-date-time 2015 1)
(zoned-date-time 2015))
</code></pre>
<p>Only the types of the arguments matter, not the values!</p>
<h3><a href="#an-appetizer" name="an-appetizer"></a>An appetizer</h3>
<p>First, let’s do a quick run through common use cases.</p>
<p>What is the current date?</p>
<pre><code class="clj">(def now (local-date))
=> #object[java.time.LocalDate "2015-09-27"]
</code></pre>
<p>What’s the next day?</p>
<pre><code class="clj">(plus now (days 1))
=> #object[java.time.LocalDate "2015-09-28"]
</code></pre>
<p>The previous day?</p>
<pre><code class="clj">(minus now (days 1))
=> #object[java.time.LocalDate "2015-09-28"]
</code></pre>
<p>Three next days?</p>
<pre><code class="clj">(take 3 (iterate plus now (days 1)))
=> (#object[java.time.LocalDate "2015-09-28"]
#object[java.time.LocalDate "2015-09-29"]
#object[java.time.LocalDate "2015-09-30"])
</code></pre>
<p>When is the first Monday in month?</p>
<pre><code class="clj">(adjust now :first-in-month :monday)
=> #object[java.time.LocalDate "2015-09-07"]
</code></pre>
<p>Date with some of its fields truncated:</p>
<pre><code class="clj">(truncate-to (local-date-time 2015 9 28 10 15) :days)
=> #object[java.time.LocalDateTime "2015-09-28T00:00"]
</code></pre>
<p>Date-time adjusted to the given hour:</p>
<pre><code class="clj">(adjust (local-date-time 2015 9 28 10 15) (local-time 6))
=> #object[java.time.LocalDateTime "2015-09-28T06:00"]
</code></pre>
<p>The latest of the given dates?</p>
<pre><code class="clj">(max (local-date 2015 9 20) (local-date 2015 9 28) (local-date 2015 9 1))
=> #object[java.time.LocalDate "2015-09-28"]
</code></pre>
<p>The shortest of the given durations?</p>
<pre><code class="clj">(min (duration 10 :seconds) (duration 5 :hours) (duration 3000 :millis))
=> #object[java.time.Duration "PT3S"]
</code></pre>
<p>Get the year field out of the date:</p>
<pre><code class="clj">(as (local-date 2015 9 28) :year)
=> 2015
</code></pre>
<p>Get multiple fields:</p>
<pre><code class="clj">(as (local-date 2015 9 28) :year :month-of-year :day-of-month)
=> (2015 9 28)
</code></pre>
<p>Get the duration in a different unit:</p>
<pre><code class="clj">java-time> (plus (hours 3) (minutes 2))
#object[java.time.Duration "PT3H2M"]
java-time> (as *1 :minutes)
182
</code></pre>
<p>Format a date:</p>
<pre><code class="clj">(format "MM/dd" (zoned-date-time 2015 9 28))
=> "09/28"
</code></pre>
<p>Parse a date:</p>
<pre><code class="clj">(local-date "MM/yyyy/dd" "09/2015/28")
=> #object[java.time.LocalDate "2015-09-28"]
</code></pre>
<p>Zoned date-times and offset date-times/times always take the zone/offset as the last argument. Offsets can be specified as float values:</p>
<pre><code class="clj">(zone-offset +1.5)
=> #<java.time.ZoneOffset +01:30>
(zone-offset -1.5)
=> #<java.time.ZoneOffset -01:30>
</code></pre>
<h4><a href="#conversions" name="conversions"></a>Conversions</h4>
<p>Time entities can be converted to other time entities if the target contains less information, e.g. (assuming we’re in UTC timezone):</p>
<pre><code class="clj">(zoned-date-time (offset-date-time 2015 9 28 1))
=> #object[java.time.ZonedDateTime "2015-09-28T01:00Z"]
(instant (offset-date-time 2015 9 28 1))
=> #object[java.time.Instant "2015-09-28T01:00:00Z"]
(offset-time (offset-date-time 2015 9 28 1))
=> #object[java.time.OffsetTime "01:00Z"]
(local-date-time (offset-date-time 2015 9 28 1))
=> #object[java.time.LocalDateTime "2015-09-28T01:00"]
(local-time (offset-time 1))
=> #object[java.time.LocalTime 0x3a3cd6d5 "01:00"]
</code></pre>
<h4><a href="#legacy-date-time-types" name="legacy-date-time-types"></a>Legacy Date-Time Types</h4>
<p>Any date which can be converted to an instant, can also be converted to a <code>java.util.Date</code>:</p>
<pre><code class="clojure">(java-date (zoned-date-time 2015 9 28))
=> #inst "2015-09-27T22:00:00.000-00:00"
(java-date 50000)
=> #inst "1970-01-01T00:00:50.000-00:00"
</code></pre>
<p>An instance of <code>java.util.Date</code> serves the same purpose as the new <code>java.time.Instant</code>. It’s a machine timestamp which isn’t aware of the timezone. Please, do not get confused by the way it is printed by the Clojure printer - the UTC timezone is applied during formatting.</p>
<p>Sometimes you’ll have to work with the legacy <code>java.sql.Date/Time/Timestamp</code> types. The correspondence between the legacy types and the new Date-Time entities is as follows:</p>
<ul>
<li><code>java.time.LocalDate</code> - <code>java.sql.Date</code></li>
<li><code>java.time.LocalDateTime</code> - <code>java.sql.Timestamp</code></li>
<li><code>java.time.LocalTime</code> - <code>java.sql.Time</code></li>
</ul>
<pre><code class="clojure">(sql-date 2015 9 28)
=> #inst "2015-09-27T22:00:00.000-00:00"
(sql-timestamp 2015 9 28 10 20 30 4000000)
=> #inst "2015-09-28T09:20:30.004-00:00"
(sql-time 10 20 30)
=> #inst "1970-01-01T09:20:30.000-00:00"
</code></pre>
<p>The results of the above calls get printed as <code>#inst</code> because all of the <code>java.sql.Date/Time/Timestamp</code> are subtypes of <code>java.util.Date</code>. Coincidentally, this makes it impossible to plug the <code>java.sql.*</code> types into the Clojure.Java-Time conversion graph.</p>
<p>Conversions to the legacy types also go the other way around:</p>
<pre><code class="clojure">(j/local-date (j/sql-date 2015 9 28))
#object[java.time.LocalDate "2015-09-28"]
(j/local-date-time (j/sql-timestamp 2015 9 28 10 20 30 4000000))
#object[java.time.LocalDateTime "2015-09-28T10:20:30.004"]
(j/local-time (j/sql-time 10 20 30))
#object[java.time.LocalTime "10:20:30"]
</code></pre>
<h4><a href="#three-ten-extra" name="three-ten-extra"></a>Three-Ten Extra</h4>
<p>If you add an optional <code>[org.threeten/threeten-extra "0.9"]</code> dependency to the project, you will get an <code>Interval</code>, <code>AmPm</code>, <code>DayOfMonth</code>, <code>DayOfYear</code>, <code>Quarter</code> and <code>YearQuarter</code> data types as well as a couple more adjusters.</p>
<p>An interval can be constructed from two entities that can be converted to instants:</p>
<pre><code class="clojure">(interval (offset-date-time 2015 1 1) (zoned-date-time 2016 1 1))
=> #<org.threeten.extra.Interval 2015-01-01T00:00:00Z/2016-01-01T00:00:00Z>
(move-start-by *1 (duration 5 :days))
=> #<org.threeten.extra.Interval 2015-01-06T00:00:00Z/2016-01-01T00:00:00Z>
(move-end-by *1 (duration 5 :days))
=> #<org.threeten.extra.Interval 2015-01-06T00:00:00Z/2016-01-06T00:00:00Z>
(contains? *1 (offset-date-time 2015 1 1))
=> false
</code></pre>
<h4><a href="#joda-time" name="joda-time"></a>Joda-Time</h4>
<p>Bonus! if you have Joda Time on the classpath (either directly, or via <code>clj-time</code>), you can seamlessly convert from Joda Time to Java Time types:</p>
<pre><code class="clojure">(java-time.repl/show-path org.joda.time.DateTime java.time.OffsetTime)
=> {:cost 2.0,
:path [[#<java_time.graph.Types@15e43c24 [org.joda.time.DateTime]>
#<java_time.graph.Types@78a2235c [java.time.Instant java.time.ZoneId]>]
[#<java_time.graph.Types@6d8ded1a [java.time.Instant java.time.ZoneId]>
#<java_time.graph.Types@5360f6ae [java.time.OffsetTime]>]]}
(offset-time (org.joda.time.DateTime/now))
=> #<java.time.OffsetTime 22:00:00.000000000-00:00>
</code></pre>
<h4><a href="#clocks" name="clocks"></a>Clocks</h4>
<p>Java Time introduced a concept of <code>Clock</code> - a time entity which can seed the dates, times and zones. However, there’s no built-in facility which would allow you to influence the date-times create using default constructors ala Joda’s <code>DateTimeUtils/setCurrentMillisSystem</code>. Clojure.Java-Time tries to fix that with the <code>with-clock</code> macro and the corresponding <code>with-clock-fn</code> function:</p>
<pre><code class="clojure">(zone-id)
=> #<java.time.ZoneRegion Europe/London>
(with-clock (system-clock "UTC")
(zone-id))
=> #<java.time.ZoneRegion UTC>
</code></pre>
<p>Clock overrides works for all of the date-time types.</p>
<h4><a href="#fields-units-and-properties" name="fields-units-and-properties"></a>Fields, Units and Properties</h4>
<p>Date-Time entities are composed of date fields, while Duration entities are composed of time units. You can see all of the predefined fields and units via the <code>java-time.repl</code> ns:</p>
<pre><code class="clojure">(java-time.repl/show-fields)
=> (:aligned-day-of-week-in-month
:aligned-day-of-week-in-year
:aligned-week-of-month
:aligned-week-of-year
:am-pm-of-day
:clock-hour-of-am-pm
...)
</code></pre>
<pre><code class="clojure">(java-time.repl/show-units)
=> (:centuries
:days
:decades
:eras
:forever
:half-days
...)
</code></pre>
<p>You can obtain any field/unit like this:</p>
<pre><code class="clojure">(field :year)
=> #object[java.time.temporal.ChronoField "Year"]
(unit :days)
=> #object[java.time.temporal.ChronoUnit "Days"]
(field (local-date 2015) :year)
=> #object[java.time.temporal.ChronoField "Year"]
</code></pre>
<p>You can obtain all of the fields/units of the temporal entity:</p>
<pre><code class="clojure">(fields (local-date))
=> {:proleptic-month #object[java.time.temporal.ChronoField ...}
(units (duration))
=> {:seconds #object[java.time.temporal.ChronoUnit "Seconds"],
:nanos #object[java.time.temporal.ChronoUnit "Nanos"]}
</code></pre>
<p>By themselves the fields and units aren’t very interesting. You can get the range of valid values for a field and a duration between two dates, but that’s about it:</p>
<pre><code class="clojure">(range (field :year))
=> #object[java.time.temporal.ValueRange "-999999999 - 999999999"]
(range (field :day-of-month))
=> #object[java.time.temporal.ValueRange "1 - 28/31"]
(time-between (local-date 2015 9) (local-date 2015 9 28) :days)
=> 27
</code></pre>
<p>Fields and units become interesting in conjunction with properties. Java-Time doesn’t support the concept of properties which is present in Joda-Time. There are reasons for that which I feel are only valid in a statically-typed API like Java’s. In Clojure, properties allow expressing time entity modifications and queries uniformly across all of the entity types.</p>
<pre><code class="clojure">(def prop (property (local-date 2015 2 28) :day-of-month))
=> #java_time.temporal.TemporalFieldProperty{...}
(value prop)
=> 28
(with-min-value prop)
=> #object[java.time.LocalDate "2015-02-01"]
(with-value prop 20)
=> #object[java.time.LocalDate "2015-02-20"]
(with-max-value prop)
=> #object[java.time.LocalDate "2015-02-28"]
(properties (local-date 2015 9 28))
=> {:proleptic-month #java_time.temporal.TemporalFieldProperty{...}, ...}
</code></pre>
<h2><a href="#implementation-details" name="implementation-details"></a>Implementation Details</h2>
<p>Most of the temporal entity constructors with arities 1 to 3 use the conversion graph underneath. This provides for a very flexible way of defining the conversions while avoiding huge conditional statements and multiple definitions of the identical conversion logic. However, the flexibility comes with a cost:</p>
<ol>
<li>The first call to a constructor will take a <em>long</em> time as it will try to find a path in the conversion graph. Subsequent calls will reuse the path.</li>
<li>It’s not trivial to evaluate the impact of adding and removing conversions both on the performance and the conversion path chosen for certain arguments.</li>
<li>You might get nonsensical results for some of the paths in the graph that you might expect would make sense.</li>
</ol>
<p>Hopefully, the performance issue will be resolved in the future…</p>
<p>You can play with the conversion graph using the following helpers:</p>
<pre><code class="clojure">(java-time.repl/show-path org.joda.time.DateTime java.time.OffsetTime)
=> {:cost 2.0,
:path [[#<java_time.graph.Types@15e43c24 [org.joda.time.DateTime]>
#<java_time.graph.Types@78a2235c [java.time.Instant java.time.ZoneId]>]
[#<java_time.graph.Types@6d8ded1a [java.time.Instant java.time.ZoneId]>
#<java_time.graph.Types@5360f6ae [java.time.OffsetTime]>]]}
(java-time.repl/show-graph)
=> {1
{org.threeten.extra.DayOfYear
[[#object[java_time.graph.Types "[java.lang.Number]"]
#object[java_time.graph.Conversion "Cost:1.0"]]],
java.lang.Number
[[#object[java_time.graph.Types "[java.time.Instant]"]
#object[java_time.graph.Conversion "Cost:1.0"]]
...
</code></pre></div></div></div></body></html>