Module 7: Dates and Date Formatting
How Dates Work in Liquid
Section titled “How Dates Work in Liquid”Dates in Liquid are not a separate data type — they’re strings that the date filter knows how to interpret. When you apply the date filter, Liquid parses the string into a date, then reformats it according to the pattern you specify.
Excel analogy: This is like using
=TEXT(A1, "MMMM DD, YYYY")to change how a date cell is displayed. The underlying date value doesn’t change — you’re just choosing a different way to show it.
Why it matters in Cast
Section titled “Why it matters in Cast”Date fields from CRMs typically arrive in formats like "2026-01-15" or "2026-01-15T09:00:00Z". These are accurate but not customer-friendly. The date filter lets you transform them into natural, readable formats like “January 15, 2026” for display in narrations and on slides.
The date Filter — Formatting Dates for Display
Section titled “The date Filter — Formatting Dates for Display”Liquid syntax
Section titled “Liquid syntax”{% raw %}{{ some_date | date: "FORMAT_STRING" }}{% endraw %}The format string uses special codes that start with % to represent different parts of a date:
| Code | Meaning | Example |
|---|---|---|
%B |
Full month name | January |
%b |
Abbreviated month name | Jan |
%m |
Month number (01–12) | 01 |
%d |
Day of month (01–31) | 15 |
%e |
Day of month (1–31, no leading zero) | 5 |
%Y |
4-digit year | 2026 |
%y |
2-digit year | 26 |
%A |
Full weekday name | Thursday |
%a |
Abbreviated weekday name | Thu |
%H |
Hour in 24-hour format (00–23) | 14 |
%I |
Hour in 12-hour format (01–12) | 02 |
%M |
Minutes (00–59) | 30 |
%S |
Seconds (00–59) | 00 |
%p |
AM/PM | PM |
%s |
Unix timestamp (seconds since 1970-01-01) | 1737000000 |
%w |
Day of week (0=Sunday, 6=Saturday) | 4 |
Any text in the format string that is not a % code is included literally — so commas, spaces, slashes, and words like “of” pass through unchanged.
Common format patterns
Section titled “Common format patterns”{% raw %}{{ renewal_date | date: "%B %d, %Y" }} → January 15, 2026{{ renewal_date | date: "%b %d, %Y" }} → Jan 15, 2026{{ renewal_date | date: "%m/%d/%Y" }} → 01/15/2026{{ renewal_date | date: "%Y-%m-%d" }} → 2026-01-15{{ renewal_date | date: "%B %Y" }} → January 2026{{ renewal_date | date: "%A, %B %d" }} → Thursday, January 15{{ renewal_date | date: "%b %e, %Y" }} → Jan 5, 2026 (no leading zero on day){% endraw %}Real Cast example
Section titled “Real Cast example”{% raw %}{%- assign renewal = renewal_date | default: "" -%}
Your contract renews on {{ renewal | date: "%B %d, %Y" }}.{% else %} We don't have a renewal date on file.
{% endraw %}If renewal_date is "2026-09-15":
Output: Your contract renews on September 15, 2026.
Common mistakes
Section titled “Common mistakes”❌ Mixing up %m (month number) and %M (minutes):
{% raw %}{{ renewal_date | date: "%B %d, %Y at %m:%S" }}{% endraw %}%m gives you the month number (like 09), not the minutes. Use %M for minutes.
✅ Use %M for minutes, %m for month:
{% raw %}{{ renewal_date | date: "%B %d, %Y at %H:%M" }} → September 15, 2026 at 14:30{% endraw %}❌ Forgetting quotes around the format string:
{% raw %}{{ renewal_date | date: %B %d, %Y }}{% endraw %}✅ Always quote the format string:
{% raw %}{{ renewal_date | date: "%B %d, %Y" }}{% endraw %}❌ Applying date to an empty or nil field without a guard:
{% raw %}{{ renewal_date | date: "%B %d, %Y" }}{% endraw %}If renewal_date is nil, this may produce unexpected output or error in strict mode.
✅ Guard with a blank check or default:
{% raw %}{%- assign renewal = renewal_date | default: "" -%}{% unless renewal == "" %} Renews on {{ renewal | date: "%B %d, %Y" }}.{% endunless %}{% endraw %}Working with the Current Date — "now" and "today"
Section titled “Working with the Current Date — "now" and "today"”Liquid recognizes two special input values for the date filter:
"now"— the current date and time"today"— the current date (equivalent to"now"in most implementations)
{% raw %}{{ "now" | date: "%B %d, %Y" }} → March 23, 2026{{ "today" | date: "%Y-%m-%d" }} → 2026-03-23{{ "now" | date: "%A" }} → Monday{% endraw %}Real Cast example — benchmark date reference
Section titled “Real Cast example — benchmark date reference”{% raw %}{%- assign benchmarkMMYYYY = "now" | date: "%B %Y" -%}Here are your metrics for the {{ benchmarkMMYYYY }} benchmark.{% endraw %}Output: Here are your metrics for the March 2026 benchmark.
Real Cast example — dynamic copyright or timestamp
Section titled “Real Cast example — dynamic copyright or timestamp”{% raw %}Data as of {{ "now" | date: "%B %d, %Y" }}.{% endraw %}Output: Data as of March 23, 2026.
Unix Timestamps — The Key to Date Math
Section titled “Unix Timestamps — The Key to Date Math”What is it?
Section titled “What is it?”A Unix timestamp is the number of seconds that have elapsed since January 1, 1970. It’s a single number (like 1711152000) that represents a specific moment in time. Unix timestamps are how Liquid performs date arithmetic — because once dates are numbers, you can subtract them, add to them, and divide the result to get days, hours, or months.
Excel analogy: Excel stores dates as numbers internally (days since January 1, 1900). When you subtract two dates, you get a number of days. Unix timestamps work the same way, but in seconds instead of days.
Why it matters in Cast
Section titled “Why it matters in Cast”Liquid doesn’t have built-in “date difference” or “days between” functions. To calculate time differences — days until renewal, months since contract start, duration of a support ticket — you convert both dates to Unix timestamps, subtract, and then convert the difference in seconds to the unit you need.
Liquid syntax
Section titled “Liquid syntax”Convert any date to a Unix timestamp:
{% raw %}{{ "2026-01-15" | date: "%s" }} → 1736899200 (or similar){{ "now" | date: "%s" }} → current Unix timestamp{% endraw %}Convert seconds to useful units:
| Seconds | Equals |
|---|---|
| 60 | 1 minute |
| 3,600 | 1 hour |
| 86,400 | 1 day |
| 604,800 | 1 week |
| 2,592,000 | ~1 month (30 days) |
| 31,536,000 | ~1 year (365 days) |
Date Math — Calculating Days Until Renewal
Section titled “Date Math — Calculating Days Until Renewal”Here’s the most common date arithmetic pattern in Cast:
{% raw %}{%- assign renewal_ts = renewal_date | date: "%s" | plus: 0 -%}{%- assign now_ts = "now" | date: "%s" | plus: 0 -%}{%- assign diff_seconds = renewal_ts | minus: now_ts -%}{%- assign days_until = diff_seconds | divided_by: 86400 -%}
Your renewal is in {{ days_until }} days.{% elsif days_until == 0 %} Your renewal is today!{% else %} Your renewal was {{ days_until | abs }} days ago.
{% endraw %}Let’s trace through this step by step:
renewal_date | date: "%s"— converts the renewal date to a Unix timestamp (a number in seconds)"now" | date: "%s"— gets the current moment as a Unix timestamprenewal_ts | minus: now_ts— subtracts to get the difference in secondsdiff_seconds | divided_by: 86400— divides by 86,400 (seconds per day) to get days
If renewal_date is "2026-06-15" and today is March 23, 2026:
- The difference is about 84 days
- Output:
Your renewal is in 84 days.
If the renewal date is in the past:
days_untilis negative- We use
| absto make it positive for the “X days ago” message
⚠️ Important note on | plus: 0
Section titled “⚠️ Important note on | plus: 0”The date: "%s" filter returns a string (e.g., "1736899200"). You must convert it to a number with | plus: 0 before doing arithmetic. Without this step, minus would operate on strings.
Date Math — Calculating Months and Years
Section titled “Date Math — Calculating Months and Years”For longer time spans, divide by the appropriate number of seconds:
{% raw %}{%- assign start_ts = contract_start_date | date: "%s" | plus: 0 -%}{%- assign now_ts = "now" | date: "%s" | plus: 0 -%}{%- assign diff = now_ts | minus: start_ts -%}{%- assign days = diff | divided_by: 86400 -%}{%- assign months = days | divided_by: 30 -%}{%- assign years = days | divided_by: 365 -%}
You've been a customer for over {{ years }} years.{% elsif months >= 1 %} You've been a customer for {{ months }} months.{% else %} You've been a customer for {{ days }} days.
{% endraw %}💡 These are approximate calculations (30 days per month, 365 days per year). For a more precise and polished version, see the CustomerSince snippet in Module 17.
Invalid Date Handling
Section titled “Invalid Date Handling”Not all CRM date fields contain valid dates. A field might be empty, contain garbage text, or have a default value that looks like a date but isn’t meaningful.
The date: "%s" filter on an invalid input may produce the value -2208988800 (which represents the date December 30, 1899 — a common default for blank dates in some systems). The CustomerSince snippet checks for this:
{% raw %}{%- assign dateStart = contract_start_date | date: '%s' -%}
{%- # Date is valid — proceed with calculations -%}{% else %} {%- assign timeFrame = 'Invalid Date Value' -%}
{% endraw %}💡 Best practice: Always validate date fields before performing date math. Check for blank, nil, and the -2208988800 sentinel value.
Complete Date Format Reference
Section titled “Complete Date Format Reference”For easy reference, here are all the formats you’re likely to use in Cast narrations:
| Format String | Output | When to Use |
|---|---|---|
"%B %d, %Y" |
January 15, 2026 | Formal narration text |
"%b %d, %Y" |
Jan 15, 2026 | Shorter version for slides |
"%m/%d/%Y" |
01/15/2026 | US-standard date format |
"%d/%m/%Y" |
15/01/2026 | International date format |
"%Y-%m-%d" |
2026-01-15 | ISO format (sorting, data) |
"%B %Y" |
January 2026 | Month-year only (benchmarks) |
"%A, %B %d" |
Thursday, January 15 | Day of week included |
"%b %e, %Y" |
Jan 5, 2026 | No leading zero on day |
"%s" |
1736899200 | Unix timestamp (for math) |
"%w" |
4 | Day of week number (for logic) |
Try It Yourself — Module 7 Capstone Exercise
Section titled “Try It Yourself — Module 7 Capstone Exercise”Exercise: You have two fields: qbr_date (value: "2026-05-10") and you want to display:
- The QBR date formatted as “May 10, 2026”
- How many days from today (March 23, 2026) until the QBR
- A message: “Your next QBR is in X days — that’s about Y weeks away.”
Click to reveal the answer
{% raw %}{%- assign qbr = qbr_date | default: "" -%}
{% unless qbr == "" %} {%- assign qbr_ts = qbr | date: "%s" | plus: 0 -%} {%- assign now_ts = "now" | date: "%s" | plus: 0 -%} {%- assign diff = qbr_ts | minus: now_ts -%} {%- assign days_until = diff | divided_by: 86400 -%} {%- assign weeks_until = days_until | divided_by: 7 -%}
Your next QBR is on {{ qbr | date: "%B %e, %Y" }}. That's in {{ days_until }} days — about {{ weeks_until }} weeks away.{% else %} No QBR date on file.{% endunless %}{% endraw %}Output: Your next QBR is on May 10, 2026. That's in 48 days — about 6 weeks away.
Key details:
date: "%s" | plus: 0converts both dates to numeric timestamps- Subtracting gives seconds, dividing by 86400 gives days
- Dividing days by 7 gives weeks (integer division rounds down)
- The blank check prevents errors if no date is on file
What’s Next
Section titled “What’s Next”You now know how to format, compare, and calculate with dates. In Module 8, you’ll learn about cast_titlecase — Cast’s custom filter for intelligently normalizing company names from CRM data.
📖 Official documentation:
- Standard Filters: https://school.cast.app/liquid/liquid-filters.html
- Snippet Library (CustomerSince, DateDifference): https://school.cast.app/liquid/liquid-library.html