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