forked from dm3/clojure.java-time
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathREADME.html
More file actions
351 lines (318 loc) · 21.5 KB
/
README.html
File metadata and controls
351 lines (318 loc) · 21.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
<!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>