Posted by

Dates and Times in HubSpot CMS: Format Them Correctly with HubL format_datetime

Dates look simple until a visitor in another country sees an event at the wrong hour, or a blog card shows 1730764800000 instead of a readable date. HubL has built-in filters for this. This article shows how to format dates and times in HubSpot CMS templates and custom modules, and how to avoid the usual time zone and locale mistakes.

The filters you need

HubL provides three formatting filters for date and time values:

  • format_date: outputs only the date part.
  • format_time: outputs only the time part.
  • format_datetime: outputs both.

Each one accepts a format, a time zone and a locale. The older datetimeformat filter still appears in many existing themes, but new code should use the format_* filters.

A first example

The blog post publish_date is a good test value, because it is available in every blog post template:

<time datetime="{{ content.publish_date | format_datetime('yyyy-MM-dd') }}">
  {{ content.publish_date | format_datetime('medium') }}
</time>

Two things are happening here. The datetime attribute uses a fixed machine-readable pattern, which helps browsers and search engines. The visible text uses the named format medium, which gives a shorter, human-friendly result that adapts to the locale. Named formats are short, medium, long and full. You can also pass a custom pattern such as 'MMMM d, yyyy'.

Always think about the time zone

A stored timestamp is a single moment in time. Formatting decides which clock it is shown on. If you do not specify a time zone, HubSpot uses a default, and that may not be the one your audience expects. For events, webinars and deadlines, set it explicitly:

{{ module.event_start | format_datetime('MMMM d, yyyy h:mm a', 'Asia/Manila') }}

Use IANA time zone names such as Europe/Brussels or America/New_York, not abbreviations like EST, which are ambiguous. It also helps visitors if you print the zone name as text next to the time.

Locale: month names and ordering

The same date is written differently around the world. Passing a locale as the third argument changes month names and ordering for the named formats:

{{ content.publish_date | format_datetime('long', 'Europe/Brussels', 'nl-BE') }}
{{ content.publish_date | format_datetime('long', 'Europe/Brussels', 'fr-BE') }}

On multi-language sites, prefer named formats over hand-written patterns. A pattern such as 'MM/dd/yyyy' is ambiguous to many readers outside the United States, while a named format lets the locale decide.

Dates in custom module fields

A custom module can include a date or datetime field. In fields.json:

[
  {
    "name": "event_start",
    "label": "Event start",
    "type": "datetime",
    "required": true
  }
]

In module.html, the field value is a timestamp, so pass it through a formatting filter and guard against empty values:

{% if module.event_start %}
  <time datetime="{{ module.event_start | format_datetime('yyyy-MM-dd', 'Asia/Manila') }}">
    {{ module.event_start | format_datetime('full', 'Asia/Manila') }}
  </time>
{% endif %}

Test this in the editor with a real value, and check the rendered HTML in your browser's inspector. Pattern syntax is the easiest place to make a typo.

Timestamps from HubDB and other sources

Date columns in HubDB tables are returned as timestamps, in milliseconds. The same formatting filters work on them:

{% for row in hubdb_table_rows(123456789) %}
  <li>{{ row.title }}: {{ row.event_date | format_date('medium', 'Europe/Brussels') }}</li>
{% endfor %}

Replace 123456789 with your own table ID. If a value displays as 1970, you most likely passed seconds where milliseconds were expected, or the value was empty.

Hiding past events

To hide past events, compare timestamps rather than formatted strings, and filter in the data source where you can (for example with a HubDB query filter on the date column). Remember that pages can be cached, so a page may keep showing an event for a short time after it ends. If an event must disappear at an exact moment, add a small piece of JavaScript as well.

Checklist

  • Use format_date, format_time or format_datetime instead of the older datetimeformat.
  • Set the time zone explicitly for events and deadlines, using IANA names.
  • Prefer named formats on multilingual sites and pass a locale.
  • Wrap dates in a <time> element with a machine-readable datetime attribute.
  • Guard optional date fields with {% if %}.
  • Check unexpected 1970 dates for a seconds versus milliseconds mix-up.

Sources

  • HubSpot Developer Documentation: HubL filters (format_date, format_time, format_datetime)
  • HubSpot Developer Documentation: HubL variables and module fields (date and datetime field types)
  • HubSpot Developer Documentation: HubDB and hubdb_table_rows

Filter signatures can change. Confirm the argument order and available formats in the current HubSpot documentation before using them in production.