Lists, ranges & relative time¶
Joining lists, formatting ranges, and directed ("relative") durations. As of
PHP v3 these all work in every port — PHP reconstructs the few formatters
ext-intl doesn't bind from live CLDR data (with two small caveats, noted below).
C# goes through the native ICU4C formatters directly.
| Method | Use it for |
|---|---|
join(items, type?, width?) |
"A, B, and C" with the locale's list grammar |
numberRange / moneyRange / dateRange |
A low–high interval ("3–5", "$3 – $5") |
relativeDuration(amount, unit, numeric?) |
"3 days ago" / "in 2 hours" from a signed amount |
relativeDurationBetween(target, reference?, numeric?) |
The same, computed between two moments |
Lists¶
join() glues items with the locale's list conventions — and crucially the type
of list changes the connector word ("and" vs "or"):
type |
English | Spanish |
|---|---|---|
conjunction (default) |
A, B, and C |
A, B y C |
disjunction |
A, B, or C |
A, B o C |
unit |
A, B, C (no connector — for measurements) |
A, B, C |
The width argument (full/long, medium/short, short/narrow) controls
spacing and the connector's verbosity.
Available in all five ports.
Number, money & date ranges¶
A range formats two endpoints as a single interval, collapsing the shared parts
("Feb 2 – 5, 2020", not "Feb 2, 2020 – Feb 5, 2020"). Each mirrors a single-value
formatter on this site:
| Method | Mirrors | Signature |
|---|---|---|
numberRange(start, end) |
number() |
two numbers |
moneyRange(start, end, code?) |
money() |
two amounts + currency |
dateRange(start, end, dateWidth?, timeWidth?) |
date() |
two moments + widths |
dateRange() defaults to medium date width (short numeric dates read poorly
as a range) and none time width.
Like money(), moneyRange() returns "" when no currency is
resolved and takes the currency from the currency modifier if you omit the code.
Two PHP caveats
numberRange / moneyRange / dateRange use ICU's formatRange in JS,
Python, and Java. PHP reconstructs them from CLDR data, with two differences:
moneyRange()is approximate — it doesn't collapse the shared currency symbol or pad the separator ($3.00–$5.00vs ICU's$3.00 – $5.00).dateRange()supportsshort/mediumonly (long/full interval skeletons aren't reachable fromext-intl).
Relative / directed duration¶
A directed duration carries a past/future orientation — the counterpart of the
undirected duration(). There are two entry points:
relativeDuration(amount, unit, numeric?)— you supply the signed amount and unit.relativeDurationBetween(target, reference?, numeric?)— you supply two moments; it computes the difference and picks the largest sensible unit.
The sign sets the direction: negative is past ("… ago"), positive is future
("in …"). The unit is one of second, minute, hour, day, week,
month, quarter, year (singular only). The numeric option chooses between
numeric and colloquial phrasing:
numeric |
relativeDuration(-1, "day", …) |
|---|---|
always (default for relativeDuration) |
"1 day ago" |
auto (default for relativeDurationBetween) |
"yesterday" |
const c = new Cosmo("en");
c.relativeDuration(-3, "day"); // "3 days ago"
c.relativeDuration(2, "hour"); // "in 2 hours"
c.relativeDuration(-1, "day", "auto"); // "yesterday"
c.relativeDurationBetween(target); // vs now → "in 5 days"
c.relativeDurationBetween(target, reference); // vs a given moment
var c = new Cosmo("en");
c.RelativeDuration(-3, "day"); // "3 days ago"
c.RelativeDuration(2, "hour"); // "in 2 hours"
c.RelativeDuration(-1, "day", "auto"); // "yesterday" (word form)
c.RelativeDurationBetween(target); // vs now → "in 5 days"
c.RelativeDurationBetween(target, reference); // vs a given moment
relativeDurationBetween() computes target − reference (with reference
defaulting to now), then walks up the unit scale — seconds, minutes, hours,
days, weeks, months, years — and formats at the first unit where the amount is
below the next threshold. So a 5-day gap renders as "in 5 days", a 40-day gap as
"in 2 months".
Port notes
relativeDuration / relativeDurationBetween are in all five ports.
ICU/Intl produce single-unit relative text only (no "3 days, 5 hours
ago"). The numeric: "auto" word-forms ("yesterday", "last week") work in PHP,
JavaScript, Java, and C#; Python falls back to the numeric form ("1 day ago")
because PyICU doesn't cleanly expose them — always correct, just not colloquial.
All non-JS ports accept only singular unit names ("day", not "days").
For an undirected span, use duration().
Practical examples¶
A "posted X ago" timestamp. Let relativeDurationBetween() choose the unit and
the colloquial wording:
A localised "and N more" tag list. Join the visible tags, then append an overflow phrase built from a plural message:
const c = new Cosmo("en");
const tags = ["news", "sport", "tech", "travel", "food"];
const shown = tags.slice(0, 3);
const rest = tags.length - shown.length;
let label = c.join(shown);
if (rest > 0) {
label += " " + c.message("and {n, plural, one {# more} other {# more}}", { n: rest });
}
// "news, sport, and tech and 2 more"
$c = new Cosmo('en');
$tags = ['news', 'sport', 'tech', 'travel', 'food'];
$shown = array_slice($tags, 0, 3);
$rest = count($tags) - count($shown);
$label = $c->join($shown);
if ($rest > 0) {
$label .= ' ' . $c->message('and {n, plural, one {# more} other {# more}}', ['n' => $rest]);
}
// "news, sport, and tech and 2 more"
var c = new Cosmo("en");
var tags = new[] { "news", "sport", "tech", "travel", "food" };
var shown = tags.Take(3).ToList();
int rest = tags.Length - shown.Count;
string label = c.Join(shown);
if (rest > 0) {
label += " " + c.Message("and {n, plural, one {# more} other {# more}}",
new Dictionary<string, object?> { ["n"] = rest });
}
// "news, sport, and tech and 2 more"