This is a small library useful for time duration humanization.
- Description: Provides
humanize-duration:humanize-duration
function to make readable representation ofLOCAL-TIME-DURATION:DURATION
objects. - Licence: Unlicense
- Author: Alexander Artemenko
- Homepage: https://40ants.com/humanize-duration/
- Source control: GIT
- Depends on: 40ants-doc, docs-config, local-time-duration, with-output-to-stream
It is different from LOCAL-TIME-DURATION:HUMAN-READABLE-DURATION
, because allows
to output only significant parts of a duration object.
Also it limits a number of part. For example, if there days, hours, minutes and seconds, then most probably it is already not important exactly how many minutes and seconds passed since the moment.
Compare these two examples:
CL-USER> (local-time-duration:human-readable-duration
(local-time-duration:duration :day 5
:hour 13
:minute 0
:sec 14
:nsec 10042))
" 5 days 13 hours 14 seconds 10042 nsecs"
CL-USER> (humanize-duration:humanize-duration
(local-time-duration:duration :day 5
:hour 13
:minute 0
:sec 14
:nsec 10042))
"5 days 13 hours"
Main job is done at humanize-duration
(1
2
):
function humanize-duration:humanize-duration
duration &key stream (n-parts 2) (format-part #'default-format-part)
This is the better version of LOCAL-TIME-DURATION:HUMAN-READABLE-DURATION
.
By default it returns only 2 most significant duration parts.
If duration is 2 hour, 43 seconds and 15 nanoseconsds, then function will return "2 hours 43 seconds":
CL-USER> (ultralisp/utils/time:humanize-duration
(local-time-duration:duration :hour 2
:sec 43
:nsec 15))
"2 hours 43 seconds"
Also, you can pass a :format-part
argument.
It should be a function of three arguments:
(stream part-type part)
where part-type
is a keyword
from this list:
(list :weeks :days :hours :minutes :secs :nsecs)
humanize-duration
(1
2
) accepts :FORMAT-PART
argument, which is default-format-part
function by default:
your own version. This could be useful if you want to support localization to other languages.
function humanize-duration:default-format-part
stream part-type part
This is should return a string with propertly pluralized form.
PART-TYPE
argument is a member of (list :weeks :days :hours :minutes :secs :nsecs).PART
is an integer.
Here are possible results:
(t :weeks 1) -> "1 week"
(t :weeks 5) -> "5 weeks"
(t :day 2) -> "2 days"
Here is how you can localize output for your language
humanize-duration
(1
2
) comes with predefined Russian localization.
This package includes a single function, useful to display duration in Russian language:
function humanize-duration/ru:format-part
stream part-type part
This is Russian version of part formatter for humanize-duration
You can use it as an template, to define other languages.
Please, don't forget to contribute your solutions!
Here is how you can use it in your code:
CL-USER> (humanize-duration:humanize-duration
(local-time-duration:duration :day 5
:hour 13
:minute 0
:sec 14
:nsec 10042)
:format-part #'humanize-duration/ru:format-part)
"5 дней 13 часов"
Here is how format-part
function is defined in the code:
(defun format-part (stream part-type part)
"This is Russian version of part formatter for HUMANIZE-DURATION"
(format stream "~d ~A"
part
(apply #'choose-form
part
(ecase part-type
(:weeks '("неделя" "недели" "недель"))
(:days '("день" "дня" "дней"))
(:hours '("час" "часа" "часов"))
(:minutes '("минута" "минуты" "минут"))
(:secs '("секунда" "секунды" "секунд"))
(:nsecs '("наносекунда" "наносекунды" "наносекунд"))))))
Russian version uses internal helper, to choose a correct word form:
(defun choose-form (n &rest forms)
"This function is based on this gettext formula:
Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
"
(let ((n%10 (rem n 10))
(n%100 (rem n 100)))
(cond
((and (= n%10
1)
(not (= n%100
11)))
(first forms))
((and (>= n%10
2)
(<= n%10
4)
(or (<= n%100
10)
(>= n%100
20)))
(second forms))
(t
(third forms)))))
Applied to a different numbers it produces the following:
CL-USER> (flet ((p (n)
(format t "~A ~A~%"
n
(choose-form n "яблоко" "яблока" "яблок"))))
(loop for i upto 12
do (p i)))
0 яблок
1 яблоко
2 яблока
3 яблока
4 яблока
5 яблок
6 яблок
7 яблок
8 яблок
9 яблок
10 яблок
11 яблок
12 яблок