since#
since formats Unix timestamps as localized relative-time messages.
Examples include:
"a few seconds ago""5 minutes ago""in 2 weeks""hace 3 horas"
Translations are compiled from lib/locales.tsv with ocsigen-i18n.
Install#
opam install since
For local development:
dune build
dune runtest
Usage#
let now = Unix.gettimeofday ()
let five_minutes_ago = now -. (5. *. 60.)
let message = Since.format ~now five_minutes_ago
(* "5 minutes ago" *)
Pass a locale explicitly:
let message = Since.format ~locale:Since.es ~now five_minutes_ago
(* "hace 5 minutos" *)
Format future timestamps:
let in_two_weeks = now +. (14. *. 24. *. 60. *. 60.)
let message = Since.format ~locale:Since.en ~now in_two_weeks
(* "in 2 weeks" *)
Look up a locale from a string:
let locale =
match Since.locale_of_string "pt-BR" with
| Some locale -> locale
| None -> Since.en
let message = Since.format ~locale ~now five_minutes_ago
Inspect the bucket without rendering:
let direction, grain = Since.classify (now -. five_minutes_ago)
(* direction = Since.Past, grain = Since.Minutes 5 *)
Use calendar for date arithmetic, then pass the Unix timestamp to since:
let now = CalendarLib.Calendar.now ()
let next_month =
CalendarLib.Calendar.add now (CalendarLib.Calendar.Period.month 1)
let message =
Since.format ~locale:Since.en
~now:(CalendarLib.Calendar.to_unixfloat now)
(CalendarLib.Calendar.to_unixfloat next_month)
(* "in a month" *)
CLI#
The installed since command uses climate for argument parsing, calendar
for date parsing, and this library for rendering.
since 2024-10-11
# 2 years ago
since 24/10/2023 --locale es
# hace 3 años
since 2025-10-21T10:45Z --locale en
# 7 months ago
Supported CLI inputs include Unix timestamp seconds, YYYY-MM-DD,
DD/MM/YYYY, YYYY-MM-DD HH:MM:SS, RFC3339-like values such as
2025-10-21T10:45Z, HH:MM:SS, 3:04PM, stamp values such as
Jan 02 15:04:05, RubyDate, RFC822/RFC822Z, and RFC1123/RFC1123Z with numeric
zones.
Timezone-name layouts such as MST are not parsed by the CLI.
Locales#
Built-in locale values include:
Since.en
Since.es
Since.pt
Since.fr
Since.de
Since.it
Since.nl
Since.pl
Since.ru
Since.ro
Since.sv
Since.nb
Since.da
Since.fi
Since.el
Since.cs
Since.hu
Since.ja
Since.zh_cn
Since.zh_tw
Use Since.all_locales to iterate over them.
API#
val format : ?locale:Since.locale -> now:float -> float -> string
val classify : float -> Since.direction * Since.grain
val locale_of_string : string -> Since.locale option
val locale_code : Since.locale -> string
Both now and the timestamp passed to format are Unix epoch seconds.