How to convert instant time to date in clojure - clojure

I have an instant time in clojure as "2016-08-03T18:45:00.000-00:00". I want to convert it to only date ("2016-08-03"). How do I do this.
My actual date is in the format "2016-08-03T18:45:00Z". I converted it to instant using (c/to-date (f/parse "2016-08-03T18:45:00Z")).How to get only date?
Thank you

clj-time is now deprecated, new projects should use java-time:
(:require [java-time :as jt]))
(defn format-date
"Format a Java instant"
[date]
(let [formatter (jt/format "dd-MM-yyyy HH:mm")
instant-with-zone (.atZone date (jt/zone-id))]
(jt/format formatter instant-with-zone)))
Note that (jt/zone-id) returns the current server zone, so you must use (jt/zone-id "America/Mexico_City") if you want a static zoned date.

If you want to get just the date part of the date and time string:
(require '[clojure.string :as str])
(def date-str "2016-08-03T18:45:00.000-00:00")
(first (str/split date-str #"T"))
;; => "2016-08-03"

Related

Clojure java-time: getting instant with millis

I'm trying to convert a local-date into an instant with millis using java-time, but instant returns a timestamp without millis.
(def UTC (java-time/zone-id "UTC")
(defn local-date-to-instant [local-date]
(-> local-date
(.atStartOfDay UTC)
java-time/instant))
(local-date-to-instant (java-time/local-date))
=> #object[java.time.Instant 0xdb3a8c7 "2021-05-13T00:00:00Z"]
but
(java-time/instant)
=> #object[java.time.Instant 0x1d1c27c8 "2021-05-13T13:12:31.782Z"]
The service downstream expects a string in this format: yyyy-MM-ddTHH:mm:ss.SSSZ.
Create a DateTimeFormatter that prints an ISO instant with milliseconds (3 fractional digits) even if they are zeroes:
(ns steffan.overflow
(:require [java-time :as jt])
(:import (java.time.format DateTimeFormatterBuilder)))
(def iso-instant-ms-formatter
(-> (DateTimeFormatterBuilder.) (.appendInstant 3) .toFormatter))
Example of use:
(def today-inst (jt/truncate-to (jt/instant) :days))
(str today-inst) ; => "2021-05-13T00:00:00Z"
(jt/format iso-instant-ms-formatter today-inst) ; => "2021-05-13T00:00:00.000Z"
You need to use a DateTimeFormatter. Then you can write code like:
LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String text = date.format(formatter);
In particular, look at these format pattern codes:
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
I think the S code is the best one for your purposes. You'll have to experiment a bit as the docs don't have all the details.
Here is one example:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[schema.core :as s]
[tupelo.java-time :as tjt]
)
(:import
[java.time Instant LocalDate LocalDateTime ZonedDateTime]
[java.time.format DateTimeFormatter]
))
(dotest
(let [top-of-hour (tjt/trunc-to-hour (ZonedDateTime/now))
fmt (DateTimeFormatter/ofPattern "yyyy-MM-dd HH:mm:ss.SSS")
]
(spyx top-of-hour)
(spyx (.format top-of-hour fmt))
))
with result
-----------------------------------
Clojure 1.10.3 Java 15.0.2
-----------------------------------
Testing tst.demo.core
top-of-hour => #object[java.time.ZonedDateTime 0x9b64076 "2021-05-13T07:00-07:00[America/Los_Angeles]"]
(.format top-of-hour fmt) => "2021-05-13 07:00:00.000"
The above is based from this template project and the library tupelo.java-time.
LocalDate doesn't have a time component. .atStartOfDay puts zeroes in all time fields. A LocalDateTime can be converted to an Instant through .toInstant.

compojure-api spec coercion on response body

I'm trying to figure out how to do custom coercion with compojure-api and spec. By reading the docs and code I have been able to do coercion on the input (the body) but am unable to do coercion on the response body.
Specifically, I have a custom type, a timestamp, that is represented as a long within my app but for the web API I want to consume and return ISO timestamps (no, I don't want to use Joda internally).
The following is what I have that works for input coercion but I have been unable to properly coerce the response.
(ns foo
(:require [clj-time.core :as t]
[clj-time.coerce :as t.c]
[spec-tools.conform :as conform]
[spec-tools.core :as st]))
(def timestamp (st/create-spec
{:spec pos-int?
:form `pos-int?
:json-schema/default "2017-10-12T05:04:57.585Z"
:type :timestamp}))
(defn json-timestamp->long [_ val]
(t.c/to-long val))
(def custom-json-conforming
(st/type-conforming
(merge
conform/json-type-conforming
{:timestamp json-timestamp->long}
conform/strip-extra-keys-type-conforming)))
(def custom-coercion
(-> compojure.api.coercion.spec/default-options
(assoc-in [:body :formats "application/json"] custom-json-
conforming)
compojure.api.coercion.spec/create-coercion))
;; how do I use this for the response coercion?
(defn timestamp->json-string [_ val]
(t.c/to-string val))
;; I've tried the following but it doesn't work:
#_(def custom-coercion
(-> compojure.api.coercion.spec/default-options
(assoc-in [:body :formats "application/json"] custom-json-
conforming)
(assoc-in [:response :formats "application/json"]
(st/type-conforming
{:timestamp timestamp->json-string}))
compojure.api.coercion.spec/create-coercion))
Problem is that Spec Conforming is a one-way transformation pipeline:
s/conform (and because of that st/conform) does both transform and validate for the result. Your response coercion first converts the integer into date string and the validates it against the original spec predicate, which is pos-int? and it fails on that.
To support two-way transformations, you need to define the end result as either of the possible formats: e.g. change your predicate to something like #(or (pos-int? %) (string? %)) and it should work.
Or you can have two different Spec Records, one for input (timestamp-long with pos-int? predicate) and another for outputs (timestamp-string with string? predicate). But one needs to remember to use correct ones for request & responses.
CLJ-2251 could possible help if there was and extra :transform mode (not written in the issue yet), which would do conforming without validating the end results.
Normally, return transformations are done by the format encoder, but they usually dispatch on value types. For example Cheshire just sees an Long and has no clue that it should be written as date string.
Maybe people on the #clojure-spec slack could help. Would also like to know how to build this kind of two-way transformation with spec.

Parse string to get date and time in a specific format in Clojure

I just started to play with wit/duckling. It is written in Clojure and I have no previous experience in Clojure. I need to parse a string like 2016-08-14T19:45:48.000+05:30 to a format like 1945hrs, Sunday, August 14th 2016. I searched on Internet and came across to lib clj-time. After struggling with it for a long time I came across this thread and thought that rfc822 is my cup of tea. So I used formatter rfc822 but it is giving me exception:
java.lang.IllegalArgumentException: Invalid format: "2016-08-16T00:00:00.000+05:30"
Here is my code:
(ns firstproj.core
(:gen-class)
(:require [duckling.core :as p])
(:require [clj-time.format :as f]))
(defn -main
"I don't do a whole lot."
[x]
(p/load! { :languages ["en"]})
(def var_ (p/parse :en$core x [:time]))
(def date_string "2016-08-14T19:45:48.000+05:30")
(f/parse (f/formatters :rfc822) date_string))
So can anybody tell me what I am doing wrong here. Or any other way in Clojure to get my desired date-time format. As I am completely naive in Clojure, I request you to answer in detail, it will help me to understand this in a better way. Thank You.
The clj-time library is a wrapper for Joda-Time. If you're using Java 8, you should prefer the built-in java.time library over Joda-Time, for reasons explained here. In Clojure, you can use the Clojure.Java-Time library, which wraps java.time. First, add it to your dependencies:
[clojure.java-time "0.2.1"]
Then you can use the offset-date-time and format functions in the java-time namespace:
(require '[java-time :as time])
(->> (time/offset-date-time "2016-08-14T19:45:48.000+05:30")
(time/format "HHmm'hrs', EEEE, MMMM d y"))
;;=> "1945hrs, Sunday, August 14 2016"
As #Piotrek pointed out in his answer, Joda-Time doesn't seem to support the "th" in your original question. Judging from the DateTimeFormatter docs, neither does java.time.
Without actually checking which predefined formatter matches your date format you might just call:
(f/parse "2016-08-14T19:45:48.000+05:30")
;; => #object[org.joda.time.DateTime 0x1bd11b14 "2016-08-14T14:15:48.000Z"]
It will try all predefined formatters and return parsed value from the first one that succeeds.
Then to print in your custom format:
(require '[clj-time.core :as t])
(def my-time-zone (t/time-zone-for-offset 5 30))
(def my-formatter
(f/formatter
"HHmm'hrs', EEEE, MMMM dd yyyy"
my-time-zone))
(f/unparse my-formatter some-date)
;; => "1945hrs, Sunday, August 14 2016"
Unfortunately to my knowledge JodaTime doesn't handle things like adding st, nd, rd, th to days.

how to get the current date as YYYYMMDD in clojure?

I am using the following code:
(require '[clj-time.core :as time]
'[clj-time.coerce :as tc]
'[clj-time.format :as f])
(f/unparse (f/formatter "yyyyMMdd") time/now)
But it throws the following error.
caused by: java.lang.ClassCastException: clj_time.core$now cannot be cast to org.joda.time.ReadableInstant
unparse function takes 2 arguments. First is the format,which should be an instance of org.joda.time.format.DateTimeFormatter, which you create correctly by calling
(f/formatter "yyyyMMdd")
the second argument is date time, which should be an instance of org.joda.time.DateTime and here you are doing small mistake. Instead of passing DateTime you are passing clojure function time/now, what you should do is to call the function like this
(f/unparse (f/formatter "yyyyMMdd") (time/now))

clj-time always returns today's date.

I have a strange problem here. I am calling dates from a database and I am attempting to show the PostgreSQL dates, formatted as "2013-01-01", to display on my site as "January 1, 2013"
I have the following code:
(ns mr.layouts
(:require
[clj-time.format :as ctf]))
(def to-mdy (ctf/formatter "MMMM d, yyyy"))
(defn coerce-mdy [sd]
(ctf/unparse to-mdy (ctf/parse sd)))
The call to the database looks like:
(layouts/coerce-mdy startdate)
The above code does what I want if I test it from the REPL:
mr.handler=> (use 'mr.layouts)
nil
mr.handler=> (require '[clj-time.format :as ctf])
nil
mr.handler=> (coerce-mdy "2012-01-01")
"January 1, 2012"
mr.handler=> (coerce-mdy "2014-10-04")
"October 4, 2014"
mr.handler=>
But what I am getting on the webpage is "September 1, 2013" (today as of this writing) and no other dates. I don't have "2013-09-01" in the database.
I've returned the raw date-value from the data base from (coerse-mdy) and it returns the correct date, so the closest I've been able to isolate this issue is in either (to-mdy) or (coerce-mdy).
So far, I've tried moving the function to the local namespace and using localized let values.
When using clj-time with a database, I've found that I need to use the functions to-sql-date and from-sql-date in the coerce namespace (source here: https://github.com/clj-time/clj-time/blob/master/src/clj_time/coerce.clj)
This is because, as #noisesmith mentioned in his comment, most clojure sql libraries will give a date object (specifically, a java.sql.Date) for a date field in the db. This needs to be treated slightly differently from a string in order to get something that plays nice with clj-time.
Assuming your date is indeed a java.sql.Date, the following should do the trick...
(ns mr.layouts
(:require [clj-time.format :as ctf]
[clj-time.coerce :as coerce]))
(def to-mdy (ctf/formatter "MMMM d, yyyy"))
(defn coerce-mdy [sd]
(ctf/unparse to-mdy (coerce/from-sql-date sd)))