I'm migrating an old piece of code from Moment to Luxon and bumped into the function below:
export const TIME_FORMATTER = {
HOUR: (t) => moment(t).format('hh:mm'),
DAY: (t) => moment(t).format('M/DD'),
HOUR_PERIOD: (t) => moment(t).format('hh:mm A'),
DAY_HOUR_PERIOD: (t) => moment(t).format('hh:mm A M/DD'),
SEC: (t) => moment(t).format('HH:mm:ss')
};
which gets called as follows:
export const getFormatterByTimeInterval = (timeInterval: number) => {
if (timeInterval <= 240) {
return TIME_FORMATTER.DAY_HOUR_PERIOD;
}
else if (timeInterval > 480) {
return TIME_FORMATTER.HOUR_PERIOD;
}
else {
return TIME_FORMATTER.DAY;
}
}
My initial thought was to use the same call format with Luxon:
const TIME_FORMATTER_LUXON = {
HOUR: (t) => DateTime.local(t).toFormat("hh:mm"),
DAY: (t) => DateTime.local(t).toFormat("M/d"),
HOUR_PERIOD: (t) => DateTime.local(t).toFormat("t"),
DAY_HOUR_PERIOD: (t) => DateTime.local(t).toFormat("t M/d"),
SEC: (t) => DateTime.local(t).toFormat("HH:mm:ss")
};
This gets called on another file const formatter = getFormatterByTimeInterval(timeInterval);
but it's returning Invalid DateTime
Is this possible with Luxon? What can be the issue?
CodePudding user response:
It seems to me like your old code might have a few issues, so I want to at least address them before responding to your question:
The only function signature for moment
which accepts a number
argument is Unix Timestamp (milliseconds):
Similar to
new Date(Number)
, you can create a moment by passing an integer value representing the number of milliseconds since the Unix Epoch (Jan 1 1970 12AM UTC).const day = moment(1318781876406);
I'm not sure what the timeInterval
parameter represents in your formatter resolver function:
function getFormatterByTimeInterval (timeInterval: number) {/* ... */}
but I'll ignore that for now. With that out of the way, let's look at the conditionals:
export const getFormatterByTimeInterval = (timeInterval: number) => {
// This formats as time and date information
if (timeInterval > 240) {
return TIME_FORMATTER.DAY_HOUR_PERIOD;
}
// This formats as time-only information
// Also, this will never match because any `timeInterval` value which is
// greater than 480 is also greater than 240, so the first conditional above
// will match and return before reaching this one
else if (timeInterval > 480) {
return TIME_FORMATTER.HOUR_PERIOD;
}
// Shorter than either of the above
// This formats as date-only information
else {
return TIME_FORMATTER.DAY;
}
}
I'm not sure how your program is supposed to work, but that doesn't seem like the intended behavior to me.
In regard to your question of conversion:
The Luxon
equivalent for instantiation of a DateTime
from a milliseconds number
type is DateTime.fromMillis()
. (You don't need to worry about using the local
method because luxon uses your system's local timezone by default.)
In regard to the equivalent formatting options, they would be:
moment | luxon |
---|---|
hh:mm |
hh:mm |
M/DD |
L/dd |
hh:mm A |
hh:mm a |
hh:mm A M/DD |
hh:mm a L/dd |
HH:mm:ss |
HH:mm:ss |
Expressed as code, it looks like this:
import {DateTime} from 'luxon';
export const TIME_FORMATTER = {
HOUR: (millis: number) => DateTime.fromMillis(millis).toFormat('hh:mm'),
DAY: (millis: number) => DateTime.fromMillis(millis).toFormat('L/dd'),
HOUR_PERIOD: (millis: number) => DateTime.fromMillis(millis).toFormat('hh:mm a'),
DAY_HOUR_PERIOD: (millis: number) => DateTime.fromMillis(millis).toFormat('hh:mm a L/dd'),
SEC: (millis: number) => DateTime.fromMillis(millis).toFormat('HH:mm:ss'),
};
Then, a small refactor of your resolver:
type FormatterFn = (millis: number) => string;
export const getFormatterByTimeInterval = (timeInterval: number): FormatterFn => {
let key: keyof typeof TIME_FORMATTER;
// I'm still not sure about these conditionals,
// but this order matches longest duration first
if (timeInterval > 480) key = 'HOUR_PERIOD';
else if (timeInterval > 240) key = 'DAY_HOUR_PERIOD';
else key = 'DAY';
return TIME_FORMATTER[key];
};
And you can check it like this:
// The time you asked this question
const millis = DateTime.fromISO('2022-03-21T21:02:03Z').toMillis();
// Check each conditional range:
for (const timeInterval of [500, 300, 100]) {
const formatter = getFormatterByTimeInterval(timeInterval);
console.log(formatter(millis));
}
Code in the TypeScript Playground
Demo of compiled JavaScript from the TS playground link:
<script type="module">
import {DateTime} from 'https://unpkg.com/[email protected]/src/luxon.js';
export const TIME_FORMATTER = {
HOUR: (millis) => DateTime.fromMillis(millis).toFormat('hh:mm'),
DAY: (millis) => DateTime.fromMillis(millis).toFormat('L/dd'),
HOUR_PERIOD: (millis) => DateTime.fromMillis(millis).toFormat('hh:mm a'),
DAY_HOUR_PERIOD: (millis) => DateTime.fromMillis(millis).toFormat('hh:mm a L/dd'),
SEC: (millis) => DateTime.fromMillis(millis).toFormat('HH:mm:ss'),
};
export const getFormatterByTimeInterval = (timeInterval) => {
let key;
// I'm still not sure about these conditionals,
// but this order matches longest duration first
if (timeInterval > 480)
key = 'HOUR_PERIOD';
else if (timeInterval > 240)
key = 'DAY_HOUR_PERIOD';
else
key = 'DAY';
return TIME_FORMATTER[key];
};
// The time you asked this question
const millis = DateTime.fromISO('2022-03-21T21:02:03Z').toMillis();
// Check each conditional range:
for (const timeInterval of [500, 300, 100]) {
const formatter = getFormatterByTimeInterval(timeInterval);
console.log(timeInterval, formatter(millis));
}
</script>