@@ -10,6 +10,118 @@ const msAmountIn: Record<string, number> = {
10
10
week : 1000 * 60 * 60 * 24 * 7 ,
11
11
} ;
12
12
13
+ const getRelativeTimeString = (
14
+ time : number ,
15
+ absTime : number ,
16
+ unit : string ,
17
+ isFuture : boolean
18
+ ) : string => {
19
+ const unitDecl =
20
+ absTime % 100 === 1 || absTime % 10 === 1 ? unit : `${ unit } s` ;
21
+
22
+ if ( unit === "second" && time === 0 ) return "just now" ;
23
+ if ( unit === "year" && time === 0 ) return "this year" ;
24
+ if ( unit === "year" && time === 1 ) return "last year" ;
25
+
26
+ return `${ isFuture ? "will come in" : "" } ${ absTime } ${ unitDecl } ${
27
+ isFuture ? "" : "ago"
28
+ } `;
29
+ } ;
30
+
31
+ const isDate = ( value : string | number | Date ) : boolean => {
32
+ const testDate = new Date ( value ) ;
33
+ if ( Object . prototype . toString . call ( testDate ) !== "[object Date]" )
34
+ return false ;
35
+ return ! isNaN ( testDate . getTime ( ) ) ;
36
+ } ;
37
+
38
+ const bestFit = (
39
+ diff : Diff
40
+ ) : "year" | "month" | "week" | "day" | "hour" | "minute" | "second" => {
41
+ const seconds = Math . abs ( diff . seconds ) ;
42
+ const minutes = Math . abs ( diff . minutes ) ;
43
+ const hours = Math . abs ( diff . hours ) ;
44
+ const days = Math . abs ( diff . days ) ;
45
+ const weeks = Math . abs ( diff . weeks ) ;
46
+ const months = Math . abs ( diff . months ) ;
47
+ const years = Math . abs ( diff . years ) ;
48
+
49
+ switch ( true ) {
50
+ case years > 0 && months > 11 :
51
+ return "year" ;
52
+ case months > 0 && days > 27 :
53
+ return "month" ;
54
+ case weeks > 0 && days > 6 :
55
+ return "week" ;
56
+ case days > 0 && hours > 23 :
57
+ return "day" ;
58
+ case hours > 0 && minutes > 59 :
59
+ return "hour" ;
60
+ case minutes > 0 && seconds > 59 :
61
+ return "minute" ;
62
+ default :
63
+ return "second" ;
64
+ }
65
+ } ;
66
+
67
+ const getInterval = ( currentUnit : string ) => {
68
+ if ( ! currentUnit . length ) return 10 ;
69
+ if ( ! msAmountIn [ currentUnit ] ) return msAmountIn . week ;
70
+ return msAmountIn [ currentUnit ] ;
71
+ } ;
72
+
73
+ const getRelativeTimeDiff = ( value : Date ) : Diff => {
74
+ const date = value ;
75
+ const now = new Date ( ) ;
76
+
77
+ const dateMs = date . getTime ( ) ;
78
+ const nowMs = now . getTime ( ) ;
79
+
80
+ const ms = nowMs - dateMs ;
81
+
82
+ const years = now . getFullYear ( ) - date . getFullYear ( ) ;
83
+ const round = Math [ ms > 0 ? "floor" : "ceil" ] ;
84
+
85
+ return {
86
+ ms,
87
+ seconds : round ( ms / msAmountIn . second ) ,
88
+ minutes : round ( ms / msAmountIn . minute ) ,
89
+ hours : round ( ms / msAmountIn . hour ) ,
90
+ days : round ( ms / msAmountIn . day ) ,
91
+ weeks : round ( ms / msAmountIn . week ) ,
92
+ months : years * 12 + now . getMonth ( ) - date . getMonth ( ) ,
93
+ years,
94
+ } ;
95
+ } ;
96
+
97
+ const updateRelativeTime = ( date : Date , currentUnit : string ) : string => {
98
+ const diff = getRelativeTimeDiff ( date ) ;
99
+ const diffkey = `${ currentUnit } s` as
100
+ | "ms"
101
+ | "seconds"
102
+ | "minutes"
103
+ | "hours"
104
+ | "days"
105
+ | "weeks"
106
+ | "months"
107
+ | "years" ;
108
+ let time = diff [ diffkey ] ;
109
+ let absTime = Math . abs ( time ) ;
110
+ const isFuture = time < 0 ;
111
+
112
+ if ( currentUnit === "second" ) {
113
+ let normTime = 45 ;
114
+ if ( absTime < 45 ) normTime = 20 ;
115
+ if ( absTime < 20 ) normTime = 5 ;
116
+ if ( absTime < 5 ) normTime = 0 ;
117
+ if ( absTime === 0 ) normTime = 0 ;
118
+ time = isFuture ? - normTime : normTime ;
119
+ absTime = Math . abs ( time ) ;
120
+ }
121
+
122
+ return getRelativeTimeString ( time , absTime , currentUnit , isFuture ) ;
123
+ } ;
124
+
13
125
const Time = ( props : Props ) => {
14
126
const {
15
127
value = "" ,
@@ -29,135 +141,23 @@ const Time = (props: Props) => {
29
141
let interval : null | number = null ;
30
142
if ( props . relativeTime && isDate ( props . value ) && ! interval ) {
31
143
const date = new Date ( props . value ) ;
32
- updateRelativeTime ( date ) ;
144
+ const diff = getRelativeTimeDiff ( date ) ;
145
+ state . currentUnit = props . unit || bestFit ( diff ) ;
146
+
147
+ setState ( {
148
+ ...state ,
149
+ relativeTime : updateRelativeTime ( date , state . currentUnit ) ,
150
+ } ) ;
33
151
interval = window . setInterval (
34
- ( ) => updateRelativeTime ( date ) ,
35
- getInterval ( )
152
+ ( ) => updateRelativeTime ( date , state . currentUnit ) ,
153
+ getInterval ( state . currentUnit )
36
154
) ;
37
155
}
38
156
39
157
return ( ) => {
40
158
if ( interval ) window . clearInterval ( interval ) ;
41
159
} ;
42
- } , [ ] ) ;
43
-
44
- const getRelativeTimeString = (
45
- time : number ,
46
- absTime : number ,
47
- unit : string ,
48
- isFuture : boolean
49
- ) : string => {
50
- const unitDecl =
51
- absTime % 100 === 1 || absTime % 10 === 1 ? unit : `${ unit } s` ;
52
-
53
- if ( unit === "second" && time === 0 ) return "just now" ;
54
- if ( unit === "year" && time === 0 ) return "this year" ;
55
- if ( unit === "year" && time === 1 ) return "last year" ;
56
-
57
- return `${ isFuture ? "will come in" : "" } ${ absTime } ${ unitDecl } ${
58
- isFuture ? "" : "ago"
59
- } `;
60
- } ;
61
-
62
- const getRelativeTimeDiff = ( value : Date ) : Diff => {
63
- const date = value ;
64
- const now = new Date ( ) ;
65
-
66
- const dateMs = date . getTime ( ) ;
67
- const nowMs = now . getTime ( ) ;
68
-
69
- const ms = nowMs - dateMs ;
70
-
71
- const years = now . getFullYear ( ) - date . getFullYear ( ) ;
72
- const round = Math [ ms > 0 ? "floor" : "ceil" ] ;
73
-
74
- return {
75
- ms,
76
- seconds : round ( ms / msAmountIn . second ) ,
77
- minutes : round ( ms / msAmountIn . minute ) ,
78
- hours : round ( ms / msAmountIn . hour ) ,
79
- days : round ( ms / msAmountIn . day ) ,
80
- weeks : round ( ms / msAmountIn . week ) ,
81
- months : years * 12 + now . getMonth ( ) - date . getMonth ( ) ,
82
- years,
83
- } ;
84
- } ;
85
-
86
- const getInterval = ( ) => {
87
- const { currentUnit } = state ;
88
- if ( ! currentUnit . length ) return 10 ;
89
- if ( ! msAmountIn [ currentUnit ] ) return msAmountIn . week ;
90
- return msAmountIn [ currentUnit ] ;
91
- } ;
92
-
93
- const updateRelativeTime = ( date : Date ) : boolean => {
94
- const diff = getRelativeTimeDiff ( date ) ;
95
- state . currentUnit = props . unit || bestFit ( diff ) ;
96
- const { currentUnit } = state ;
97
-
98
- const diffkey = `${ currentUnit } s` as
99
- | "ms"
100
- | "seconds"
101
- | "minutes"
102
- | "hours"
103
- | "days"
104
- | "weeks"
105
- | "months"
106
- | "years" ;
107
- let time = diff [ diffkey ] ;
108
- let absTime = Math . abs ( time ) ;
109
- const isFuture = time < 0 ;
110
-
111
- if ( currentUnit === "second" ) {
112
- let normTime = 45 ;
113
- if ( absTime < 45 ) normTime = 20 ;
114
- if ( absTime < 20 ) normTime = 5 ;
115
- if ( absTime < 5 ) normTime = 0 ;
116
- if ( absTime === 0 ) normTime = 0 ;
117
- time = isFuture ? - normTime : normTime ;
118
- absTime = Math . abs ( time ) ;
119
- }
120
-
121
- setState ( {
122
- ...state ,
123
- relativeTime : getRelativeTimeString ( time , absTime , currentUnit , isFuture ) ,
124
- } ) ;
125
- return true ;
126
- } ;
127
-
128
- const bestFit = ( diff : Diff ) : string => {
129
- const seconds = Math . abs ( diff . seconds ) ;
130
- const minutes = Math . abs ( diff . minutes ) ;
131
- const hours = Math . abs ( diff . hours ) ;
132
- const days = Math . abs ( diff . days ) ;
133
- const weeks = Math . abs ( diff . weeks ) ;
134
- const months = Math . abs ( diff . months ) ;
135
- const years = Math . abs ( diff . years ) ;
136
-
137
- switch ( true ) {
138
- case years > 0 && months > 11 :
139
- return "year" ;
140
- case months > 0 && days > 27 :
141
- return "month" ;
142
- case weeks > 0 && days > 6 :
143
- return "week" ;
144
- case days > 0 && hours > 23 :
145
- return "day" ;
146
- case hours > 0 && minutes > 59 :
147
- return "hour" ;
148
- case minutes > 0 && seconds > 59 :
149
- return "minute" ;
150
- default :
151
- return "second" ;
152
- }
153
- } ;
154
-
155
- const isDate = ( value : string | number | Date ) : boolean => {
156
- const testDate = new Date ( value ) ;
157
- if ( Object . prototype . toString . call ( testDate ) !== "[object Date]" )
158
- return false ;
159
- return ! isNaN ( testDate . getTime ( ) ) ;
160
- } ;
160
+ } , [ props . relativeTime , props . value , props . unit ] ) ;
161
161
162
162
return (
163
163
< span className = { className } >
0 commit comments