1+ /**
2+ * @license
3+ * Copyright The Closure Library Authors.
4+ * SPDX-License-Identifier: Apache-2.0
5+ */
6+
7+ /**
8+ * @fileoverview DurationFormat provides methods to format duration time
9+ * into a human-readable, locale-sensitive string in a user friendly way and a
10+ * locale sensitive manner.
11+ */
12+ goog . module ( 'goog.i18n.DurationFormat' ) ;
13+
14+ const DurationSymbols = goog . require ( 'goog.i18n.DurationSymbols' ) ;
15+ const MessageFormat = goog . require ( 'goog.i18n.MessageFormat' ) ;
16+ const { DurationSymbols : DurationSymbolsTypes , DurationSymbolsFormatStyles} = goog . require ( 'goog.i18n.DurationSymbolTypes' ) ;
17+ const { ListFormat, ListFormatStyle, ListFormatType} = goog . require ( 'goog.i18n.listFormat' ) ;
18+ const { assert, assertNumber, assertObject} = goog . require ( 'goog.asserts' ) ;
19+
20+ /**
21+ * Choices for options bag 'type' in DurationFormat's constructor.
22+ * @enum {number} DurationFormatStyle
23+ */
24+ const DurationFormatStyle = {
25+ SHORT : 0 ,
26+ LONG : 1 ,
27+ NARROW : 2 ,
28+ } ;
29+ exports . DurationFormatStyle = DurationFormatStyle ;
30+
31+ /**
32+ * Available keys for the input object of public method format.
33+ * @enum {string} DurationFormatUnit
34+ */
35+ const DurationFormatUnit = {
36+ YEAR : 'years' ,
37+ MONTH : 'months' ,
38+ WEEK : 'weeks' ,
39+ DAY : 'days' ,
40+ HOUR : 'hours' ,
41+ MINUTE : 'minutes' ,
42+ SECOND : 'seconds'
43+ } ;
44+ exports . DurationFormatUnit = DurationFormatUnit ;
45+
46+ /**
47+ * Collection of duration unit and time for a locale.
48+ * @typedef {{
49+ * days: (number|undefined),
50+ * hours: (number|undefined),
51+ * minutes: (number|undefined),
52+ * months: (number|undefined),
53+ * seconds: (number|undefined),
54+ * weeks: (number|undefined),
55+ * years: (number|undefined)
56+ * }}
57+ */
58+ let DurationLike ; /* The data for the locale */
59+
60+ /** @typedef {!DurationLike } */
61+ exports . DurationLike ;
62+
63+ /**
64+ * Collection of duration display style.
65+ * @typedef {{
66+ * style: DurationFormatStyle!
67+ * }}
68+ */
69+ let DurationFormatOptions ; /* The data for the display style */
70+
71+ /** @typedef {!DurationFormatOptions } */
72+ exports . DurationFormatOptions ;
73+
74+ class DurationFormat {
75+ /**
76+ * Returns the durationformatter for the locale given by goog.LOCALE.
77+ * specified, a durationformatter for the user's locale will be returned.
78+ * @param {!DurationFormatOptions= } opt_options
79+ * This optional value determines the style of the duration time output.
80+ * A s part of the resulting formatted string, values include LONG, SHORT,
81+ * NARROW. Default is SHORT to keep consistency with ECMAScript.
82+ * @param {!DurationSymbolsTypes= } opt_durationSymbols
83+ * This optional value can be used to override the duration format symbols
84+ * selected using goog.LOCALE. This does not override the symbols used by
85+ * the underlying list formatter, so users overriding this should prefer
86+ * to use format each unit and do list formatting on the results.
87+ * @final
88+ */
89+ constructor ( opt_options , opt_durationSymbols ) {
90+ /** @private {!DurationFormatStyle} */
91+ const style = opt_options ?. style || DurationFormatStyle . SHORT ;
92+ assert (
93+ style >= DurationFormatStyle . SHORT &&
94+ style <= DurationFormatStyle . NARROW ,
95+ 'Style must be LONG, SHORT, NARROW' ) ;
96+ /** @private @const {!DurationFormatStyle} */
97+ this . style_ = style ;
98+
99+ /**
100+ * DurationSymbols object for locale data required by the formatter.
101+ * @private @const {!DurationSymbolsTypes}
102+ */
103+ const durationSymbols =
104+ opt_durationSymbols || DurationSymbols . getDurationSymbols ( ) ;
105+ assert ( durationSymbols !== null , 'Duration symbols cannot be null' ) ;
106+ this . durationSymbols_ = durationSymbols ;
107+ }
108+
109+ /**
110+ * From the data, return the information for the given unit and style.
111+ * @param {string } durationUnit
112+ * @return {string }
113+ * @private
114+ */
115+ getIcuFormattingPattern_ ( durationUnit ) {
116+ const unitInfo = this . getIcuFormattingPatternsForUnit_ ( durationUnit ) ;
117+ assertObject ( unitInfo ) ;
118+ return this . getIcuFormattingPatternForStyle_ ( unitInfo ) ;
119+ } ;
120+
121+ /**
122+ * From the data, check if the duration unit is legal.
123+ * @param {string } durationUnit
124+ * @return {boolean }
125+ * @private
126+ */
127+ checkIfLegalUnit_ ( durationUnit ) {
128+ return durationUnit === DurationFormatUnit . YEAR ||
129+ durationUnit === DurationFormatUnit . MONTH ||
130+ durationUnit === DurationFormatUnit . WEEK ||
131+ durationUnit === DurationFormatUnit . DAY ||
132+ durationUnit === DurationFormatUnit . HOUR ||
133+ durationUnit === DurationFormatUnit . MINUTE ||
134+ durationUnit === DurationFormatUnit . SECOND ;
135+ }
136+
137+ /**
138+ * Use public unit symbol to retrieve data for that unit.
139+ * @param {string } unit
140+ * @return {!DurationSymbolsFormatStyles }
141+ * @private
142+ */
143+ getIcuFormattingPatternsForUnit_ ( unit ) {
144+ assert (
145+ this . checkIfLegalUnit_ ( unit ) ,
146+ 'Unit must be years, months, weeks, days, hours, minutes or seconds' ) ;
147+ switch ( unit ) {
148+ default :
149+ case DurationFormatUnit . YEAR :
150+ return this . durationSymbols_ . YEAR ;
151+ case DurationFormatUnit . MONTH :
152+ return this . durationSymbols_ . MONTH ;
153+ case DurationFormatUnit . WEEK :
154+ return this . durationSymbols_ . WEEK ;
155+ case DurationFormatUnit . DAY :
156+ return this . durationSymbols_ . DAY ;
157+ case DurationFormatUnit . HOUR :
158+ return this . durationSymbols_ . HOUR ;
159+ case DurationFormatUnit . MINUTE :
160+ return this . durationSymbols_ . MINUTE ;
161+ case DurationFormatUnit . SECOND :
162+ return this . durationSymbols_ . SECOND ;
163+ }
164+ } ;
165+
166+ /**
167+ * Use unit symbol to retrieve data for that unit, given the style.
168+ * @param {!DurationSymbolsFormatStyles } unitInfo
169+ * @return {string }
170+ * @private
171+ */
172+ getIcuFormattingPatternForStyle_ ( unitInfo ) {
173+ // Fall back from LONG to NARROW to SHORT as needed.
174+ switch ( this . style_ ) {
175+ case DurationFormatStyle . LONG :
176+ if ( unitInfo . LONG != undefined ) {
177+ return unitInfo . LONG ;
178+ }
179+ case DurationFormatStyle . NARROW :
180+ if ( unitInfo . NARROW != undefined ) {
181+ return unitInfo . NARROW ;
182+ }
183+ case DurationFormatStyle . SHORT :
184+ default :
185+ return unitInfo . SHORT ;
186+ }
187+ } ;
188+
189+ /**
190+ * Format using pure JavaScript
191+ * @param {number } quantity Duration time value.
192+ * @param {string } durationUnit string such as hours, years,
193+ * months.
194+ * @return {string } The formatted result. May be empty string for an
195+ * unsupported locale.
196+ * @private
197+ */
198+ formatPolyfill_ ( quantity , durationUnit ) {
199+ /**
200+ * Find the right data based on unit, quantity, and plural.
201+ */
202+ const unitStyleString = this . getIcuFormattingPattern_ ( durationUnit ) ;
203+ if ( ! unitStyleString ) return '' ;
204+
205+ /**
206+ * Formatter for the messages requiring units. Plural formatting needed.
207+ * @type {?MessageFormat }
208+ */
209+ // Take basic message and wrap with plural message type.
210+ const msgFormatter =
211+ new MessageFormat ( '{DURATION_VALUE,plural,' + unitStyleString + '}' ) ;
212+ return msgFormatter . format ( { 'DURATION_VALUE' : quantity } ) ;
213+ } ;
214+
215+ /**
216+ * Formats a string with the amount and one unit.
217+ * @param {number } quantity A value for the duration time.
218+ * @param {string } durationUnit string such as hours, years,
219+ * months.
220+ * @return {string } The formatted result.
221+ * @private
222+ */
223+ formatWithUnit_ ( quantity , durationUnit ) {
224+ assertNumber ( quantity , 'Quantity must be a number' ) ;
225+ assert ( quantity >= 0 , 'Duration value should not be less than zero.' ) ;
226+
227+ // TODO(user): Add formatNative_ method when available.
228+ return this . formatPolyfill_ ( quantity , durationUnit ) ;
229+ } ;
230+
231+ /**
232+ * Formats a string with the amount and correspondent unit.
233+ * @param {!DurationLike } durationLike An object for the duration time whose
234+ * keys can be one of DurationFormatUnit value.
235+ * @return {string } The formatted result.
236+ */
237+ format ( durationLike ) {
238+ const formattedDurationList = [ ] ;
239+ const years = durationLike [ DurationFormatUnit . YEAR ] ;
240+ const months = durationLike [ DurationFormatUnit . MONTH ] ;
241+ const weeks = durationLike [ DurationFormatUnit . WEEK ] ;
242+ const days = durationLike [ DurationFormatUnit . DAY ] ;
243+ const hours = durationLike [ DurationFormatUnit . HOUR ] ;
244+ const minutes = durationLike [ DurationFormatUnit . MINUTE ] ;
245+ const seconds = durationLike [ DurationFormatUnit . SECOND ] ;
246+ if ( years != null ) {
247+ formattedDurationList . push (
248+ this . formatWithUnit_ ( years , DurationFormatUnit . YEAR ) ) ;
249+ }
250+
251+ if ( months != null ) {
252+ formattedDurationList . push (
253+ this . formatWithUnit_ ( months , DurationFormatUnit . MONTH ) ) ;
254+ }
255+
256+ if ( weeks != null ) {
257+ formattedDurationList . push (
258+ this . formatWithUnit_ ( weeks , DurationFormatUnit . WEEK ) ) ;
259+ }
260+
261+ if ( days != null ) {
262+ formattedDurationList . push (
263+ this . formatWithUnit_ ( days , DurationFormatUnit . DAY ) ) ;
264+ }
265+
266+ if ( hours != null ) {
267+ formattedDurationList . push (
268+ this . formatWithUnit_ ( hours , DurationFormatUnit . HOUR ) ) ;
269+ }
270+
271+ if ( minutes != null ) {
272+ formattedDurationList . push (
273+ this . formatWithUnit_ ( minutes , DurationFormatUnit . MINUTE ) ) ;
274+ }
275+
276+ if ( seconds != null ) {
277+ formattedDurationList . push (
278+ this . formatWithUnit_ ( seconds , DurationFormatUnit . SECOND ) ) ;
279+ }
280+
281+ // TODO(user): Add method for STYLE.DIGIT when available.
282+ const newListFormater = new ListFormat (
283+ { type : ListFormatType . UNIT , style : ListFormatStyle . NARROW } ) ;
284+ return newListFormater . format ( formattedDurationList ) ;
285+ } ;
286+ }
287+
288+ exports . DurationFormat = DurationFormat ;
0 commit comments