Tiny date formatter

The built-in display() method uses strtftime-style functions, which are fine but not as flexible as I’d like. In particular I don’t want a leading zero when the day of month has value 1-9. So I wrote this. This is not a Formal Release and it’s too small to put on Universe, but it might be helpful to someone else getting started. (I assert no rights, go wild.)

What I specifically want is to format YYYY-MM-DD as DD(superscripted ordinal) MMMM YYYY; so 2025-04-01 would be 1^st April 2025 (which was what I got by default in the LibreOffice document I was using before I found typst). This is of course wildly non-internationalised.

// display a date as "DAY^th MONTH YEAR"
#let longdisp(date) = {
  let dd = date.day()
  let ord = "th"
  if dd < 10 or dd > 19 {
    let mod = calc.rem(dd, 10)
    if mod == 1 {
      ord = "st"
    } else if mod == 2 {
      ord = "nd"
    } else if mod == 3 {
      ord = "rs"
    }
  }
  str(dd) + super(ord) + " " + date.display("[month repr:long] [year]")
}

and use it as longdisp(date).

There is the [day padding:none] syntax which removes the leading zero if day is < 10. If that helps. (I don’t know if Typst supports string interpolation yet).

See: Datetime Type – Typst Documentation

// Display a date as "DAY^th MONTH YEAR".
#let display-date(date) = {
  let day = date.day()
  let ord = if day in (11, 12, 13) { "th" } else {
    ("th", "st", "nd", "rd").at(calc.rem(day, 10), default: "th")
  }
  str(day) + super(ord) + date.display(" [month repr:long] [year]")
}

#{
  range(1, 32)
    .map(day => datetime(year: 2025, month: 1, day: day))
    .map(display-date)
    .map(list.item)
    .join()
}
output

Note: it’s “rd”, not “rs”.

Looks like datify – Typst Universe doesn’t have the superscript ordinal format, maybe you can open an issue there. I also don’t know if you can remove zero padding from days in custom-date-format.


Or even better:

#import "@preview/nth:1.0.1": nths

// Display a date as "DAY^th MONTH YEAR".
#let display-date(date) = {
  nths(date.day()) + date.display(" [month repr:long] [year]")
}

#{
  range(1, 32)
    .map(day => datetime(year: 2025, month: 1, day: day))
    .map(display-date)
    .map(list.item)
    .join()
}

date.display("[day padding:none]") does work, but due to super() call it can’t be called in a single function, so you would have to write something like

#{
  date.display("[day padding:none]")
  super(ord)
  date.display(" [month repr:long] [year]")
}

Instead of

#{
  str(day) + super(ord) + date.display(" [month repr:long] [year]")
}

So at this point there is just no benefit in using that over str(day).

String interpolation is not yet a thing.

for the ordinals, you could also make use of nth – Typst Universe :slight_smile:

1 Like

Right! I do remember seeing this packages a long time ago, but completely forgot about it.