Date and time handling in NextJs with Sitecore
How to properly handle dates, timezones and formatting while using NextJS for headless solutions
NextJsSitecore XMJavascriptHeadless
| Reading Time: 6 Minutes, 9 Seconds
2024-01-10
When using traditional traditional (monolith) approach for Sitecore, the complexity in date handling is often solved more or less automatically.
With headless, things are not really different but it seems like it is relevant to remind us of date handling, hereby this blog post. Sitecore also have documentation with Date/time best practices
In the database, all dates should be persisted as UTC dates and that is also the case for Sitecore. That is important as it will allow moving the database between data centers, switch on-prem vs. cloud hosting etc. When reading and showing dates, we must handle the relevant timezone.
In the Sitecore client the timezone used for the UI is controlled by the setting ServerTimeZone
so you would create a patch file like:
|
|
Here by, in Sitecore UI you will see the UTC time in raw values, while when using the Date field, the time is for the specified (local) time zone.
With traditional monolith/ASP.Net MVC approach
When developing with ASP.Net MVC in the same process it is simple enough that. Sitecore provides the dedicated Sitecore.DateUtil
methods Sitecore.Kernel and hereby the UTC dates are transformed.
These methods are also called when using the renderField pipeline or when using eg. GlassMapper.
When using Next.JS
As we the database contains the date values in UTC it is not really a surprise that we also receive those from the Layout service.
The equivalent statement for initializing the date shown above is
|
|
For rendering in javascript, often the moment.js is used to transform from UTC to local timezones.
However, there is actually a more native approach with the Intl functions. Hereby you can render date, time and handle timezone by instantiating an Intl.DateTimeFormat
which can be configured in a lot of different ways.
Formatting times
|
|
The two different components, language and timezone, are specified and handled individually.
The locale or language code handles how the time should be formatted.
locale | timeStyle short | timeStyle long |
---|---|---|
en-US | 3:00 PM | 3:00:00 PM GMT+1 |
en-GB | 15:00 | 15:00:00 CET |
da-DK | 15.00 | 15.00.00 CET |
fr-FR | 15:00 | 15:00:00 UTC+1 |
on the other hand the timezone handles transforming the actual time, so you might want to combine those
locale | timezone | timeStyle short | timeStyle long |
---|---|---|---|
en-US | America/New_York | 9:00 AM | 9:00:00 AM EST |
en-GB | Europe/London | 14:00 | 14:00:00 GMT |
da-DK | Europe/Copenhagen | 15.00 | 15.00.00 CET |
fr-FR | Europe/Paris | 15:00 | 15:00:00 UTC+1 |
Formatting dates
Similar, for formatting dates you can specify a dateStyle
|
|
Hereby both formatting and translating of month names are handled.
Eg. when running this for the date new Date(2024, 1, 2)
locale | dateStyle medium | dateStyle short | dateStyle long |
---|---|---|---|
en-US | Feb 1, 2024 | 2/1/24 | February 1, 2024 |
en-GB | 1 Feb 2024 | 01/02/2024 | 1 February 2024 |
da-DK | 1. feb. 2024 | 01.02.2024 | 1. februar 2024 |
fr-FR | 1 févr. 2024 | 01/02/2024 | 1 février 2024 |
More advanced scenarios
Ok, so the DateTimeFormat actually handles the date and time part very well and remove a lot of complexity from the custom application.
If/when both date and time should be rendered, you just specify both - and this also handles how to separate date and time
|
|
locale | dateStyle long and timeStyle long |
---|---|
en-US | January 2, 2024 at 3:00:00 PM GMT+1 |
en-GB | 2 January 2024 at 15:00:00 CET |
da-DK | 2. januar 2024 kl. 15.00.00 CET |
fr-FR | 2 janvier 2024 à 15:00:00 UTC+1 |
Even more advanced, when you want to format a start and end date, the Intl.DateTimeFormat also have a method for this and handles how complex the range should be represented:
|
|
startDate | endDate | result |
---|---|---|
2024-01-02T10:00:00Z | 2024-01-02T14:00:00Z | 2. jan. 2024 11.00–15.00 |
2024-01-02T10:00:00Z | 2024-01-03T14:00:00Z | 2. jan. 2024 11.00-3. jan. 2024 15.00 |
Ok, but I have some very specific requirements for how the date should be formatted, the day of the week should be included, no seconds, timezone must be there… No worries, DateTimeFormat still have you covered:
|
|
Well, that is fine, but I also need some different elements and styling for various parts of the dates. Ok, there is a method for you so you can handle that. With formatToParts
or formatRangeToParts
you get the individual parts and can create the elements with classNames that you want:
|
|
Ok this Intl.DateTimeFormat is pretty cool. There is even more configuration options that I haven’t covered here (different calendars, numbering systems, )1
A few word on hydrating
We are using the pages router in NextJS as the app router is currently not supported with Sitecore JSS. Hereby the components are rendered server side/statically and served to the client, on the client the components are rendered again in the hydration phase with the same properties to activate any behaviors.
If the result of the server provided and the client side rendered are not equal you will get a client side hydration error.
It is tempting to just use the timezone of the browser, and you can do that by leaving out the timezone from the options. but that will give this hydration error. So, you must make sure that all of those options are configured consistently, or render those to properties within a Sitecore supported getServerSideComponentProps
so the client is just using the rendered value - or alternatively only render the values on the client.
Browser support
When using javascript APIs it is always good to consider how well this is supported and if it matches your requirements. From my point of view the support is pretty good, all browsers support this is the newest version2.
However, as always do your testing. If you use some very specific features (number systems, to parts) you might end up in a scenario that is not that well supported. Then consider using the api server side only and just use that serialized output from the component props.
For formatting of numbers
It is not really related to dates, but there is also a similar Intl.NumberFormat
to render numbers. It can also handle currency formatting and even translations of a lot of common units. Go explore the documentation3…