Skip to content

Commit a4289d7

Browse files
committed
refactored to use Intl instead of date-fns #204
1 parent ef0a7e7 commit a4289d7

File tree

2 files changed

+70
-49
lines changed

2 files changed

+70
-49
lines changed

app/utils/ving/dateTime.mjs

+68-49
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { format, parseISO, parseJSON, parse, getUnixTime } from 'date-fns';
21
import { isArray, isString, isUndefined } from '#ving/utils/identify.mjs';
3-
2+
import { createError } from 'h3';
43

54
/**
65
* Figures out what kind of date it was passed and returns a javascript date object
76
*
8-
* @param input Usually a Javascript Date object, or a JSON Date string. But it can also be an array containing a date string of any type along with a [parse pattern](https://date-fns.org/v2.30.0/docs/parse) as the second element of the array. Or even an ISO date string (MySQL date).
7+
* @param input Usually a Javascript Date object, or a JSON Date string. But it can also be an array containing a date string of any type along with a format string as the second element of the array. Or even an ISO date string (MySQL date).
98
* @returns A javascript Date object
109
* @example
1110
* const date = determineDate("2012-04-23T18:25:43.511Z")
@@ -15,17 +14,17 @@ export const determineDate = (input) => {
1514
return new Date();
1615
}
1716
if (isArray(input) && isString(input[0])) {
18-
// date + input pattern
19-
return parse(input[0], input[1], new Date());
17+
// date + input pattern - attempt to parse with given format
18+
const date = new Date(input[0]);
19+
if (!isNaN(date)) return date;
20+
throw createError({ statusCode: 400, message: 'Invalid date format with pattern: ' + input });
2021
}
2122
if (input instanceof Date) {
2223
return input;
2324
}
24-
if (isString(input) && input.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/)) {
25-
return parseISO(input);
26-
}
27-
if (isString(input) && input.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+/)) {
28-
return parseJSON(input);
25+
if (isString(input)) {
26+
const date = new Date(input);
27+
if (!isNaN(date)) return date;
2928
}
3029
console.error('Have no idea what type this date is: ', input);
3130
throw createError({ statusCode: 400, message: 'Have no idea what type this date is: ' + input });
@@ -35,17 +34,32 @@ export const determineDate = (input) => {
3534
* Formats a date to a human readable string with an American time format.
3635
*
3736
* @param input Anything that `determineDate()` understands.
38-
* @param pattern Optional, defaults to `LLLL d, y h:mm a`. A [format pattern](https://date-fns.org/v2.30.0/docs/format)
39-
* @returns A formatted string. Example: `April 23, 2012 6:25pm`
37+
* @param options Optional Intl.DateTimeFormat options, defaults to showing full month, day, year, hour and minute in 12-hour format
38+
* @returns A formatted string. Example: `April 23, 2012 6:25 PM`
4039
* @example
41-
* const formatted = formatDate("2012-04-23T18:25:43.511Z")
42-
40+
* const formatted = formatDateTime("2012-04-23T18:25:43.511Z")
41+
*/
42+
/**
43+
* @typedef {Object} DateTimeFormatOptions
44+
* @property {'numeric'|'2-digit'|'long'|'short'|'narrow'} [month='long']
45+
* @property {'numeric'|'2-digit'} [day='numeric']
46+
* @property {'numeric'|'2-digit'} [year='numeric']
47+
* @property {'numeric'|'2-digit'} [hour='numeric']
48+
* @property {'numeric'|'2-digit'} [minute='2-digit']
49+
* @property {boolean} [hour12=true]
4350
*/
4451

45-
export const formatDateTime = (input, pattern = "LLLL d, y h:mm a") => {
52+
export const formatDateTime = (input, /** @type {DateTimeFormatOptions} */ options = {
53+
month: 'long',
54+
day: 'numeric',
55+
year: 'numeric',
56+
hour: 'numeric',
57+
minute: '2-digit',
58+
hour12: true
59+
}) => {
4660
try {
4761
const date = determineDate(input);
48-
return format(date, pattern)
62+
return new Intl.DateTimeFormat('en-US', options).format(date);
4963
}
5064
catch {
5165
return 'bad date object';
@@ -56,13 +70,24 @@ export const formatDateTime = (input, pattern = "LLLL d, y h:mm a") => {
5670
* Formats a date to a human readable string.
5771
*
5872
* @param input Anything that `determineDate()` understands.
59-
* @param pattern Optional, defaults to `"LLLL d, y"`. A [format pattern](https://date-fns.org/v2.30.0/docs/format)
73+
* @param options Optional Intl.DateTimeFormat options, defaults to showing full month, day, and year
6074
* @returns A formatted string. Example: `April 23, 2012`
6175
* @example
6276
* const formatted = formatDate("2012-04-23T18:25:43.511Z")
6377
*/
64-
export const formatDate = (input, pattern = "LLLL d, y") => {
65-
return formatDateTime(input, pattern);
78+
/**
79+
* @typedef {Object} DateFormatOptions
80+
* @property {'numeric'|'2-digit'|'long'|'short'|'narrow'} [month='long']
81+
* @property {'numeric'|'2-digit'} [day='numeric']
82+
* @property {'numeric'|'2-digit'} [year='numeric']
83+
*/
84+
85+
export const formatDate = (input, /** @type {DateFormatOptions} */ options = {
86+
month: 'long',
87+
day: 'numeric',
88+
year: 'numeric'
89+
}) => {
90+
return formatDateTime(input, options);
6691
}
6792

6893
/**
@@ -74,35 +99,29 @@ export const formatDate = (input, pattern = "LLLL d, y") => {
7499
* const formatted = formatTimeAgo("2012-04-23T18:25:43.511Z")
75100
*/
76101
export const formatTimeAgo = (input) => {
77-
const duration = getUnixTime(new Date()) - getUnixTime(determineDate(input));
78-
const abs_dur = Math.abs(duration);
79-
let message = '';
80-
if (abs_dur < 60) {
81-
message = Math.round(abs_dur).toString();
82-
message += message == '1' ? " second" : " seconds";
83-
} else if (abs_dur < 3600) {
84-
message = Math.round(abs_dur / 60).toString();
85-
message += message == '1' ? " minute" : " minutes";
86-
} else if (abs_dur < 86400) {
87-
message = Math.round(abs_dur / 3600).toString();
88-
message += message == '1' ? " hour" : " hours";
89-
} else if (abs_dur < 604800) {
90-
message = Math.round(abs_dur / 86400).toString();
91-
message += message == '1' ? " day" : " days";
92-
} else if (abs_dur < 2419200) {
93-
message = Math.round(abs_dur / 604800).toString();
94-
message += message == '1' ? " week" : " weeks";
95-
} else if (abs_dur < 31536000) {
96-
message = Math.round(abs_dur / 2419200).toString();
97-
message += message == '1' ? " month" : " months";
98-
} else {
99-
message = Math.round(abs_dur / 31536000).toString();
100-
message += message == '1' ? " year" : " years";
101-
}
102-
if (duration < 0) {
103-
message += " from now";
104-
} else {
105-
message += " ago";
102+
const date = determineDate(input);
103+
const now = new Date();
104+
const diffInSeconds = (now - date) / 1000;
105+
const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'always' });
106+
107+
const intervals = [
108+
{ unit: 'year', seconds: 31536000 },
109+
{ unit: 'month', seconds: 2419200 },
110+
{ unit: 'week', seconds: 604800 },
111+
{ unit: 'day', seconds: 86400 },
112+
{ unit: 'hour', seconds: 3600 },
113+
{ unit: 'minute', seconds: 60 },
114+
{ unit: 'second', seconds: 1 }
115+
];
116+
117+
for (const interval of intervals) {
118+
const value = Math.round(Math.abs(diffInSeconds) / interval.seconds);
119+
if (value >= 1 || interval.unit === 'second') {
120+
return formatter.format(
121+
diffInSeconds > 0 ? -value : value,
122+
interval.unit
123+
);
124+
}
106125
}
107-
return message;
126+
return formatter.format(0, 'second'); // Fallback for edge cases
108127
}

ving/docs/change-log.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ outline: deep
88
### 2025-01-12
99
* Removed pulumi from the project.
1010
* Fixed: Documentation: useVingRecord findOne docs are wrong #201
11+
* refactored to use Intl instead of date-fns #204
12+
* NOTE: If you are using the date formating strings in dateTime.mjs you will need to update them to use Intl instead of date-fns.
1113

1214
## December 2024
1315

0 commit comments

Comments
 (0)