@@ -39,6 +39,14 @@ function get_pretty_int_list(arr, ranges) {
3939 return arr . slice ( 0 , arr . length - 1 ) . join ( ', ' ) + ' and ' + arr [ arr . length - 1 ] ;
4040}
4141
42+ function get_pretty_str_list ( arr ) {
43+ // compose string array to string using commas + spaces, and
44+ // the english "and" to group the final two elements
45+ if ( ! arr || ! arr . length ) return '' ;
46+ if ( arr . length == 1 ) return arr [ 0 ] . toString ( ) ;
47+ return arr . slice ( 0 , arr . length - 1 ) . join ( ', ' ) + ' and ' + arr [ arr . length - 1 ] ;
48+ }
49+
4250function summarize_event_timings ( event ) {
4351 // summarize all event triggers from event into human-readable string
4452 // separate schedule items and options
@@ -87,63 +95,122 @@ function summarize_event_timing(trigger, idx) {
8795
8896 // years
8997 var year_str = '' ;
98+ var date_opts = app . getDateOptions ( { timeZone : 'UTC' } ) ;
99+ var locale = date_opts . locale ;
100+ var lang = ( locale || '' ) . split ( / \- / ) [ 0 ] ;
101+ var is_english = ( lang == 'en' ) ;
102+ var numbering = date_opts . numberingSystem ;
103+
104+ function build_formatter ( extra ) {
105+ var opts = Object . assign ( { } , date_opts , extra ) ;
106+ var loc = opts . locale ;
107+ delete opts . locale ;
108+ return new Intl . DateTimeFormat ( loc , opts ) ;
109+ }
110+ var month_fmt = build_formatter ( { month : 'long' } ) ;
111+ var wday_fmt = build_formatter ( { weekday : 'long' } ) ;
112+ var hour_fmt = build_formatter ( { hour : 'numeric' } ) ;
113+ var hm_fmt = build_formatter ( { hour : 'numeric' , minute : '2-digit' } ) ;
114+ var num_fmt = new Intl . NumberFormat ( locale , { useGrouping : false , numberingSystem : numbering } ) ;
115+ var num2_fmt = new Intl . NumberFormat ( locale , { useGrouping : false , numberingSystem : numbering , minimumIntegerDigits : 2 } ) ;
116+
117+ function format_number ( num , min_digits ) {
118+ return ( min_digits ? num2_fmt : num_fmt ) . format ( num ) ;
119+ }
120+ function format_ordinal ( num ) {
121+ // keep English ordinal suffixes, but localize digits
122+ var suffix = 'th' ;
123+ if ( ( num % 100 < 11 ) || ( num % 100 > 13 ) ) {
124+ switch ( num % 10 ) {
125+ case 1 : suffix = 'st' ; break ;
126+ case 2 : suffix = 'nd' ; break ;
127+ case 3 : suffix = 'rd' ; break ;
128+ }
129+ }
130+ return format_number ( num ) + suffix ;
131+ }
132+ function format_month_name ( num ) {
133+ return month_fmt . format ( new Date ( Date . UTC ( 2020 , num - 1 , 1 ) ) ) ;
134+ }
135+ function format_weekday_name ( num ) {
136+ return wday_fmt . format ( new Date ( Date . UTC ( 2020 , 7 , 2 + num ) ) ) ;
137+ }
138+ function normalize_time_label ( text ) {
139+ if ( ! is_english ) return text ;
140+ return text . replace ( / \s + / g, '' ) . replace ( / a m | p m / i, function ( m_all ) { return m_all . toLowerCase ( ) ; } ) ;
141+ }
142+ function format_hour_name ( num ) {
143+ return normalize_time_label ( hour_fmt . format ( new Date ( Date . UTC ( 2020 , 0 , 1 , num , 0 ) ) ) ) ;
144+ }
145+ function format_time_name ( hour , minute ) {
146+ return normalize_time_label ( hm_fmt . format ( new Date ( Date . UTC ( 2020 , 0 , 1 , hour , minute ) ) ) ) ;
147+ }
148+ function format_weekday_label ( num ) {
149+ var name = format_weekday_name ( num ) ;
150+ return is_english ? ( name + 's' ) : name ;
151+ }
152+ function format_month_day ( num ) {
153+ if ( num < 0 ) {
154+ if ( num == - 1 ) return 'last day' ;
155+ return format_ordinal ( Math . abs ( num ) ) + ' last day' ;
156+ }
157+ return format_ordinal ( num ) ;
158+ }
159+ function format_minute_label ( num ) {
160+ if ( num == 0 ) return 'hour' ;
161+ if ( num == 30 ) return 'half-hour' ;
162+ return ':' + format_number ( num , 2 ) ;
163+ }
164+
90165 if ( trigger . years && trigger . years . length ) {
91- year_str = get_pretty_int_list ( trigger . years , true ) ;
166+ year_str = get_pretty_int_list ( trigger . years , true ) . replace ( / ( \d + ) / g, function ( m_all , m_g1 ) {
167+ return format_number ( parseInt ( m_g1 ) ) ;
168+ } ) ;
92169 }
93170
94171 // months
95172 var mon_str = '' ;
96173 if ( trigger . months && trigger . months . length ) {
97174 mon_str = get_pretty_int_list ( trigger . months , true ) . replace ( / ( \d + ) / g, function ( m_all , m_g1 ) {
98- return _months [ parseInt ( m_g1 ) - 1 ] [ 1 ] ;
175+ return format_month_name ( parseInt ( m_g1 ) ) ;
99176 } ) ;
100177 }
101178
102179 // days
103180 var mday_str = '' ;
104181 if ( trigger . days && trigger . days . length ) {
105182 mday_str = get_pretty_int_list ( trigger . days , true ) . replace ( / ( \- ? \d + ) / g, function ( m_all , m_g1 ) {
106- var result = '' ;
107- switch ( m_g1 ) {
108- case '-1' : result = 'last day' ; break ;
109- case '-2' : result = '2nd last day' ; break ;
110- case '-3' : result = '3rd last day' ; break ;
111- case '-4' : result = '4th last day' ; break ;
112- case '-5' : result = '5th last day' ; break ;
113- case '-6' : result = '6th last day' ; break ;
114- case '-7' : result = '7th last day' ; break ;
115- default :
116- if ( m_g1 . match ( / ^ 1 [ 1 - 9 ] $ / ) ) result = m_g1 + 'th' ; // teens break the rule (11th, 12th, 13th, etc.)
117- else result = m_g1 + _number_suffixes [ parseInt ( m_g1 . substring ( m_g1 . length - 1 ) ) ] ;
118- break ;
119- }
120- return result ;
183+ return format_month_day ( parseInt ( m_g1 ) ) ;
121184 } ) ;
122185 }
123186
124187 // weekdays
125188 var wday_str = '' ;
126189 if ( trigger . weekdays && trigger . weekdays . length ) {
127- wday_str = get_pretty_int_list ( trigger . weekdays , true ) . replace ( / ( \d + ) / g, function ( m_all , m_g1 ) {
128- return _day_names [ parseInt ( m_g1 ) ] + 's' ;
129- } ) ;
130- wday_str = wday_str . replace ( / M o n d a y s \s + \- \s + F r i d a y s / , 'weekdays' ) ;
190+ var wdays = deep_copy_object ( trigger . weekdays ) . sort ( function ( a , b ) { return a - b ; } ) ;
191+ if ( ( wdays . length == 5 ) && ( wdays [ 0 ] == 1 ) && ( wdays [ 4 ] == 5 ) ) {
192+ wday_str = 'weekdays' ;
193+ }
194+ else {
195+ wday_str = get_pretty_int_list ( wdays , true ) . replace ( / ( \d + ) / g, function ( m_all , m_g1 ) {
196+ return format_weekday_label ( parseInt ( m_g1 ) ) ;
197+ } ) ;
198+ }
131199 }
132200
133201 // hours
134202 var hour_str = '' ;
135203 if ( trigger . hours && trigger . hours . length ) {
136204 hour_str = get_pretty_int_list ( trigger . hours , true ) . replace ( / ( \d + ) / g, function ( m_all , m_g1 ) {
137- return _hour_names [ parseInt ( m_g1 ) ] ;
205+ return format_hour_name ( parseInt ( m_g1 ) ) ;
138206 } ) ;
139207 }
140208
141209 // minutes
142210 var min_str = '' ;
143211 if ( trigger . minutes && trigger . minutes . length ) {
144- min_str = get_pretty_int_list ( trigger . minutes , false ) . replace ( / ( \d + ) / g, function ( m_all , m_g1 ) {
145- return ':' + ( ( m_g1 . length == 1 ) ? ( '0' + m_g1 ) : m_g1 ) ;
146- } ) ;
212+ var mins = deep_copy_object ( trigger . minutes ) . sort ( function ( a , b ) { return a - b ; } ) ;
213+ min_str = get_pretty_str_list ( mins . map ( function ( min ) { return format_minute_label ( min ) ; } ) ) ;
147214 }
148215
149216 // construct final string
@@ -173,10 +240,7 @@ function summarize_event_timing(trigger, idx) {
173240
174241 // compress single hour + single minute
175242 if ( trigger . hours && trigger . hours . length == 1 && trigger . minutes && trigger . minutes . length == 1 ) {
176- hour_str . match ( / ^ ( \d + ) ( \w + ) $ / ) ;
177- var hr = RegExp . $1 ;
178- var ampm = RegExp . $2 ;
179- var new_str = hr + min_str + ampm ;
243+ var new_str = format_time_name ( trigger . hours [ 0 ] , trigger . minutes [ 0 ] ) ;
180244
181245 if ( mday_str || wday_str ) groups . push ( 'at ' + new_str ) ;
182246 else groups . push ( 'daily at ' + new_str ) ;
@@ -194,8 +258,7 @@ function summarize_event_timing(trigger, idx) {
194258 if ( interval ) {
195259 var new_str = 'every ' + interval + ' minutes' ;
196260 if ( trigger . minutes [ 0 ] > 0 ) {
197- var m_g1 = trigger . minutes [ 0 ] . toString ( ) ;
198- new_str += ' starting on the :' + ( ( m_g1 . length == 1 ) ? ( '0' + m_g1 ) : m_g1 ) ;
261+ new_str += ' starting on the :' + format_number ( trigger . minutes [ 0 ] , 2 ) ;
199262 }
200263 groups . push ( new_str ) ;
201264 min_added = true ;
@@ -208,15 +271,17 @@ function summarize_event_timing(trigger, idx) {
208271 }
209272
210273 if ( ! min_added ) {
211- if ( min_str ) groups . push ( 'on the ' + min_str . replace ( / \: 0 0 / , 'hour' ) . replace ( / \: 3 0 / , 'half-hour' ) ) ;
274+ if ( min_str ) groups . push ( 'on the ' + min_str ) ;
212275 else groups . push ( 'every minute' ) ;
213276 }
214277 }
215278
216279 var text = ( typeof ( idx ) != 'undefined' ) ? groups . join ( ' ' ) : groups . join ( ', ' ) ;
217280 var output = text ;
218281 if ( ! idx ) output = text . substring ( 0 , 1 ) . toUpperCase ( ) + text . substring ( 1 , text . length ) ;
219- if ( trigger . timezone ) output += ' (' + trigger . timezone + ')' ;
282+ var timing_tz = trigger . timezone || app . config . tz ;
283+ var user_tz = ( app . user && app . user . timezone ) ? app . user . timezone : Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ;
284+ if ( timing_tz && user_tz && ( timing_tz != user_tz ) ) output += ' (' + timing_tz + ')' ;
220285
221286 return output ;
222287} ;
0 commit comments