Converting Datetimes to Local & Relative with Vue
Jan 18, 2021 • Ben Sochar
This builds on my previous post about implimenting this in Rails. Unlike my other post, this will only use VueJS.
Yarn or NPM date-fns. I only needed US format but date-fns supports others. We’ll also import realtive formatting & checks for same year/week as the current date so we can cleanup the date.
There’s the optonal removal of the “text-muted” styles once the formatting is complete.
// frontend/src/javascripts/dateTimeHelper.js
'use strict'
export const supportedLocales = ['en-US']
import { format } from 'date-fns-tz'
import { isValid, formatRelative, isThisWeek, isThisYear, getDay } from 'date-fns'
export default function dateTimeHelper (ele) {
let str = ''
const nowDate = new Date()
const eleData = ele.dataset
let dateFormat = eleData.dateformat
const dateVal = Date.parse(JSON.parse(eleData.datevalue))
// We don't need to know the year if it's the same as the current year
let dateFormatWithYear = function(format) {
if (isThisYear(dateVal)) {
return dateFormat.replace('yyyy', '')
} else {
return format
}
}
if (isValid(dateVal)) {
const offsetWeekDay = getDay(nowDate)
// We can drop the day of the week if it's the same as the current week
if (isThisWeek(dateVal, { weekStartsOn: offsetWeekDay })) {
let realtiveStr = formatRelative(dateVal, nowDate, { weekStartsOn: offsetWeekDay }) + ' ' + format(dateVal, 'z')
str = realtiveStr.replace(/^\w/, (c) => c.toUpperCase())
} else {
str = format(dateVal, dateFormatWithYear(dateFormat))
}
ele.textContent = str
ele.classList.remove('text-muted')
} else {
ele.classList.remove('text-muted')
}
}
document.addEventListener('turbolinks:load', () => {
const eles = document.querySelectorAll('.js-local-time')
Array.prototype.forEach.call(eles, function(ele) {
dateTimeHelper(ele)
})
})
And now our Vue component. We only need 2 props. A date & date format. Both are strings.
<template>
<time class="text-muted transition-all" :data-datevalue="localValue" :data-dateformat="localFormat"></time>
</template>
<script>
import dateTimeHelper from '../javascripts/dateTimeHelper.js'
const dateFormats = dateTimeConfig()
export default {
name: 'LocalDateTime',
props: {
dateValue: {
type: String,
required: true
},
dateFormat: {
type: String,
default: 'iiii, MMM d, yyyy h:mm a z'
}
},
computed: {
localValue: function () {
return JSON.stringify(this.dateValue)
},
localFormat: function () {
return this.dateFormat
}
},
mounted () {
let self = this
dateTimeHelper(self.$el)
}
}
</script>
Now we can use our date time component in an Event card:
<template v-if="event">
<div class="card border-0 h-100 position-relative">
<card-header :title="event.title" :link="event.object_path"></card-header>
<div class="card-body p-0 border-0">
<p class="card-text text-muted small"></p>
<local-date-time :date-value="event.start_date_change_zone"></local-date-time>
</div>
</div>
</template>
<script>
export default {
components: {
LocalDateTime: () => import('LocalDateTime.vue')
},
name: 'EventCard',
props: {
event: {
type: Object,
required: true
}
}
}
</script>
VueJs, date-fns, dates, & javascript