1
- import { format , parseISO , parseJSON , parse , getUnixTime } from 'date-fns' ;
2
1
import { isArray , isString , isUndefined } from '#ving/utils/identify.mjs' ;
3
-
2
+ import { createError } from 'h3' ;
4
3
5
4
/**
6
5
* Figures out what kind of date it was passed and returns a javascript date object
7
6
*
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).
9
8
* @returns A javascript Date object
10
9
* @example
11
10
* const date = determineDate("2012-04-23T18:25:43.511Z")
@@ -15,17 +14,17 @@ export const determineDate = (input) => {
15
14
return new Date ( ) ;
16
15
}
17
16
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 } ) ;
20
21
}
21
22
if ( input instanceof Date ) {
22
23
return input ;
23
24
}
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 ;
29
28
}
30
29
console . error ( 'Have no idea what type this date is: ' , input ) ;
31
30
throw createError ( { statusCode : 400 , message : 'Have no idea what type this date is: ' + input } ) ;
@@ -35,17 +34,32 @@ export const determineDate = (input) => {
35
34
* Formats a date to a human readable string with an American time format.
36
35
*
37
36
* @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 `
40
39
* @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]
43
50
*/
44
51
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
+ } ) => {
46
60
try {
47
61
const date = determineDate ( input ) ;
48
- return format ( date , pattern )
62
+ return new Intl . DateTimeFormat ( 'en-US' , options ) . format ( date ) ;
49
63
}
50
64
catch {
51
65
return 'bad date object' ;
@@ -56,13 +70,24 @@ export const formatDateTime = (input, pattern = "LLLL d, y h:mm a") => {
56
70
* Formats a date to a human readable string.
57
71
*
58
72
* @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
60
74
* @returns A formatted string. Example: `April 23, 2012`
61
75
* @example
62
76
* const formatted = formatDate("2012-04-23T18:25:43.511Z")
63
77
*/
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 ) ;
66
91
}
67
92
68
93
/**
@@ -74,35 +99,29 @@ export const formatDate = (input, pattern = "LLLL d, y") => {
74
99
* const formatted = formatTimeAgo("2012-04-23T18:25:43.511Z")
75
100
*/
76
101
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
+ }
106
125
}
107
- return message ;
126
+ return formatter . format ( 0 , 'second' ) ; // Fallback for edge cases
108
127
}
0 commit comments