On a latest episode of Syntax.fm, Wes Bos, Scott Tolinski, and visitor Scott Jehl mentioned the present panorama of “Internet Parts” (aka, customized parts). I have not labored with customized parts straight; however, I’ve been taking part in round rather a lot with Alpine.js. And, one factor that Jehl talked about that caught my ear was using customized parts to format date/time values on the client-side. I wished to see what it’d appear like to carry out this process with an Alpine.js element.
Run this demo in my JavaScript Demos challenge on GitHub.
View this code in my JavaScript Demos challenge on GitHub.
Formatting date/time values in a user-facing software is hard as a result of dates are sometimes saved in UTC time after which rendered in varied timezones all over the world. So, a given date that’s 11am for one person may truly be 3am for a unique person. Which suggests, though it is perhaps best to simply format a date/time worth as-is, it is usually not the most effective expertise for the customers.
To translate date/time values right into a person’s native timezone, we are able to both carry out the interpretation on the server-side utilizing a desire setting; or, we are able to parse a date/time worth within the browser, which can routinely translate it into the person’s native timezone, after which render it on the client-side. On this experiment, we’ll be doing somewhat of each.
There is no such thing as a server-side rendering on this experiment; however, let’s assume that now we have a server-side rendered template which will probably be progressively enhanced by Alpine.js on the client-side. When the server-side renders a date, it is going to achieve this utilizing the UTC time:
Nov 20, 2024
However, it is going to wrap the rendered date in an HTML <time>
factor so as to present the unique date (in UTC) and the date masks which it used to format the server-side date:
<time datetime="..." data-mask="...">Nov 20, 2024</time>
Then, we’ll apply an x-data
attribute to bind this <time>
factor to an Alpine.js element. This Alpine.js element will parse the datetime
attribute into an area Date
occasion; and change the textContent
of the host factor with a formatted date-string utilizing the given masks.
To be clear, this is not supposed to be an Internationalization (I18n) demo—the masks that I am utilizing right here is not locale-specific. That is solely an exploration of utilizing Alpine.js to rerender dates within the native timezone.
With that stated, here is a proof-of-concept. There are two completely different dates, every of which can obtain a separate occasion of the Alpine.js element.
<!doctype html>
<html lang="en">
<physique>
<h1>
Formatting Dates In The Native Timezone With Alpine.js
</h1>
<!--
Within the following examples, let's assume that the already-rendered date string was
rendered on the server utilizing the UTC timezone.
-->
<p>
<time
x-data="LocalDateFormat"
data-mask="mmmm d, yyyy 'at' HH:mmtt"
datetime="2024-11-17T16:38:48Z">
Nov 17, 2024 UTC <!-- Formatted in UTC. -->
</time>
</p>
<p>
<time
x-data="LocalDateFormat"
data-mask="mmmm d, yyyy 'at' HH:mmtt"
datetime="2024-11-20T14:40:42.832Z">
Nov 20, 2024 UTC <!-- Formatted in UTC. -->
</time>
</p>
<script sort="textual content/javascript" src="https://www.bennadel.com/weblog/vendor/alpine/3.13.5/alpine.3.13.5.min.js" defer></script>
<script sort="textual content/javascript">
/**
* This Alpine.js element replaces the text-content of the host factor with a
* date-string formatted within the person's native timezone. It does this by parsing the
* datetime attribute into an area Date object after which re-masking it.
*
* Word: This isn't an Internationalization approach - it would not use the Intl
* module, although I am positive it could possibly be up to date to take action. That is extra of a higher-
* degree exploration client-side date-formatting.
*/
operate LocalDateFormat() {
// In a <time> factor, the "datetime" attribute is meant to characterize a
// interval in time. For the sake of this demo, I'll assume that the
// attribute comprises a full UTC date/time worth.
var date = new Date( this.$el.getAttribute( "datetime" ) );
var masks = this.$el.dataset.masks;
// So as to translate the server-side date formatting right into a client-side
// context within the person's native timezone, this Alpine.js element expects a
// date masks to be offered as a data-attribute. The textual content content material of the host
// will probably be changed with the interpolation of the date components and the masks.
this.$el.textContent = masks.change(
/'([^']*)'|y+|m+|d+|H+|h+|n+|s+|T+|t+/g,
( $0, $1 ) => {
// Return escaped string (much less the encircling quotes).
if ( $1 ) {
return $1;
}
return translations[ $0 ]( date );
}
);
}
// Utility strategies for making use of masks components to a given date.
var translations = {
yyyy: ( date ) => String( date.getFullYear() ),
yy: ( date ) => String( date.getYear() - 100 ), // Deprecated, by no means use brief 12 months.
mmmm: ( date ) => String( monthNames[ date.getMonth() ].lengthy ),
mmm: ( date ) => String( monthNames[ date.getMonth() ].brief ),
mm: ( date ) => String( date.getMonth() ).padStart( 2, "0" ),
m: ( date ) => String( date.getMonth() ),
dddd: ( date ) => String( dayNames[ date.getDate() ].lengthy ),
ddd: ( date ) => String( dayNames[ date.getDate() ].brief ),
dd: ( date ) => String( date.getDate() ).padStart( 2, "0" ),
d: ( date ) => String( date.getDate() ),
HH: ( date ) => String( date.getHours() ).padStart( 2, "0" ),
H: ( date ) => String( date.getHours() ),
hh: ( date ) => String( 12 % date.getHours() ).padStart( 2, "0" ),
mm: ( date ) => String( date.getMinutes() ).padStart( 2, "0" ),
m: ( date ) => String( date.getMinutes() ),
ss: ( date ) => String( date.getSeconds() ).padStart( 2, "0" ),
s: ( date ) => String( date.getSeconds() ),
TT: ( date ) => String( date.getHours() >= 12 ? "PM" : "AM" ),
tt: ( date ) => String( date.getHours() >= 12 ? "pm" : "am" )
};
var monthNames = [
{ short: "Jan", long: "January" },
{ short: "Feb", long: "February" },
{ short: "Mar", long: "March" },
{ short: "Apr", long: "April" },
{ short: "May", long: "May" },
{ short: "Jun", long: "June" },
{ short: "Jul", long: "July" },
{ short: "Aug", long: "August" },
{ short: "Sep", long: "September" },
{ short: "Oct", long: "October" },
{ short: "Nov", long: "November" },
{ short: "Dec", long: "December" }
];
var dayNames = [
{ short: "Sun", long: "Sunday" },
{ short: "Mon", long: "Monday" },
{ short: "Tue", long: "Tuesday" },
{ short: "Wed", long: "Wednesday" },
{ short: "Thr", long: "Thursday" },
{ short: "Fri", long: "Friday" },
{ short: "Sat", long: "Saturday" }
];
</script>
</physique>
</html>
There isn’t any error dealing with on this code; and, it isn’t supposed to be exhaustive in its choices. I solely wished to discover the idea. And, after we run this code, we find yourself with the next output:
As you may see, the textContent
of every <time>
factor has been up to date to render the given UTC date in my native timezone utilizing the given date masks.
If the JavaScript had been to fail to load or error-out, the person would nonetheless see a date with the server-rendered UTC labeling. However, as soon as the JavaScript kicks-in, they are going to see the date in their very own timezone (and with out the UTC label).
Clearly this solely works in a web-based context the place Alpine.js can run; so, it would not be relevant for duties like doc technology or exports. However, it is a very compelling notion for an online software.
Need to use code from this submit?
Try the license.
https://bennadel.com/4730