How would Time Ago function implementation look like in Clojure? - clojure

I mean function, when given time returns smallest time unit ago.
E.g
"5 minutes ago"
"30 seconds ago"
"just now"

One possible implementation might look like this:
Note, I've used clj-time/clj-time ยท GitHub library.
(require '[clj-time.core :as t])
(defn time-ago [time]
(let [units [{:name "second" :limit 60 :in-second 1}
{:name "minute" :limit 3600 :in-second 60}
{:name "hour" :limit 86400 :in-second 3600}
{:name "day" :limit 604800 :in-second 86400}
{:name "week" :limit 2629743 :in-second 604800}
{:name "month" :limit 31556926 :in-second 2629743}
{:name "year" :limit Long/MAX_VALUE :in-second 31556926}]
diff (t/in-seconds (t/interval time (t/now)))]
(if (< diff 5)
"just now"
(let [unit (first (drop-while #(or (>= diff (:limit %))
(not (:limit %)))
units))]
(-> (/ diff (:in-second unit))
Math/floor
int
(#(str % " " (:name unit) (when (> % 1) "s") " ago")))))))
Example usage:
(time-ago (t/minus (t/now) (t/days 400)))
=> "1 year ago"
(time-ago (t/minus (t/now) (t/days 15)))
=> "2 weeks ago"
(time-ago (t/minus (t/now) (t/seconds 45)))
=> "45 seconds ago"
(time-ago (t/minus (t/now) (t/seconds 1)))
=> "just now"

If you are using Clojure on the JVM, consider using the PrettyTime library. Using that library for implementing "time ago" in Java was suggested here.
To use PrettyTime library from Clojure, first add the following to the :dependencies vector in project.clj:
[org.ocpsoft.prettytime/prettytime "3.2.7.Final"]
Then you can use Java interop directly. One quirk I found is that the cut-off between "moments ago" and other outputs is at 1 minute by default. I added a line to change that to one second. This library appears to support several languages, which is a plus. By default it prints "moments ago" instead of "just now". It would require some effort to deal with in case that is really important.
(import 'org.ocpsoft.prettytime.PrettyTime
'org.ocpsoft.prettytime.units.JustNow
'java.util.Date)
(defn time-ago [date]
(let [pretty-time (PrettyTime.)]
(.. pretty-time (getUnit JustNow) (setMaxQuantity 1000))
(.format pretty-time date)))
(let [now (System/currentTimeMillis)]
(doseq [offset [200, (* 30 1000), (* 5 60 1000)]]
(println (time-ago (Date. (- now offset))))))
;; moments ago
;; 30 seconds ago
;; 5 minutes ago

It only supports minutes, hours & days but if that's sufficient you may also want to look at goog.date.relative:
https://github.com/google/closure-library/blob/master/closure/goog/date/relative.js#L87

Related

Clojure - Get hour only from Time String

I have a time string "2017-08-30 09:01:48". Is there a way to round the time to the nearest hour and return the hour?
So if the time were "2017-08-30 09:01:48" it would return 9
If the time were "2017-08-30 09:51:48" it would return 10
I looked at the library clj-time but I couldn't see anything in there that would help me
Any help would be much appreciated?
clj-time is a thin wrapper for Joda Time library. You can easily use the methods of the underlying Java objects to perform common date manipulations.
For rounding the hour, you can access the hour as a DateTime.Property and use its API to round to nearest hour as follows:
(require '[clj-time.format :as tf])
(-> (tf/parse "2017-08-30 09:01:48")
.hourOfDay
.roundHalfCeilingCopy
.getHourOfDay)
There are several rounding methods (roundCeilingCopy, roundHalfFloorCopy, etc.) to choose from, depending on your exact case.
Note also, that you don't need to specify a formatter, as your date string is already in the ISO8601 format.
If you don't want to depend on another library, don't forget, that we have new date/time API in Java 8.
Although it doesn't support rounding to the nearest hour (minute, whatever), that's quite easy to implement like madstap in his solution.
Here's the code:
(import 'java.time.LocalDateTime)
(import 'java.time.format.DateTimeFormatter)
(def date-format (DateTimeFormatter/ofPattern "yyyy-MM-dd HH:mm:ss"))
(defn- round-to-nearest-hour [date-time-string]
(let [date-time (LocalDateTime/parse date-time-string date-format)]
(if (>= 30 (.getMinute date-time))
date-time
(.plusHours date-time 1))))
(.getHour (round-to-nearest-hour "2017-08-30 09:01:48")) ;=> 9
(.getHour (round-to-nearest-hour "2017-08-30 09:31:48")) ;=> 10
This is pretty simple to do with the clj-time library.
(ns foo.hours
(:require
[clj-time.core :as time]
[clj-time.format :as timef]))
(defn parse [s]
(timef/parse (timef/formatter "yyyy-MM-dd HH:mm:ss") s))
(defn round-hour [time]
(let [m (time/minute time)
h (time/hour time)
rounded-h (if (<= 30 m) (inc h) h)]
(if (= 24 rounded-h) 0 rounded-h)))
(comment
(def f (comp round-hour parse))
(f "2017-08-30 09:20:48") ;=> 9
(f "2017-08-30 09:33:48") ;=> 10
(f "2017-08-30 23:39:48") ;=> 0
)

How to add days to current date in clojure

In clojure I want to add days to current date can anyone please guide me on that. Am getting current date as below and now let's say I want to add 7 days to it, how can I get a new date?
(.format (java.text.SimpleDateFormat. "MM/dd/yyyy") (java.util.Date.))
This would work:
(java.util.Date. (+ (* 7 86400 1000) (.getTime (java.util.Date.)))
I prefer to use System/currentTimeMillis for the current time:
(java.util.Date. (+ (* 7 86400 1000) (System/currentTimeMillis)))
Or you can use clj-time which is a nicer api to deal with time (it's a wrapper around Joda Time). From the readme file:
(t/plus (t/date-time 1986 10 14) (t/months 1) (t/weeks 3))
=> #<DateTime 1986-12-05T00:00:00.000Z>
user> (import '[java.util Calendar])
;=> java.util.Calendar
user> (defn days-later [n]
(let [today (Calendar/getInstance)]
(doto today
(.add Calendar/DATE n)
.toString)))
#'user/days-later
user> (println "Tomorrow: " (days-later 1))
;=> Tomorrow: #inst "2014-11-26T15:36:31.901+09:00"
;=> nil
user> (println "7 Days from now: " (days-later 7))
;=> 7 Days from now: #inst "2014-12-02T15:36:44.785+09:00"
;=> nil

Add days to current date

Am new to clojure, can anyone help me to understand how can I get current date in clojure and then adding days to it?
for e.g. adding 3 days to current date?
The idiomatic Clojure way is to use clj-time (see link for Leiningen/Maven install instructions), which wraps Joda time as referenced by the first answer from overthink.
user=> (use '[clj-time.core])
nil
user=> (now)
#<DateTime 2014-11-25T12:03:34.714Z>
user=> (plus (now) (days 3))
#<DateTime 2014-11-28T12:05:40.888Z>
This isn't a Clojure-specific answer, really, but I'd use Joda time.
(import 'org.joda.time.DateTime)
(let [now (DateTime/now)
later (.plusDays now 3)]
[now later])
;; [#<DateTime 2014-11-24T23:26:05.885-05:00> #<DateTime 2014-11-27T23:26:05.885-05:00>]
user> (import '[java.util Calendar])
;=> java.util.Calendar
user> (defn days-later [n]
(let [today (Calendar/getInstance)]
(doto today
(.add Calendar/DATE n)
.toString)))
#'user/days-later
user> (println "Tomorrow: " (days-later 1))
;=> Tomorrow: #inst "2014-11-26T15:36:31.901+09:00"
;=> nil
user> (println "7 Days from now: " (days-later 7))
;=> 7 Days from now: #inst "2014-12-02T15:36:44.785+09:00"
;=> nil

Can I process an unrealized lazy-seq step by step

I have a lazy-seq where each item takes some time to calculate:
(defn gen-lazy-seq [size]
(for [i (range size)]
(do
(Thread/sleep 1000)
(rand-int 10))))
Is it possible to evaluate this sequence step by step and print the results. When I try to process it with for or doseq clojure always realizes the whole lazy-seq before printing anything out:
(doseq [item (gen-lazy-seq 10)]
(println item))
(for [item (gen-lazy-seq 10)]
(println item))
Both expressions will wait for 10 seconds before printing anything out. I have looked at doall and dorun as a solution, but they require that the lazy-seq producing function contain the println. I would like to define a lazy-seq producing function and lazy-seq printing function separately and make them work together item by item.
Motivation for trying to do this:
I have messages coming in over a network, and I want to start processing them before all have been received. At the same time it would be nice to save all messages corresponding to a query in a lazy-seq.
Edit 1:
JohnJ's answer shows how to create a lazy-seq that will be evaluated step by step. I would like to know how to evaluate any lazy-seq step by step.
I'm confused because running (chunked-seq? (gen-lazy-seq 10)) on gen-lazy-seq as defined above OR as defined in JohnJ's answer both return false. So then the problem can't be that one creates a chunked sequence and the other doesn't.
In this answer, a function seq1 which turns a chunked lazy-seq into a non-chunked one is shown. Trying that function still gives the same problem with delayed output. I thought that maybe the delay has to do with the some sort of buffering in the repl, so I tried to also print the time when each item in the seq is realized:
(defn seq1 [s]
(lazy-seq
(when-let [[x] (seq s)]
(cons x (seq1 (rest s))))))
(let [start-time (java.lang.System/currentTimeMillis)]
(doseq [item (seq1 (gen-lazy-seq 10))]
(let [elapsed-time (- (java.lang.System/currentTimeMillis) start-time)]
(println "time: " elapsed-time "item: " item))))
; output:
time: 10002 item: 1
time: 10002 item: 8
time: 10003 item: 9
time: 10003 item: 1
time: 10003 item: 7
time: 10003 item: 2
time: 10004 item: 0
time: 10004 item: 3
time: 10004 item: 5
time: 10004 item: 0
Doing the same thing with JohnJ's version of gen-lazy-seq works as expected
; output:
time: 1002 item: 4
time: 2002 item: 1
time: 3002 item: 6
time: 4002 item: 8
time: 5002 item: 8
time: 6002 item: 4
time: 7002 item: 5
time: 8002 item: 6
time: 9003 item: 1
time: 10003 item: 4
Edit 2:
It's not only sequences generated with for which have this problem. This sequence generated with map cannot be processed step by step regardless of seq1 wrapping:
(defn gen-lazy-seq [size]
(map (fn [_]
(Thread/sleep 1000)
(rand-int 10))
(range 0 size)))
But this sequence, also created with map works:
(defn gen-lazy-seq [size]
(map (fn [_]
(Thread/sleep 1000)
(rand-int 10))
(repeat size :ignored)))
Clojure's lazy sequences are often chunked. You can see the chunking at work in your example if you take large sizes (it will be helpful to reduce the thread sleep time in this case). See also these related SO posts.
Though for seems to be chunked, the following is not and works as desired:
(defn gen-lazy-seq [size]
(take size (repeatedly #(do (Thread/sleep 1000)
(rand-int 10)))))
(doseq [item (gen-lazy-seq 10)]
(println item))
"I have messages coming in over a network, and I want to start processing them before all have been received." Chunked or no, this should actually be the case if you process them lazily.

Updating certain values of maps contained in a list

I have a list of maps which looks something like this:
(def balances ({:name "Steve" :money 1000} {:name "Bill" :money 1000} ...))
I'm trying to write a function that transfers a given amount (let's say 100) of Steves money to Bill and update the data structure:
({:name "Steve" :money 900} {:name "Bill" :money 1100})
I figured that the function should expect the balance as parameter each time and look something like this:
(defn transfer [current-balances amount sender receiver] ... )
How would a function like this look and is this a clever way of managing and updating the account balance? In general my program will take quite a long list of transfers and iteratively apply them to the balance structure. Do I always have to pass the balance structure to the transfer function because of the persistant data structures in Clojure?
The fact that your balances are all contained under a single umbrella data structure, and that structure is a proper value, your transfer function can simply take in the structure describing the current state of the accounts, another describing the change, and produce the new state of accounts. This lets you treat the transfer actions as proper values as well which should help deal with very long lists of transfers :) My only change would be to use a map of balances instead of a list.
bar> (def balances {"Steve" {:money 1000} "Bill" {:money 1000}})
#'bar/balances
bar> (def transfers [["Steve" "Bill" 100] ["Bill" "Steve" 100]
["Steve" "Bill" 10 ] ["Bill" "Steve" 10 ]
["Bill" "Steve" 10 ]])
#'bar/transfers
Then define a simple transfer function that takes one of these and appliese it to the accounts
(defn transfer [balances [from to ammount]]
(-> balances
(update-in [from :money] - ammount)
(update-in [to :money] + ammount)))
this function can be used to directly reduce any sequence of transfers to the state of all accounts:
bar> (reduce transfer balances transfers)
{"Bill" {:money 990}, "Steve" {:money 1010}}
A function that accepts a new transfers from a customer can then use this function to alter the state of whatever you choose to store your bank in (DB, atom, agent, etc.)
bar> (def bank (agent {:current balances :ledger []}))
#'bar/bank
bar> (defn accept-transfers [transfers]
(send bank assoc :current (reduce transfer (:current #bank) transfers)
:ledger (concat transfers (:ledger #bank))))
#'bar/accept-transfers
bar> (accept-transfers transfers)
#<Agent#2eb9bc1: {:current {"Bill" {:money 1000}, "Steve" {:money 1000}}, :ledger []}>
which puts the transfer on the banks queue (and returns that agent which the REPL quickly prints while the transfer may be running) after a moment when we look at the bank wee see all these transfers have been applied.
bar> bank
#<Agent#2eb9bc1: {:current {"Bill" {:money 990}, "Steve" {:money 1010}},
:ledger (["Steve" "Bill" 100] ["Bill" "Steve" 100]
["Steve" "Bill" 10] ["Bill" "Steve" 10]
["Bill" "Steve" 10])}>
Clojure data is immutable, you can't modify it. You need to use STM.
Here is sample code (simplified to the point of question).
(def account1 {:name "Foo" :money (ref 1000)})
(def account2 {:name "Bar" :money (ref 1000)})
(defn transfer
[from to amount]
(dosync
(alter (:money from) - amount)
(alter (:money to) + amount)))
(transfer account1 account2 100)
(println #(:money account1))
(println #(:money account2))
Read more at http://clojure.org/refs, http://clojure.org/atoms and, perhaps, http://clojure.org/agents
Here's my two cents. I hope it'll be helpful too.
(def balances {1 (ref {:name "Steve"
:money 1000})
2 (ref {:name "Bill"
:money 1000})
3 (ref {:name "John"
:money 1000})})
(defn balance [person-id]
((deref (balances person-id)) :money))
(defn credit [balance amount]
(- balance amount))
(defn debet [balance amount]
(+ balance amount))
(defn update-account [person-id operation-fn amount]
(alter (get balances person-id)
#(assoc % :money
(operation-fn (:money %) amount))))
(defn credit-account [person-id amount]
(update-account person-id credit amount))
(defn debet-account [person-id amount]
(update-account person-id debet amount))
(defn transfer [sender-id receiver-id amount]
(if (< (credit (balance sender-id) amount) 0)
{:result :insufficient-fund}
(do (credit-account sender-id amount)
(debet-account receiver-id amount)
{:result :ok})))
test
(defn transaction []
(dosync (transfer 1 2 100)
(transfer 2 3 200)
(transfer 3 1 200)))
(transaction)
-> {:result :ok}
balances
-> {1 #<Ref#b54dba: {:money 1100, :name "Steve"}>,
2 #<Ref#1020230: {:money 900, :name "Bill"}>,
3 #<Ref#1803641: {:money 1000, :name "John"}>}