DurationFormat.js (16305B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * DurationFormat internal properties. 7 */ 8 function durationFormatLocaleData() { 9 return { 10 nu: getNumberingSystems, 11 default: { 12 nu: intl_numberingSystem, 13 }, 14 }; 15 } 16 var durationFormatInternalProperties = { 17 localeData: durationFormatLocaleData, 18 relevantExtensionKeys: ["nu"], 19 }; 20 21 /** 22 * Intl.DurationFormat ( [ locales [ , options ] ] ) 23 * 24 * Compute an internal properties object from |lazyDurationFormatData|. 25 */ 26 function resolveDurationFormatInternals(lazyDurationFormatData) { 27 assert(IsObject(lazyDurationFormatData), "lazy data not an object?"); 28 29 var internalProps = std_Object_create(null); 30 31 var DurationFormat = durationFormatInternalProperties; 32 33 // Compute effective locale. 34 35 // Step 9. 36 var r = ResolveLocale( 37 "DurationFormat", 38 lazyDurationFormatData.requestedLocales, 39 lazyDurationFormatData.opt, 40 DurationFormat.relevantExtensionKeys, 41 DurationFormat.localeData 42 ); 43 44 // Steps 10-11. 45 internalProps.locale = r.locale; 46 47 // Steps 12-21. (Not applicable in our implementation.) 48 49 // Step 22. 50 internalProps.numberingSystem = r.nu; 51 52 // Step 24. 53 internalProps.style = lazyDurationFormatData.style; 54 55 // Step 26. 56 internalProps.yearsStyle = lazyDurationFormatData.yearsStyle; 57 internalProps.yearsDisplay = lazyDurationFormatData.yearsDisplay; 58 59 internalProps.weeksStyle = lazyDurationFormatData.weeksStyle; 60 internalProps.weeksDisplay = lazyDurationFormatData.weeksDisplay; 61 62 internalProps.monthsStyle = lazyDurationFormatData.monthsStyle; 63 internalProps.monthsDisplay = lazyDurationFormatData.monthsDisplay; 64 65 internalProps.daysStyle = lazyDurationFormatData.daysStyle; 66 internalProps.daysDisplay = lazyDurationFormatData.daysDisplay; 67 68 internalProps.hoursStyle = lazyDurationFormatData.hoursStyle; 69 internalProps.hoursDisplay = lazyDurationFormatData.hoursDisplay; 70 71 internalProps.minutesStyle = lazyDurationFormatData.minutesStyle; 72 internalProps.minutesDisplay = lazyDurationFormatData.minutesDisplay; 73 74 internalProps.secondsStyle = lazyDurationFormatData.secondsStyle; 75 internalProps.secondsDisplay = lazyDurationFormatData.secondsDisplay; 76 77 internalProps.millisecondsStyle = lazyDurationFormatData.millisecondsStyle; 78 internalProps.millisecondsDisplay = 79 lazyDurationFormatData.millisecondsDisplay; 80 81 internalProps.microsecondsStyle = lazyDurationFormatData.microsecondsStyle; 82 internalProps.microsecondsDisplay = 83 lazyDurationFormatData.microsecondsDisplay; 84 85 internalProps.nanosecondsStyle = lazyDurationFormatData.nanosecondsStyle; 86 internalProps.nanosecondsDisplay = lazyDurationFormatData.nanosecondsDisplay; 87 88 // Step 27. 89 internalProps.fractionalDigits = lazyDurationFormatData.fractionalDigits; 90 91 // The caller is responsible for associating |internalProps| with the right 92 // object using |setInternalProperties|. 93 return internalProps; 94 } 95 96 /** 97 * Returns an object containing the DurationFormat internal properties of |obj|. 98 */ 99 function getDurationFormatInternals(obj) { 100 assert(IsObject(obj), "getDurationFormatInternals called with non-object"); 101 assert( 102 intl_GuardToDurationFormat(obj) !== null, 103 "getDurationFormatInternals called with non-DurationFormat" 104 ); 105 106 var internals = getIntlObjectInternals(obj); 107 assert( 108 internals.type === "DurationFormat", 109 "bad type escaped getIntlObjectInternals" 110 ); 111 112 // If internal properties have already been computed, use them. 113 var internalProps = maybeInternalProperties(internals); 114 if (internalProps) { 115 return internalProps; 116 } 117 118 // Otherwise it's time to fully create them. 119 internalProps = resolveDurationFormatInternals(internals.lazyData); 120 setInternalProperties(internals, internalProps); 121 return internalProps; 122 } 123 124 /** 125 * Intl.DurationFormat ( [ locales [ , options ] ] ) 126 * 127 * Initializes an object as a DurationFormat. 128 * 129 * This method is complicated a moderate bit by its implementing initialization 130 * as a *lazy* concept. Everything that must happen now, does -- but we defer 131 * all the work we can until the object is actually used as a DurationFormat. 132 * This later work occurs in |resolveDurationFormatInternals|; steps not noted 133 * here occur there. 134 */ 135 function InitializeDurationFormat(durationFormat, locales, options) { 136 assert( 137 IsObject(durationFormat), 138 "InitializeDurationFormat called with non-object" 139 ); 140 assert( 141 intl_GuardToDurationFormat(durationFormat) !== null, 142 "InitializeDurationFormat called with non-DurationFormat" 143 ); 144 145 // Lazy DurationFormat data has the following structure: 146 // 147 // { 148 // requestedLocales: List of locales, 149 // style: "long" / "short" / "narrow" / "digital", 150 // 151 // yearsStyle: "long" / "short" / "narrow", 152 // yearsDisplay: "auto" / "always", 153 // 154 // monthsStyle: "long" / "short" / "narrow", 155 // monthsDisplay: "auto" / "always", 156 // 157 // weeksStyle: "long" / "short" / "narrow", 158 // weeksDisplay: "auto" / "always", 159 // 160 // daysStyle: "long" / "short" / "narrow", 161 // daysDisplay: "auto" / "always", 162 // 163 // hoursStyle: "long" / "short" / "narrow" / "numeric" / "2-digit", 164 // hoursDisplay: "auto" / "always", 165 // 166 // minutesStyle: "long" / "short" / "narrow" / "numeric" / "2-digit", 167 // minutesDisplay: "auto" / "always", 168 // 169 // secondsStyle: "long" / "short" / "narrow" / "numeric" / "2-digit", 170 // secondsDisplay: "auto" / "always", 171 // 172 // millisecondsStyle: "long" / "short" / "narrow" / "numeric", 173 // millisecondsDisplay: "auto" / "always", 174 // 175 // microsecondsStyle: "long" / "short" / "narrow" / "numeric", 176 // microsecondsDisplay: "auto" / "always", 177 // 178 // nanosecondsStyle: "long" / "short" / "narrow" / "numeric", 179 // nanosecondsDisplay: "auto" / "always", 180 // 181 // fractionalDigits: integer ∈ [0, 9] / undefined, 182 // 183 // opt: // opt object computed in InitializeDurationFormat 184 // { 185 // localeMatcher: "lookup" / "best fit", 186 // 187 // nu: string matching a Unicode extension type, // optional 188 // } 189 // } 190 // 191 // Note that lazy data is only installed as a final step of initialization, 192 // so every DurationFormat lazy data object has *all* these properties, 193 // never a subset of them. 194 var lazyDurationFormatData = std_Object_create(null); 195 196 // Step 3. 197 var requestedLocales = CanonicalizeLocaleList(locales); 198 lazyDurationFormatData.requestedLocales = requestedLocales; 199 200 // Step 4. 201 if (options === undefined) { 202 options = std_Object_create(null); 203 } else if (!IsObject(options)) { 204 ThrowTypeError( 205 JSMSG_OBJECT_REQUIRED, 206 options === null ? "null" : typeof options 207 ); 208 } 209 210 // Step 5. 211 var matcher = GetOption( 212 options, 213 "localeMatcher", 214 "string", 215 ["lookup", "best fit"], 216 "best fit" 217 ); 218 219 // Step 6. 220 var numberingSystem = GetOption( 221 options, 222 "numberingSystem", 223 "string", 224 undefined, 225 undefined 226 ); 227 228 // Step 7. 229 if (numberingSystem !== undefined) { 230 numberingSystem = intl_ValidateAndCanonicalizeUnicodeExtensionType( 231 numberingSystem, 232 "numberingSystem", 233 "nu" 234 ); 235 } 236 237 // Step 8. 238 var opt = NEW_RECORD(); 239 opt.localeMatcher = matcher; 240 opt.nu = numberingSystem; 241 242 lazyDurationFormatData.opt = opt; 243 244 // Compute formatting options. 245 246 // Steps 23-24. 247 var style = GetOption( 248 options, 249 "style", 250 "string", 251 ["long", "short", "narrow", "digital"], 252 "short" 253 ); 254 lazyDurationFormatData.style = style; 255 256 // Step 25. (Not applicable in our implementation) 257 258 // Step 26, unit = "years". 259 var yearsOptions = GetDurationUnitOptions( 260 "years", 261 options, 262 style, 263 ["long", "short", "narrow"], 264 "short", 265 /* prevStyle= */ "" 266 ); 267 lazyDurationFormatData.yearsStyle = yearsOptions.style; 268 lazyDurationFormatData.yearsDisplay = yearsOptions.display; 269 270 // Step 26, unit = "months". 271 var monthsOptions = GetDurationUnitOptions( 272 "months", 273 options, 274 style, 275 ["long", "short", "narrow"], 276 "short", 277 /* prevStyle= */ "" 278 ); 279 lazyDurationFormatData.monthsStyle = monthsOptions.style; 280 lazyDurationFormatData.monthsDisplay = monthsOptions.display; 281 282 // Step 26, unit = "weeks". 283 var weeksOptions = GetDurationUnitOptions( 284 "weeks", 285 options, 286 style, 287 ["long", "short", "narrow"], 288 "short", 289 /* prevStyle= */ "" 290 ); 291 lazyDurationFormatData.weeksStyle = weeksOptions.style; 292 lazyDurationFormatData.weeksDisplay = weeksOptions.display; 293 294 // Step 26, unit = "days". 295 var daysOptions = GetDurationUnitOptions( 296 "days", 297 options, 298 style, 299 ["long", "short", "narrow"], 300 "short", 301 /* prevStyle= */ "" 302 ); 303 lazyDurationFormatData.daysStyle = daysOptions.style; 304 lazyDurationFormatData.daysDisplay = daysOptions.display; 305 306 // Step 26, unit = "hours". 307 var hoursOptions = GetDurationUnitOptions( 308 "hours", 309 options, 310 style, 311 ["long", "short", "narrow", "numeric", "2-digit"], 312 "numeric", 313 /* prevStyle= */ "" 314 ); 315 lazyDurationFormatData.hoursStyle = hoursOptions.style; 316 lazyDurationFormatData.hoursDisplay = hoursOptions.display; 317 318 // Step 26, unit = "minutes". 319 var minutesOptions = GetDurationUnitOptions( 320 "minutes", 321 options, 322 style, 323 ["long", "short", "narrow", "numeric", "2-digit"], 324 "numeric", 325 hoursOptions.style 326 ); 327 lazyDurationFormatData.minutesStyle = minutesOptions.style; 328 lazyDurationFormatData.minutesDisplay = minutesOptions.display; 329 330 // Step 26, unit = "seconds". 331 var secondsOptions = GetDurationUnitOptions( 332 "seconds", 333 options, 334 style, 335 ["long", "short", "narrow", "numeric", "2-digit"], 336 "numeric", 337 minutesOptions.style 338 ); 339 lazyDurationFormatData.secondsStyle = secondsOptions.style; 340 lazyDurationFormatData.secondsDisplay = secondsOptions.display; 341 342 // Step 26, unit = "milliseconds". 343 var millisecondsOptions = GetDurationUnitOptions( 344 "milliseconds", 345 options, 346 style, 347 ["long", "short", "narrow", "numeric"], 348 "numeric", 349 secondsOptions.style 350 ); 351 lazyDurationFormatData.millisecondsStyle = millisecondsOptions.style; 352 lazyDurationFormatData.millisecondsDisplay = millisecondsOptions.display; 353 354 // Step 26, unit = "microseconds". 355 var microsecondsOptions = GetDurationUnitOptions( 356 "microseconds", 357 options, 358 style, 359 ["long", "short", "narrow", "numeric"], 360 "numeric", 361 millisecondsOptions.style 362 ); 363 lazyDurationFormatData.microsecondsStyle = microsecondsOptions.style; 364 lazyDurationFormatData.microsecondsDisplay = microsecondsOptions.display; 365 366 // Step 26, unit = "milliseconds". 367 var nanosecondsOptions = GetDurationUnitOptions( 368 "nanoseconds", 369 options, 370 style, 371 ["long", "short", "narrow", "numeric"], 372 "numeric", 373 microsecondsOptions.style 374 ); 375 lazyDurationFormatData.nanosecondsStyle = nanosecondsOptions.style; 376 lazyDurationFormatData.nanosecondsDisplay = nanosecondsOptions.display; 377 378 // Step 27. 379 lazyDurationFormatData.fractionalDigits = GetNumberOption( 380 options, 381 "fractionalDigits", 382 0, 383 9, 384 undefined 385 ); 386 387 // We've done everything that must be done now: mark the lazy data as fully 388 // computed and install it. 389 initializeIntlObject( 390 durationFormat, 391 "DurationFormat", 392 lazyDurationFormatData 393 ); 394 } 395 396 /** 397 * GetDurationUnitOptions ( unit, options, baseStyle, stylesList, digitalBase, prevStyle, twoDigitHours ) 398 */ 399 function GetDurationUnitOptions( 400 unit, 401 options, 402 baseStyle, 403 stylesList, 404 digitalBase, 405 prevStyle 406 ) { 407 assert(typeof unit === "string", "unit is a string"); 408 assert(IsObject(options), "options is an object"); 409 assert(typeof baseStyle === "string", "baseStyle is a string"); 410 assert(IsArray(stylesList), "stylesList is an array"); 411 assert(typeof digitalBase === "string", "digitalBase is a string"); 412 assert(typeof prevStyle === "string", "prevStyle is a string"); 413 414 // Step 1. 415 var styleOption = GetOption(options, unit, "string", stylesList, undefined); 416 417 var style = styleOption; 418 419 // Step 2. 420 var displayDefault = "always"; 421 422 // Step 3. 423 if (style === undefined) { 424 // Steps 3.a-b. 425 if (baseStyle === "digital") { 426 // Step 3.a.i. 427 if (unit !== "hours" && unit !== "minutes" && unit !== "seconds") { 428 displayDefault = "auto"; 429 } 430 431 // Step 3.a.ii. 432 style = digitalBase; 433 } else { 434 // Steps 3.b.i-ii. ("fractional" handled implicitly) 435 if (prevStyle === "numeric" || prevStyle === "2-digit") { 436 // Step 3.b.i.1. 437 if (unit !== "minutes" && unit !== "seconds") { 438 // Step 3.b.i.1.a. 439 displayDefault = "auto"; 440 } 441 442 // Step 3.b.i.2. 443 style = "numeric"; 444 } else { 445 // Step 3.b.ii.1. 446 displayDefault = "auto"; 447 448 // Step 3.b.ii.2. 449 style = baseStyle; 450 } 451 } 452 } 453 454 // Step 4. 455 var isFractional = 456 style === "numeric" && 457 (unit === "milliseconds" || 458 unit === "microseconds" || 459 unit === "nanoseconds"); 460 if (isFractional) { 461 // Step 4.a.i. (Not applicable in our implementation) 462 463 // Step 4.a.ii. 464 displayDefault = "auto"; 465 } 466 467 // Step 5. 468 var displayField = unit + "Display"; 469 470 // Step 6. 471 var displayOption = GetOption( 472 options, 473 displayField, 474 "string", 475 ["auto", "always"], 476 undefined 477 ); 478 479 var display = displayOption ?? displayDefault; 480 481 // Step 7. 482 if (display === "always" && isFractional) { 483 assert( 484 styleOption !== undefined || displayOption !== undefined, 485 "no error is thrown when both 'style' and 'display' are absent" 486 ); 487 488 ThrowRangeError( 489 // eslint-disable-next-line no-nested-ternary 490 styleOption !== undefined && displayOption !== undefined 491 ? JSMSG_INTL_DURATION_INVALID_DISPLAY_OPTION 492 : displayOption !== undefined 493 ? JSMSG_INTL_DURATION_INVALID_DISPLAY_OPTION_DEFAULT_STYLE 494 : JSMSG_INTL_DURATION_INVALID_DISPLAY_OPTION_DEFAULT_DISPLAY, 495 unit 496 ); 497 } 498 499 // Steps 8-9. 500 if (prevStyle === "numeric" || prevStyle === "2-digit") { 501 // Step 8.a. and 9.a. 502 if (style !== "numeric" && style !== "2-digit") { 503 ThrowRangeError( 504 JSMSG_INTL_DURATION_INVALID_NON_NUMERIC_OPTION, 505 unit, 506 `"${style}"` 507 ); 508 } 509 510 // Step 9.b. 511 else if (unit === "minutes" || unit === "seconds") { 512 style = "2-digit"; 513 } 514 } 515 516 // Step 10. (Our implementation doesn't use |twoDigitHours|.) 517 518 // Step 11. 519 return { style, display }; 520 } 521 522 /** 523 * Returns the resolved options for a DurationFormat object. 524 */ 525 function Intl_DurationFormat_resolvedOptions() { 526 // Step 1. 527 var durationFormat = this; 528 529 // Step 2. 530 if ( 531 !IsObject(durationFormat) || 532 (durationFormat = intl_GuardToDurationFormat(durationFormat)) === null 533 ) { 534 return callFunction( 535 intl_CallDurationFormatMethodIfWrapped, 536 this, 537 "Intl_DurationFormat_resolvedOptions" 538 ); 539 } 540 541 var internals = getDurationFormatInternals(durationFormat); 542 543 // Steps 3-4. 544 var result = { 545 locale: internals.locale, 546 numberingSystem: internals.numberingSystem, 547 style: internals.style, 548 years: internals.yearsStyle, 549 yearsDisplay: internals.yearsDisplay, 550 months: internals.monthsStyle, 551 monthsDisplay: internals.monthsDisplay, 552 weeks: internals.weeksStyle, 553 weeksDisplay: internals.weeksDisplay, 554 days: internals.daysStyle, 555 daysDisplay: internals.daysDisplay, 556 hours: internals.hoursStyle, 557 hoursDisplay: internals.hoursDisplay, 558 minutes: internals.minutesStyle, 559 minutesDisplay: internals.minutesDisplay, 560 seconds: internals.secondsStyle, 561 secondsDisplay: internals.secondsDisplay, 562 milliseconds: internals.millisecondsStyle, 563 millisecondsDisplay: internals.millisecondsDisplay, 564 microseconds: internals.microsecondsStyle, 565 microsecondsDisplay: internals.microsecondsDisplay, 566 nanoseconds: internals.nanosecondsStyle, 567 nanosecondsDisplay: internals.nanosecondsDisplay, 568 }; 569 570 if (internals.fractionalDigits !== undefined) { 571 DefineDataProperty(result, "fractionalDigits", internals.fractionalDigits); 572 } 573 574 // Step 5. 575 return result; 576 }