feat(common): export functions to format numbers, percents, currencies & dates (#22423)
The utility functions `formatNumber`, `formatPercent`, `formatCurrency`, and `formatDate` used by the number, percent, currency and date pipes are now available for developers who want to use them outside of templates. Fixes #20536 PR Close #22423
This commit is contained in:
parent
094666da17
commit
4180912538
@ -12,9 +12,11 @@
|
|||||||
* Entry point for all public APIs of the common package.
|
* Entry point for all public APIs of the common package.
|
||||||
*/
|
*/
|
||||||
export * from './location/index';
|
export * from './location/index';
|
||||||
|
export {formatDate} from './i18n/format_date';
|
||||||
|
export {formatCurrency, formatNumber, formatPercent} from './i18n/format_number';
|
||||||
export {NgLocaleLocalization, NgLocalization} from './i18n/localization';
|
export {NgLocaleLocalization, NgLocalization} from './i18n/localization';
|
||||||
export {registerLocaleData} from './i18n/locale_data';
|
export {registerLocaleData} from './i18n/locale_data';
|
||||||
export {Plural, NumberFormatStyle, FormStyle, Time, TranslationWidth, FormatWidth, NumberSymbol, WeekDay, getNbOfCurrencyDigits, getCurrencySymbol, getLocaleDayPeriods, getLocaleDayNames, getLocaleMonthNames, getLocaleId, getLocaleEraNames, getLocaleWeekEndRange, getLocaleFirstDayOfWeek, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocalePluralCase, getLocaleTimeFormat, getLocaleNumberSymbol, getLocaleNumberFormat, getLocaleCurrencyName, getLocaleCurrencySymbol} from './i18n/locale_data_api';
|
export {Plural, NumberFormatStyle, FormStyle, Time, TranslationWidth, FormatWidth, NumberSymbol, WeekDay, getNumberOfCurrencyDigits, getCurrencySymbol, getLocaleDayPeriods, getLocaleDayNames, getLocaleMonthNames, getLocaleId, getLocaleEraNames, getLocaleWeekEndRange, getLocaleFirstDayOfWeek, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocalePluralCase, getLocaleTimeFormat, getLocaleNumberSymbol, getLocaleNumberFormat, getLocaleCurrencyName, getLocaleCurrencySymbol} from './i18n/locale_data_api';
|
||||||
export {parseCookieValue as ɵparseCookieValue} from './cookie';
|
export {parseCookieValue as ɵparseCookieValue} from './cookie';
|
||||||
export {CommonModule, DeprecatedI18NPipesModule} from './common_module';
|
export {CommonModule, DeprecatedI18NPipesModule} from './common_module';
|
||||||
export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
|
export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
import {FormStyle, FormatWidth, NumberSymbol, Time, TranslationWidth, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleDayNames, getLocaleDayPeriods, getLocaleEraNames, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocaleId, getLocaleMonthNames, getLocaleNumberSymbol, getLocaleTimeFormat} from './locale_data_api';
|
import {FormStyle, FormatWidth, NumberSymbol, Time, TranslationWidth, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleDayNames, getLocaleDayPeriods, getLocaleEraNames, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocaleId, getLocaleMonthNames, getLocaleNumberSymbol, getLocaleTimeFormat} from './locale_data_api';
|
||||||
|
|
||||||
|
export const ISO8601_DATE_REGEX =
|
||||||
|
/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
||||||
|
// 1 2 3 4 5 6 7 8 9 10 11
|
||||||
const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {};
|
const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {};
|
||||||
const DATE_FORMATS_SPLIT =
|
const DATE_FORMATS_SPLIT =
|
||||||
/((?:[^GyMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/;
|
/((?:[^GyMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/;
|
||||||
@ -38,11 +41,27 @@ enum TranslationType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a date to a locale string based on a pattern and a timezone
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Formats a date according to locale rules.
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* @internal
|
* Where:
|
||||||
|
* - `value` is a Date, a number (milliseconds since UTC epoch) or an ISO string
|
||||||
|
* (https://www.w3.org/TR/NOTE-datetime).
|
||||||
|
* - `format` indicates which date/time components to include. See {@link DatePipe} for more
|
||||||
|
* details.
|
||||||
|
* - `locale` is a `string` defining the locale to use.
|
||||||
|
* - `timezone` to be used for formatting. It understands UTC/GMT and the continental US time zone
|
||||||
|
* abbreviations, but for general use, use a time zone offset (e.g. `'+0430'`).
|
||||||
|
* If not specified, host system settings are used.
|
||||||
|
*
|
||||||
|
* See {@link DatePipe} for more details.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
*/
|
*/
|
||||||
export function formatDate(date: Date, format: string, locale: string, timezone?: string): string {
|
export function formatDate(
|
||||||
|
value: string | number | Date, format: string, locale: string, timezone?: string): string {
|
||||||
|
let date = toDate(value);
|
||||||
const namedFormat = getNamedFormat(locale, format);
|
const namedFormat = getNamedFormat(locale, format);
|
||||||
format = namedFormat || format;
|
format = namedFormat || format;
|
||||||
|
|
||||||
@ -165,8 +184,10 @@ function padNumber(
|
|||||||
neg = minusSign;
|
neg = minusSign;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let strNum = '' + num;
|
let strNum = String(num);
|
||||||
while (strNum.length < digits) strNum = '0' + strNum;
|
while (strNum.length < digits) {
|
||||||
|
strNum = '0' + strNum;
|
||||||
|
}
|
||||||
if (trim) {
|
if (trim) {
|
||||||
strNum = strNum.substr(strNum.length - digits);
|
strNum = strNum.substr(strNum.length - digits);
|
||||||
}
|
}
|
||||||
@ -607,3 +628,90 @@ function convertTimezoneToLocal(date: Date, timezone: string, reverse: boolean):
|
|||||||
const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
||||||
return addDateMinutes(date, reverseValue * (timezoneOffset - dateTimezoneOffset));
|
return addDateMinutes(date, reverseValue * (timezoneOffset - dateTimezoneOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a value to date.
|
||||||
|
*
|
||||||
|
* Supported input formats:
|
||||||
|
* - `Date`
|
||||||
|
* - number: timestamp
|
||||||
|
* - string: numeric (e.g. "1234"), ISO and date strings in a format supported by
|
||||||
|
* [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
|
||||||
|
* Note: ISO strings without time return a date without timeoffset.
|
||||||
|
*
|
||||||
|
* Throws if unable to convert to a date.
|
||||||
|
*/
|
||||||
|
export function toDate(value: string | number | Date): Date {
|
||||||
|
if (isDate(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number' && !isNaN(value)) {
|
||||||
|
return new Date(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
value = value.trim();
|
||||||
|
|
||||||
|
const parsedNb = parseFloat(value);
|
||||||
|
|
||||||
|
// any string that only contains numbers, like "1234" but not like "1234hello"
|
||||||
|
if (!isNaN(value as any - parsedNb)) {
|
||||||
|
return new Date(parsedNb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
||||||
|
/* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
||||||
|
before Date creation to avoid time offset and errors in the new Date.
|
||||||
|
If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
||||||
|
date, some browsers (e.g. IE 9) will throw an invalid Date error.
|
||||||
|
If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
|
||||||
|
is applied.
|
||||||
|
Note: ISO months are 0 for January, 1 for February, ... */
|
||||||
|
const [y, m, d] = value.split('-').map((val: string) => +val);
|
||||||
|
return new Date(y, m - 1, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
let match: RegExpMatchArray|null;
|
||||||
|
if (match = value.match(ISO8601_DATE_REGEX)) {
|
||||||
|
return isoStringToDate(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(value as any);
|
||||||
|
if (!isDate(date)) {
|
||||||
|
throw new Error(`Unable to convert "${value}" into a date`);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a date in ISO8601 to a Date.
|
||||||
|
* Used instead of `Date.parse` because of browser discrepancies.
|
||||||
|
*/
|
||||||
|
export function isoStringToDate(match: RegExpMatchArray): Date {
|
||||||
|
const date = new Date(0);
|
||||||
|
let tzHour = 0;
|
||||||
|
let tzMin = 0;
|
||||||
|
|
||||||
|
// match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100"
|
||||||
|
const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear;
|
||||||
|
const timeSetter = match[8] ? date.setUTCHours : date.setHours;
|
||||||
|
|
||||||
|
// if there is a timezone defined like "+01:00" or "+0100"
|
||||||
|
if (match[9]) {
|
||||||
|
tzHour = Number(match[9] + match[10]);
|
||||||
|
tzMin = Number(match[9] + match[11]);
|
||||||
|
}
|
||||||
|
dateSetter.call(date, Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
||||||
|
const h = Number(match[4] || 0) - tzHour;
|
||||||
|
const m = Number(match[5] || 0) - tzMin;
|
||||||
|
const s = Number(match[6] || 0);
|
||||||
|
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
|
||||||
|
timeSetter.call(date, h, m, s, ms);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDate(value: any): value is Date {
|
||||||
|
return value instanceof Date && !isNaN(value.valueOf());
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NumberFormatStyle, NumberSymbol, getLocaleNumberFormat, getLocaleNumberSymbol, getNbOfCurrencyDigits} from './locale_data_api';
|
import {NumberFormatStyle, NumberSymbol, getLocaleNumberFormat, getLocaleNumberSymbol, getNumberOfCurrencyDigits} from './locale_data_api';
|
||||||
|
|
||||||
export const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
export const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||||
const MAX_DIGITS = 22;
|
const MAX_DIGITS = 22;
|
||||||
@ -18,34 +18,19 @@ const DIGIT_CHAR = '#';
|
|||||||
const CURRENCY_CHAR = '¤';
|
const CURRENCY_CHAR = '¤';
|
||||||
const PERCENT_CHAR = '%';
|
const PERCENT_CHAR = '%';
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms a string into a number (if needed)
|
|
||||||
*/
|
|
||||||
function strToNumber(value: number | string): number {
|
|
||||||
// Convert strings to numbers
|
|
||||||
if (typeof value === 'string' && !isNaN(+value - parseFloat(value))) {
|
|
||||||
return +value;
|
|
||||||
}
|
|
||||||
if (typeof value !== 'number') {
|
|
||||||
throw new Error(`${value} is not a number`);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a number to a locale string based on a style and a format
|
* Transforms a number to a locale string based on a style and a format
|
||||||
*/
|
*/
|
||||||
function formatNumber(
|
function formatNumberToLocaleString(
|
||||||
value: number | string, pattern: ParsedNumberFormat, locale: string, groupSymbol: NumberSymbol,
|
value: number, pattern: ParsedNumberFormat, locale: string, groupSymbol: NumberSymbol,
|
||||||
decimalSymbol: NumberSymbol, digitsInfo?: string, isPercent = false): string {
|
decimalSymbol: NumberSymbol, digitsInfo?: string, isPercent = false): string {
|
||||||
let formattedText = '';
|
let formattedText = '';
|
||||||
let isZero = false;
|
let isZero = false;
|
||||||
const num = strToNumber(value);
|
|
||||||
|
|
||||||
if (!isFinite(num)) {
|
if (!isFinite(value)) {
|
||||||
formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity);
|
formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity);
|
||||||
} else {
|
} else {
|
||||||
let parsedNumber = parseNumber(num);
|
let parsedNumber = parseNumber(value);
|
||||||
|
|
||||||
if (isPercent) {
|
if (isPercent) {
|
||||||
parsedNumber = toPercent(parsedNumber);
|
parsedNumber = toPercent(parsedNumber);
|
||||||
@ -128,7 +113,7 @@ function formatNumber(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num < 0 && !isZero) {
|
if (value < 0 && !isZero) {
|
||||||
formattedText = pattern.negPre + formattedText + pattern.negSuf;
|
formattedText = pattern.negPre + formattedText + pattern.negSuf;
|
||||||
} else {
|
} else {
|
||||||
formattedText = pattern.posPre + formattedText + pattern.posSuf;
|
formattedText = pattern.posPre + formattedText + pattern.posSuf;
|
||||||
@ -138,20 +123,32 @@ function formatNumber(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a currency to a locale string
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Formats a number as currency using locale rules.
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* @internal
|
* Use `currency` to format a number as currency.
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* - `value` is a number.
|
||||||
|
* - `locale` is a `string` defining the locale to use.
|
||||||
|
* - `currency` is the string that represents the currency, it can be its symbol or its name.
|
||||||
|
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||||
|
* as `USD` for the US dollar and `EUR` for the euro.
|
||||||
|
* - `digitInfo` See {@link DecimalPipe} for more details.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
*/
|
*/
|
||||||
export function formatCurrency(
|
export function formatCurrency(
|
||||||
value: number | string, locale: string, currency: string, currencyCode?: string,
|
value: number, locale: string, currency: string, currencyCode?: string,
|
||||||
digitsInfo?: string): string {
|
digitsInfo?: string): string {
|
||||||
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency);
|
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency);
|
||||||
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
||||||
|
|
||||||
pattern.minFrac = getNbOfCurrencyDigits(currencyCode !);
|
pattern.minFrac = getNumberOfCurrencyDigits(currencyCode !);
|
||||||
pattern.maxFrac = pattern.minFrac;
|
pattern.maxFrac = pattern.minFrac;
|
||||||
|
|
||||||
const res = formatNumber(
|
const res = formatNumberToLocaleString(
|
||||||
value, pattern, locale, NumberSymbol.CurrencyGroup, NumberSymbol.CurrencyDecimal, digitsInfo);
|
value, pattern, locale, NumberSymbol.CurrencyGroup, NumberSymbol.CurrencyDecimal, digitsInfo);
|
||||||
return res
|
return res
|
||||||
.replace(CURRENCY_CHAR, currency)
|
.replace(CURRENCY_CHAR, currency)
|
||||||
@ -160,28 +157,48 @@ export function formatCurrency(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a percentage to a locale string
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Formats a number as a percentage according to locale rules.
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* @internal
|
* Formats a number as percentage.
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* - `value` is a number.
|
||||||
|
* - `locale` is a `string` defining the locale to use.
|
||||||
|
* - `digitInfo` See {@link DecimalPipe} for more details.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
*/
|
*/
|
||||||
export function formatPercent(value: number | string, locale: string, digitsInfo?: string): string {
|
export function formatPercent(value: number, locale: string, digitsInfo?: string): string {
|
||||||
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Percent);
|
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Percent);
|
||||||
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
||||||
const res = formatNumber(
|
const res = formatNumberToLocaleString(
|
||||||
value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo, true);
|
value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo, true);
|
||||||
return res.replace(
|
return res.replace(
|
||||||
new RegExp(PERCENT_CHAR, 'g'), getLocaleNumberSymbol(locale, NumberSymbol.PercentSign));
|
new RegExp(PERCENT_CHAR, 'g'), getLocaleNumberSymbol(locale, NumberSymbol.PercentSign));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a number to a locale string
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Formats a number according to locale rules.
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* @internal
|
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||||
|
* configurations are based on the locale.
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* - `value` is a number.
|
||||||
|
* - `locale` is a `string` defining the locale to use.
|
||||||
|
* - `digitInfo` See {@link DecimalPipe} for more details.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
*/
|
*/
|
||||||
export function formatDecimal(value: number | string, locale: string, digitsInfo?: string): string {
|
export function formatNumber(value: number, locale: string, digitsInfo?: string): string {
|
||||||
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Decimal);
|
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Decimal);
|
||||||
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
||||||
return formatNumber(value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo);
|
return formatNumberToLocaleString(
|
||||||
|
value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParsedNumberFormat {
|
interface ParsedNumberFormat {
|
||||||
@ -335,7 +352,7 @@ function parseNumber(num: number): ParsedNumber {
|
|||||||
digits = [];
|
digits = [];
|
||||||
// Convert string to array of digits without leading/trailing zeros.
|
// Convert string to array of digits without leading/trailing zeros.
|
||||||
for (j = 0; i <= zeros; i++, j++) {
|
for (j = 0; i <= zeros; i++, j++) {
|
||||||
digits[j] = +numStr.charAt(i);
|
digits[j] = Number(numStr.charAt(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +441,6 @@ function roundNumber(parsedNumber: ParsedNumber, minFrac: number, maxFrac: numbe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export function parseIntAutoRadix(text: string): number {
|
export function parseIntAutoRadix(text: string): number {
|
||||||
const result: number = parseInt(text);
|
const result: number = parseInt(text);
|
||||||
if (isNaN(result)) {
|
if (isNaN(result)) {
|
||||||
|
@ -560,7 +560,7 @@ const DEFAULT_NB_OF_CURRENCY_DIGITS = 2;
|
|||||||
*
|
*
|
||||||
* @experimental i18n support is experimental.
|
* @experimental i18n support is experimental.
|
||||||
*/
|
*/
|
||||||
export function getNbOfCurrencyDigits(code: string): number {
|
export function getNumberOfCurrencyDigits(code: string): number {
|
||||||
let digits;
|
let digits;
|
||||||
const currency = CURRENCIES_EN[code];
|
const currency = CURRENCIES_EN[code];
|
||||||
if (currency) {
|
if (currency) {
|
||||||
|
@ -10,41 +10,36 @@ import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
|||||||
import {formatDate} from '../i18n/format_date';
|
import {formatDate} from '../i18n/format_date';
|
||||||
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
export const ISO8601_DATE_REGEX =
|
|
||||||
/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
|
||||||
// 1 2 3 4 5 6 7 8 9 10 11
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
* @whatItDoes Formats a date according to locale rules.
|
* @whatItDoes Uses the function {@link formatDate} to format a date according to locale rules.
|
||||||
* @howToUse `date_expression | date[:format[:timezone[:locale]]]`
|
* @howToUse `date_expression | date[:format[:timezone[:locale]]]`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Where:
|
* Where:
|
||||||
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
* - `value` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||||
* (https://www.w3.org/TR/NOTE-datetime).
|
* (https://www.w3.org/TR/NOTE-datetime).
|
||||||
* - `format` indicates which date/time components to include. The format can be predefined as
|
* - `format` indicates which date/time components to include. The format can be predefined as
|
||||||
* shown below (all examples are given for `en-US`) or custom as shown in the table.
|
* shown below (all examples are given for `en-US`) or custom as shown in the table.
|
||||||
* - `'short'`: equivalent to `'M/d/yy, h:mm a'` (e.g. `6/15/15, 9:03 AM`)
|
* - `'short'`: equivalent to `'M/d/yy, h:mm a'` (e.g. `6/15/15, 9:03 AM`).
|
||||||
* - `'medium'`: equivalent to `'MMM d, y, h:mm:ss a'` (e.g. `Jun 15, 2015, 9:03:01 AM`)
|
* - `'medium'`: equivalent to `'MMM d, y, h:mm:ss a'` (e.g. `Jun 15, 2015, 9:03:01 AM`).
|
||||||
* - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (e.g. `June 15, 2015 at 9:03:01 AM GMT+1`)
|
* - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (e.g. `June 15, 2015 at 9:03:01 AM GMT+1`).
|
||||||
* - `'full'`: equivalent to `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (e.g. `Monday, June 15, 2015 at
|
* - `'full'`: equivalent to `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (e.g. `Monday, June 15, 2015 at
|
||||||
* 9:03:01 AM GMT+01:00`)
|
* 9:03:01 AM GMT+01:00`).
|
||||||
* - `'shortDate'`: equivalent to `'M/d/yy'` (e.g. `6/15/15`)
|
* - `'shortDate'`: equivalent to `'M/d/yy'` (e.g. `6/15/15`).
|
||||||
* - `'mediumDate'`: equivalent to `'MMM d, y'` (e.g. `Jun 15, 2015`)
|
* - `'mediumDate'`: equivalent to `'MMM d, y'` (e.g. `Jun 15, 2015`).
|
||||||
* - `'longDate'`: equivalent to `'MMMM d, y'` (e.g. `June 15, 2015`)
|
* - `'longDate'`: equivalent to `'MMMM d, y'` (e.g. `June 15, 2015`).
|
||||||
* - `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` (e.g. `Monday, June 15, 2015`)
|
* - `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` (e.g. `Monday, June 15, 2015`).
|
||||||
* - `'shortTime'`: equivalent to `'h:mm a'` (e.g. `9:03 AM`)
|
* - `'shortTime'`: equivalent to `'h:mm a'` (e.g. `9:03 AM`).
|
||||||
* - `'mediumTime'`: equivalent to `'h:mm:ss a'` (e.g. `9:03:01 AM`)
|
* - `'mediumTime'`: equivalent to `'h:mm:ss a'` (e.g. `9:03:01 AM`).
|
||||||
* - `'longTime'`: equivalent to `'h:mm:ss a z'` (e.g. `9:03:01 AM GMT+1`)
|
* - `'longTime'`: equivalent to `'h:mm:ss a z'` (e.g. `9:03:01 AM GMT+1`).
|
||||||
* - `'fullTime'`: equivalent to `'h:mm:ss a zzzz'` (e.g. `9:03:01 AM GMT+01:00`)
|
* - `'fullTime'`: equivalent to `'h:mm:ss a zzzz'` (e.g. `9:03:01 AM GMT+01:00`).
|
||||||
* - `timezone` to be used for formatting. It understands UTC/GMT and the continental US time zone
|
* - `timezone` to be used for formatting. It understands UTC/GMT and the continental US time zone
|
||||||
* abbreviations, but for general use, use a time zone offset, for example,
|
* abbreviations, but for general use, use a time zone offset (e.g. `'+0430'`).
|
||||||
* `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
|
|
||||||
* If not specified, the local system timezone of the end-user's browser will be used.
|
* If not specified, the local system timezone of the end-user's browser will be used.
|
||||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||||
* default)
|
* default).
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* | Field Type | Format | Description | Example Value |
|
* | Field Type | Format | Description | Example Value |
|
||||||
@ -137,66 +132,10 @@ export class DatePipe implements PipeTransform {
|
|||||||
transform(value: any, format = 'mediumDate', timezone?: string, locale?: string): string|null {
|
transform(value: any, format = 'mediumDate', timezone?: string, locale?: string): string|null {
|
||||||
if (value == null || value === '' || value !== value) return null;
|
if (value == null || value === '' || value !== value) return null;
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
try {
|
||||||
value = value.trim();
|
return formatDate(value, format, locale || this.locale, timezone);
|
||||||
|
} catch (error) {
|
||||||
|
throw invalidPipeArgumentError(DatePipe, error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
let date: Date;
|
|
||||||
let match: RegExpMatchArray|null;
|
|
||||||
if (isDate(value)) {
|
|
||||||
date = value;
|
|
||||||
} else if (!isNaN(value - parseFloat(value))) {
|
|
||||||
date = new Date(parseFloat(value));
|
|
||||||
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
|
||||||
/**
|
|
||||||
* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
|
||||||
* before Date creation to avoid time offset and errors in the new Date.
|
|
||||||
* If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
|
||||||
* date, some browsers (e.g. IE 9) will throw an invalid Date error
|
|
||||||
* If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
|
|
||||||
* is applied
|
|
||||||
* Note: ISO months are 0 for January, 1 for February, ...
|
|
||||||
*/
|
|
||||||
const [y, m, d] = value.split('-').map((val: string) => +val);
|
|
||||||
date = new Date(y, m - 1, d);
|
|
||||||
} else if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) {
|
|
||||||
date = isoStringToDate(match);
|
|
||||||
} else {
|
|
||||||
date = new Date(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDate(date)) {
|
|
||||||
throw invalidPipeArgumentError(DatePipe, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatDate(date, format, locale || this.locale, timezone);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export function isoStringToDate(match: RegExpMatchArray): Date {
|
|
||||||
const date = new Date(0);
|
|
||||||
let tzHour = 0;
|
|
||||||
let tzMin = 0;
|
|
||||||
|
|
||||||
// match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100"
|
|
||||||
const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear;
|
|
||||||
const timeSetter = match[8] ? date.setUTCHours : date.setHours;
|
|
||||||
|
|
||||||
// if there is a timezone defined like "+01:00" or "+0100"
|
|
||||||
if (match[9]) {
|
|
||||||
tzHour = +(match[9] + match[10]);
|
|
||||||
tzMin = +(match[9] + match[11]);
|
|
||||||
}
|
|
||||||
dateSetter.call(date, +(match[1]), +(match[2]) - 1, +(match[3]));
|
|
||||||
const h = +(match[4] || '0') - tzHour;
|
|
||||||
const m = +(match[5] || '0') - tzMin;
|
|
||||||
const s = +(match[6] || '0');
|
|
||||||
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
|
|
||||||
timeSetter.call(date, h, m, s, ms);
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDate(value: any): value is Date {
|
|
||||||
return value instanceof Date && !isNaN(value.valueOf());
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
import {ISO8601_DATE_REGEX, isoStringToDate} from '../date_pipe';
|
import {ISO8601_DATE_REGEX, isoStringToDate} from '../../i18n/format_date';
|
||||||
import {invalidPipeArgumentError} from '../invalid_pipe_argument_error';
|
import {invalidPipeArgumentError} from '../invalid_pipe_argument_error';
|
||||||
import {DateFormatter} from './intl';
|
import {DateFormatter} from './intl';
|
||||||
|
|
||||||
|
@ -7,29 +7,28 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
import {formatCurrency, formatDecimal, formatPercent} from '../i18n/format_number';
|
import {formatCurrency, formatNumber, formatPercent} from '../i18n/format_number';
|
||||||
import {getCurrencySymbol} from '../i18n/locale_data_api';
|
import {getCurrencySymbol} from '../i18n/locale_data_api';
|
||||||
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
* @whatItDoes Formats a number according to locale rules.
|
* @whatItDoes Uses the function {@link formatNumber} to format a number according to locale rules.
|
||||||
* @howToUse `number_expression | number[:digitInfo[:locale]]`
|
* @howToUse `number_expression | number[:digitInfo[:locale]]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* Formats a number as text. Group sizing and separator and other locale-specific
|
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||||
* configurations are based on the active locale.
|
* configurations are based on the locale.
|
||||||
*
|
*
|
||||||
* where `expression` is a number:
|
* Where:
|
||||||
|
* - `value` is a number
|
||||||
* - `digitInfo` is a `string` which has a following format: <br>
|
* - `digitInfo` is a `string` which has a following format: <br>
|
||||||
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>
|
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
|
||||||
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
||||||
* - `minFractionDigits` is the minimum number of digits after fraction. Defaults to `0`.
|
* - `minFractionDigits` is the minimum number of digits after the decimal point. Defaults to `0`.
|
||||||
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
* - `maxFractionDigits` is the maximum number of digits after the decimal point. Defaults to `3`.
|
||||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||||
* default)
|
* default).
|
||||||
*
|
|
||||||
* For more information on the acceptable range for each of these numbers and other
|
|
||||||
* details see your native internationalization library.
|
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
@ -47,7 +46,8 @@ export class DecimalPipe implements PipeTransform {
|
|||||||
locale = locale || this._locale;
|
locale = locale || this._locale;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return formatDecimal(value, locale, digitsInfo);
|
const num = strToNumber(value);
|
||||||
|
return formatNumber(num, locale, digitsInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw invalidPipeArgumentError(DecimalPipe, error.message);
|
throw invalidPipeArgumentError(DecimalPipe, error.message);
|
||||||
}
|
}
|
||||||
@ -56,16 +56,18 @@ export class DecimalPipe implements PipeTransform {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
* @whatItDoes Formats a number as a percentage according to locale rules.
|
* @whatItDoes Uses the function {@link formatPercent} to format a number as a percentage according
|
||||||
|
* to locale rules.
|
||||||
* @howToUse `number_expression | percent[:digitInfo[:locale]]`
|
* @howToUse `number_expression | percent[:digitInfo[:locale]]`
|
||||||
*
|
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Formats a number as percentage.
|
* Formats a number as percentage.
|
||||||
*
|
*
|
||||||
* - `digitInfo` See {@link DecimalPipe} for a detailed description.
|
* Where:
|
||||||
|
* - `value` is a number.
|
||||||
|
* - `digitInfo` See {@link DecimalPipe} for more details.
|
||||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||||
* default)
|
* default).
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
@ -83,7 +85,8 @@ export class PercentPipe implements PipeTransform {
|
|||||||
locale = locale || this._locale;
|
locale = locale || this._locale;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return formatPercent(value, locale, digitsInfo);
|
const num = strToNumber(value);
|
||||||
|
return formatPercent(num, locale, digitsInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw invalidPipeArgumentError(PercentPipe, error.message);
|
throw invalidPipeArgumentError(PercentPipe, error.message);
|
||||||
}
|
}
|
||||||
@ -92,25 +95,28 @@ export class PercentPipe implements PipeTransform {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngModule CommonModule
|
* @ngModule CommonModule
|
||||||
* @whatItDoes Formats a number as currency using locale rules.
|
* @whatItDoes Uses the functions {@link getCurrencySymbol} and {@link formatCurrency} to format a
|
||||||
|
* number as currency using locale rules.
|
||||||
* @howToUse `number_expression | currency[:currencyCode[:display[:digitInfo[:locale]]]]`
|
* @howToUse `number_expression | currency[:currencyCode[:display[:digitInfo[:locale]]]]`
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Use `currency` to format a number as currency.
|
* Use `currency` to format a number as currency.
|
||||||
*
|
*
|
||||||
|
* Where:
|
||||||
|
* - `value` is a number.
|
||||||
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||||
* as `USD` for the US dollar and `EUR` for the euro.
|
* as `USD` for the US dollar and `EUR` for the euro.
|
||||||
* - `display` indicates whether to show the currency symbol, the code or a custom value
|
* - `display` indicates whether to show the currency symbol, the code or a custom value:
|
||||||
* - `code`: use code (e.g. `USD`).
|
* - `code`: use code (e.g. `USD`).
|
||||||
* - `symbol`(default): use symbol (e.g. `$`).
|
* - `symbol`(default): use symbol (e.g. `$`).
|
||||||
* - `symbol-narrow`: some countries have two symbols for their currency, one regular and one
|
* - `symbol-narrow`: some countries have two symbols for their currency, one regular and one
|
||||||
* narrow (e.g. the canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`).
|
* narrow (e.g. the canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`).
|
||||||
* - `string`: use this value instead of a code or a symbol
|
* - `string`: use this value instead of a code or a symbol.
|
||||||
* - boolean (deprecated from v5): `true` for symbol and false for `code`
|
* - boolean (deprecated from v5): `true` for symbol and false for `code`.
|
||||||
* If there is no narrow symbol for the chosen currency, the regular symbol will be used.
|
* If there is no narrow symbol for the chosen currency, the regular symbol will be used.
|
||||||
* - `digitInfo` See {@link DecimalPipe} for a detailed description.
|
* - `digitInfo` See {@link DecimalPipe} for more details.
|
||||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||||
* default)
|
* default).
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
@ -148,7 +154,8 @@ export class CurrencyPipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return formatCurrency(value, locale, currency, currencyCode, digitsInfo);
|
const num = strToNumber(value);
|
||||||
|
return formatCurrency(num, locale, currency, currencyCode, digitsInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw invalidPipeArgumentError(CurrencyPipe, error.message);
|
throw invalidPipeArgumentError(CurrencyPipe, error.message);
|
||||||
}
|
}
|
||||||
@ -158,3 +165,17 @@ export class CurrencyPipe implements PipeTransform {
|
|||||||
function isEmpty(value: any): boolean {
|
function isEmpty(value: any): boolean {
|
||||||
return value == null || value === '' || value !== value;
|
return value == null || value === '' || value !== value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a string into a number (if needed)
|
||||||
|
*/
|
||||||
|
function strToNumber(value: number | string): number {
|
||||||
|
// Convert strings to numbers
|
||||||
|
if (typeof value === 'string' && !isNaN(Number(value) - parseFloat(value))) {
|
||||||
|
return Number(value);
|
||||||
|
}
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new Error(`${value} is not a number`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
315
packages/common/test/i18n/format_date_spec.ts
Normal file
315
packages/common/test/i18n/format_date_spec.ts
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {registerLocaleData} from '@angular/common';
|
||||||
|
import localeAr from '@angular/common/locales/ar';
|
||||||
|
import localeDe from '@angular/common/locales/de';
|
||||||
|
import localeEn from '@angular/common/locales/en';
|
||||||
|
import localeEnExtra from '@angular/common/locales/extra/en';
|
||||||
|
import localeHu from '@angular/common/locales/hu';
|
||||||
|
import localeSr from '@angular/common/locales/sr';
|
||||||
|
import localeTh from '@angular/common/locales/th';
|
||||||
|
import {isDate, toDate, formatDate} from '@angular/common/src/i18n/format_date';
|
||||||
|
|
||||||
|
describe('Format date', () => {
|
||||||
|
describe('toDate', () => {
|
||||||
|
it('should support date', () => { expect(isDate(toDate(new Date()))).toBeTruthy(); });
|
||||||
|
|
||||||
|
it('should support int', () => { expect(isDate(toDate(123456789))).toBeTruthy(); });
|
||||||
|
|
||||||
|
it('should support numeric strings',
|
||||||
|
() => { expect(isDate(toDate('123456789'))).toBeTruthy(); });
|
||||||
|
|
||||||
|
it('should support decimal strings',
|
||||||
|
() => { expect(isDate(toDate('123456789.11'))).toBeTruthy(); });
|
||||||
|
|
||||||
|
it('should support ISO string',
|
||||||
|
() => { expect(isDate(toDate('2015-06-15T21:43:11Z'))).toBeTruthy(); });
|
||||||
|
|
||||||
|
it('should throw for empty string', () => { expect(() => toDate('')).toThrow(); });
|
||||||
|
|
||||||
|
it('should throw for alpha numeric strings',
|
||||||
|
() => { expect(() => toDate('123456789 hello')).toThrow(); });
|
||||||
|
|
||||||
|
it('should throw for NaN', () => { expect(() => toDate(Number.NaN)).toThrow(); });
|
||||||
|
|
||||||
|
it('should support ISO string without time',
|
||||||
|
() => { expect(isDate(toDate('2015-01-01'))).toBeTruthy(); });
|
||||||
|
|
||||||
|
it('should throw for objects', () => { expect(() => toDate({} as any)).toThrow(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatDate', () => {
|
||||||
|
const isoStringWithoutTime = '2015-01-01';
|
||||||
|
const defaultLocale = 'en-US';
|
||||||
|
const defaultFormat = 'mediumDate';
|
||||||
|
let date: Date;
|
||||||
|
|
||||||
|
// Check the transformation of a date into a pattern
|
||||||
|
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
|
||||||
|
expect(formatDate(date, pattern, defaultLocale)).toEqual(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
registerLocaleData(localeEn, localeEnExtra);
|
||||||
|
registerLocaleData(localeDe);
|
||||||
|
registerLocaleData(localeHu);
|
||||||
|
registerLocaleData(localeSr);
|
||||||
|
registerLocaleData(localeTh);
|
||||||
|
registerLocaleData(localeAr);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => { date = new Date(2015, 5, 15, 9, 3, 1, 550); });
|
||||||
|
|
||||||
|
it('should format each component correctly', () => {
|
||||||
|
const dateFixtures: any = {
|
||||||
|
G: 'AD',
|
||||||
|
GG: 'AD',
|
||||||
|
GGG: 'AD',
|
||||||
|
GGGG: 'Anno Domini',
|
||||||
|
GGGGG: 'A',
|
||||||
|
y: '2015',
|
||||||
|
yy: '15',
|
||||||
|
yyy: '2015',
|
||||||
|
yyyy: '2015',
|
||||||
|
M: '6',
|
||||||
|
MM: '06',
|
||||||
|
MMM: 'Jun',
|
||||||
|
MMMM: 'June',
|
||||||
|
MMMMM: 'J',
|
||||||
|
L: '6',
|
||||||
|
LL: '06',
|
||||||
|
LLL: 'Jun',
|
||||||
|
LLLL: 'June',
|
||||||
|
LLLLL: 'J',
|
||||||
|
w: '25',
|
||||||
|
ww: '25',
|
||||||
|
W: '3',
|
||||||
|
d: '15',
|
||||||
|
dd: '15',
|
||||||
|
E: 'Mon',
|
||||||
|
EE: 'Mon',
|
||||||
|
EEE: 'Mon',
|
||||||
|
EEEE: 'Monday',
|
||||||
|
EEEEEE: 'Mo',
|
||||||
|
h: '9',
|
||||||
|
hh: '09',
|
||||||
|
H: '9',
|
||||||
|
HH: '09',
|
||||||
|
m: '3',
|
||||||
|
mm: '03',
|
||||||
|
s: '1',
|
||||||
|
ss: '01',
|
||||||
|
S: '6',
|
||||||
|
SS: '55',
|
||||||
|
SSS: '550',
|
||||||
|
a: 'AM',
|
||||||
|
aa: 'AM',
|
||||||
|
aaa: 'AM',
|
||||||
|
aaaa: 'AM',
|
||||||
|
aaaaa: 'a',
|
||||||
|
b: 'morning',
|
||||||
|
bb: 'morning',
|
||||||
|
bbb: 'morning',
|
||||||
|
bbbb: 'morning',
|
||||||
|
bbbbb: 'morning',
|
||||||
|
B: 'in the morning',
|
||||||
|
BB: 'in the morning',
|
||||||
|
BBB: 'in the morning',
|
||||||
|
BBBB: 'in the morning',
|
||||||
|
BBBBB: 'in the morning',
|
||||||
|
};
|
||||||
|
|
||||||
|
const isoStringWithoutTimeFixtures: any = {
|
||||||
|
G: 'AD',
|
||||||
|
GG: 'AD',
|
||||||
|
GGG: 'AD',
|
||||||
|
GGGG: 'Anno Domini',
|
||||||
|
GGGGG: 'A',
|
||||||
|
y: '2015',
|
||||||
|
yy: '15',
|
||||||
|
yyy: '2015',
|
||||||
|
yyyy: '2015',
|
||||||
|
M: '1',
|
||||||
|
MM: '01',
|
||||||
|
MMM: 'Jan',
|
||||||
|
MMMM: 'January',
|
||||||
|
MMMMM: 'J',
|
||||||
|
L: '1',
|
||||||
|
LL: '01',
|
||||||
|
LLL: 'Jan',
|
||||||
|
LLLL: 'January',
|
||||||
|
LLLLL: 'J',
|
||||||
|
w: '1',
|
||||||
|
ww: '01',
|
||||||
|
W: '1',
|
||||||
|
d: '1',
|
||||||
|
dd: '01',
|
||||||
|
E: 'Thu',
|
||||||
|
EE: 'Thu',
|
||||||
|
EEE: 'Thu',
|
||||||
|
EEEE: 'Thursday',
|
||||||
|
EEEEE: 'T',
|
||||||
|
EEEEEE: 'Th',
|
||||||
|
h: '12',
|
||||||
|
hh: '12',
|
||||||
|
H: '0',
|
||||||
|
HH: '00',
|
||||||
|
m: '0',
|
||||||
|
mm: '00',
|
||||||
|
s: '0',
|
||||||
|
ss: '00',
|
||||||
|
S: '0',
|
||||||
|
SS: '00',
|
||||||
|
SSS: '000',
|
||||||
|
a: 'AM',
|
||||||
|
aa: 'AM',
|
||||||
|
aaa: 'AM',
|
||||||
|
aaaa: 'AM',
|
||||||
|
aaaaa: 'a',
|
||||||
|
b: 'midnight',
|
||||||
|
bb: 'midnight',
|
||||||
|
bbb: 'midnight',
|
||||||
|
bbbb: 'midnight',
|
||||||
|
bbbbb: 'midnight',
|
||||||
|
B: 'midnight',
|
||||||
|
BB: 'midnight',
|
||||||
|
BBB: 'midnight',
|
||||||
|
BBBB: 'midnight',
|
||||||
|
BBBBB: 'mi',
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
|
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
||||||
|
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format with timezones', () => {
|
||||||
|
const dateFixtures: any = {
|
||||||
|
z: /GMT(\+|-)\d/,
|
||||||
|
zz: /GMT(\+|-)\d/,
|
||||||
|
zzz: /GMT(\+|-)\d/,
|
||||||
|
zzzz: /GMT(\+|-)\d{2}\:30/,
|
||||||
|
Z: /(\+|-)\d{2}30/,
|
||||||
|
ZZ: /(\+|-)\d{2}30/,
|
||||||
|
ZZZ: /(\+|-)\d{2}30/,
|
||||||
|
ZZZZ: /GMT(\+|-)\d{2}\:30/,
|
||||||
|
ZZZZZ: /(\+|-)\d{2}\:30/,
|
||||||
|
O: /GMT(\+|-)\d/,
|
||||||
|
OOOO: /GMT(\+|-)\d{2}\:30/,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
|
expect(formatDate(date, pattern, defaultLocale, '+0430')).toMatch(dateFixtures[pattern]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format common multi component patterns', () => {
|
||||||
|
const dateFixtures: any = {
|
||||||
|
'EEE, M/d/y': 'Mon, 6/15/2015',
|
||||||
|
'EEE, M/d': 'Mon, 6/15',
|
||||||
|
'MMM d': 'Jun 15',
|
||||||
|
'dd/MM/yyyy': '15/06/2015',
|
||||||
|
'MM/dd/yyyy': '06/15/2015',
|
||||||
|
'yMEEEd': '20156Mon15',
|
||||||
|
'MEEEd': '6Mon15',
|
||||||
|
'MMMd': 'Jun15',
|
||||||
|
'EEEE, MMMM d, y': 'Monday, June 15, 2015',
|
||||||
|
'H:mm a': '9:03 AM',
|
||||||
|
'ms': '31',
|
||||||
|
'MM/dd/yy hh:mm': '06/15/15 09:03',
|
||||||
|
'MM/dd/y': '06/15/2015'
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
|
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format with pattern aliases', () => {
|
||||||
|
const dateFixtures: any = {
|
||||||
|
'MM/dd/yyyy': '06/15/2015',
|
||||||
|
shortDate: '6/15/15',
|
||||||
|
mediumDate: 'Jun 15, 2015',
|
||||||
|
longDate: 'June 15, 2015',
|
||||||
|
fullDate: 'Monday, June 15, 2015',
|
||||||
|
short: '6/15/15, 9:03 AM',
|
||||||
|
medium: 'Jun 15, 2015, 9:03:01 AM',
|
||||||
|
long: /June 15, 2015 at 9:03:01 AM GMT(\+|-)\d/,
|
||||||
|
full: /Monday, June 15, 2015 at 9:03:01 AM GMT(\+|-)\d{2}:\d{2}/,
|
||||||
|
shortTime: '9:03 AM',
|
||||||
|
mediumTime: '9:03:01 AM',
|
||||||
|
longTime: /9:03:01 AM GMT(\+|-)\d/,
|
||||||
|
fullTime: /9:03:01 AM GMT(\+|-)\d{2}:\d{2}/,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
|
expect(formatDate(date, pattern, defaultLocale)).toMatch(dateFixtures[pattern]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format invalid in IE ISO date',
|
||||||
|
() => expect(formatDate('2017-01-11T12:00:00.014-0500', defaultFormat, defaultLocale))
|
||||||
|
.toEqual('Jan 11, 2017'));
|
||||||
|
|
||||||
|
it('should format invalid in Safari ISO date',
|
||||||
|
() => expect(formatDate('2017-01-20T12:00:00+0000', defaultFormat, defaultLocale))
|
||||||
|
.toEqual('Jan 20, 2017'));
|
||||||
|
|
||||||
|
// test for the following bugs:
|
||||||
|
// https://github.com/angular/angular/issues/9524
|
||||||
|
// https://github.com/angular/angular/issues/9524
|
||||||
|
it('should format correctly with iso strings that contain time',
|
||||||
|
() => expect(formatDate('2017-05-07T22:14:39', 'dd-MM-yyyy HH:mm', defaultLocale))
|
||||||
|
.toMatch(/07-05-2017 \d{2}:\d{2}/));
|
||||||
|
|
||||||
|
// test for issue https://github.com/angular/angular/issues/21491
|
||||||
|
it('should not assume UTC for iso strings in Safari if the timezone is not defined', () => {
|
||||||
|
// this test only works if the timezone is not in UTC
|
||||||
|
// which is the case for BrowserStack when we test Safari
|
||||||
|
if (new Date().getTimezoneOffset() !== 0) {
|
||||||
|
expect(formatDate('2018-01-11T13:00:00', 'HH', defaultLocale))
|
||||||
|
.not.toEqual(formatDate('2018-01-11T13:00:00Z', 'HH', defaultLocale));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// test for the following bugs:
|
||||||
|
// https://github.com/angular/angular/issues/16624
|
||||||
|
// https://github.com/angular/angular/issues/17478
|
||||||
|
it('should show the correct time when the timezone is fixed', () => {
|
||||||
|
expect(formatDate('2017-06-13T10:14:39+0000', 'shortTime', defaultLocale, '+0000'))
|
||||||
|
.toEqual('10:14 AM');
|
||||||
|
expect(formatDate('2017-06-13T10:14:39+0000', 'h:mm a', defaultLocale, '+0000'))
|
||||||
|
.toEqual('10:14 AM');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove bidi control characters',
|
||||||
|
() => expect(formatDate(date, 'MM/dd/yyyy', defaultLocale) !.length).toEqual(10));
|
||||||
|
|
||||||
|
it(`should format the date correctly in various locales`, () => {
|
||||||
|
expect(formatDate(date, 'short', 'de')).toEqual('15.06.15, 09:03');
|
||||||
|
expect(formatDate(date, 'short', 'ar')).toEqual('15/6/2015 9:03 ص');
|
||||||
|
expect(formatDate(date, 'dd-MM-yy', 'th')).toEqual('15-06-15');
|
||||||
|
expect(formatDate(date, 'a', 'hu')).toEqual('de.');
|
||||||
|
expect(formatDate(date, 'a', 'sr')).toEqual('пре подне');
|
||||||
|
|
||||||
|
// TODO(ocombe): activate this test when we support local numbers
|
||||||
|
// expect(formatDate(date, 'hh', 'mr')).toEqual('०९');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if we use getExtraDayPeriods without loading extra locale data', () => {
|
||||||
|
expect(() => formatDate(date, 'b', 'de'))
|
||||||
|
.toThrowError(/Missing extra locale data for the locale "de"/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
118
packages/common/test/i18n/format_number_spec.ts
Normal file
118
packages/common/test/i18n/format_number_spec.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import localeEn from '@angular/common/locales/en';
|
||||||
|
import localeEsUS from '@angular/common/locales/es-US';
|
||||||
|
import localeFr from '@angular/common/locales/fr';
|
||||||
|
import localeAr from '@angular/common/locales/ar';
|
||||||
|
import {formatCurrency, formatNumber, formatPercent, registerLocaleData} from '@angular/common';
|
||||||
|
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||||
|
|
||||||
|
describe('Format number', () => {
|
||||||
|
const defaultLocale = 'en-US';
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
registerLocaleData(localeEn);
|
||||||
|
registerLocaleData(localeEsUS);
|
||||||
|
registerLocaleData(localeFr);
|
||||||
|
registerLocaleData(localeAr);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Number', () => {
|
||||||
|
describe('transform', () => {
|
||||||
|
it('should return correct value for numbers', () => {
|
||||||
|
expect(formatNumber(12345, defaultLocale)).toEqual('12,345');
|
||||||
|
expect(formatNumber(123, defaultLocale, '.2')).toEqual('123.00');
|
||||||
|
expect(formatNumber(1, defaultLocale, '3.')).toEqual('001');
|
||||||
|
expect(formatNumber(1.1, defaultLocale, '3.4-5')).toEqual('001.1000');
|
||||||
|
expect(formatNumber(1.123456, defaultLocale, '3.4-5')).toEqual('001.12346');
|
||||||
|
expect(formatNumber(1.1234, defaultLocale)).toEqual('1.123');
|
||||||
|
expect(formatNumber(1.123456, defaultLocale, '.2')).toEqual('1.123');
|
||||||
|
expect(formatNumber(1.123456, defaultLocale, '.4')).toEqual('1.1235');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if minFractionDigits is explicitly higher than maxFractionDigits', () => {
|
||||||
|
expect(() => formatNumber(1.1, defaultLocale, '3.4-2'))
|
||||||
|
.toThrowError(/is higher than the maximum/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transform with custom locales', () => {
|
||||||
|
it('should return the correct format for es-US',
|
||||||
|
() => { expect(formatNumber(9999999.99, 'es-US', '1.2-2')).toEqual('9,999,999.99'); });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Percent', () => {
|
||||||
|
describe('transform', () => {
|
||||||
|
it('should return correct value for numbers', () => {
|
||||||
|
expect(formatPercent(1.23, defaultLocale)).toEqual('123%');
|
||||||
|
expect(formatPercent(1.2, defaultLocale, '.2')).toEqual('120.00%');
|
||||||
|
expect(formatPercent(1.2, defaultLocale, '4.2')).toEqual('0,120.00%');
|
||||||
|
expect(formatPercent(1.2, 'fr', '4.2')).toEqual('0 120,00 %');
|
||||||
|
expect(formatPercent(1.2, 'ar', '4.2')).toEqual('0,120.00%');
|
||||||
|
// see issue #20136
|
||||||
|
expect(formatPercent(0.12345674, defaultLocale, '0.0-10')).toEqual('12.345674%');
|
||||||
|
expect(formatPercent(0, defaultLocale, '0.0-10')).toEqual('0%');
|
||||||
|
expect(formatPercent(0.00, defaultLocale, '0.0-10')).toEqual('0%');
|
||||||
|
expect(formatPercent(1, defaultLocale, '0.0-10')).toEqual('100%');
|
||||||
|
expect(formatPercent(0.1, defaultLocale, '0.0-10')).toEqual('10%');
|
||||||
|
expect(formatPercent(0.12, defaultLocale, '0.0-10')).toEqual('12%');
|
||||||
|
expect(formatPercent(0.123, defaultLocale, '0.0-10')).toEqual('12.3%');
|
||||||
|
expect(formatPercent(12.3456, defaultLocale, '0.0-10')).toEqual('1,234.56%');
|
||||||
|
expect(formatPercent(12.345600, defaultLocale, '0.0-10')).toEqual('1,234.56%');
|
||||||
|
expect(formatPercent(12.345699999, defaultLocale, '0.0-6')).toEqual('1,234.57%');
|
||||||
|
expect(formatPercent(12.345699999, defaultLocale, '0.4-6')).toEqual('1,234.5700%');
|
||||||
|
expect(formatPercent(100, defaultLocale, '0.4-6')).toEqual('10,000.0000%');
|
||||||
|
expect(formatPercent(100, defaultLocale, '0.0-10')).toEqual('10,000%');
|
||||||
|
expect(formatPercent(1.5e2, defaultLocale)).toEqual('15,000%');
|
||||||
|
expect(formatPercent(1e100, defaultLocale)).toEqual('1E+102%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Currency', () => {
|
||||||
|
const defaultCurrencyCode = 'USD';
|
||||||
|
describe('transform', () => {
|
||||||
|
it('should return correct value for numbers', () => {
|
||||||
|
expect(formatCurrency(123, defaultLocale, '$')).toEqual('$123.00');
|
||||||
|
expect(formatCurrency(12, defaultLocale, 'EUR', 'EUR', '.1')).toEqual('EUR12.0');
|
||||||
|
expect(
|
||||||
|
formatCurrency(5.1234, defaultLocale, defaultCurrencyCode, defaultCurrencyCode, '.0-3'))
|
||||||
|
.toEqual('USD5.123');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, defaultCurrencyCode)).toEqual('USD5.12');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, '$')).toEqual('$5.12');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'CA$')).toEqual('CA$5.12');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, '$')).toEqual('$5.12');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, '$', defaultCurrencyCode, '5.2-2'))
|
||||||
|
.toEqual('$00,005.12');
|
||||||
|
expect(formatCurrency(5.1234, 'fr', '$', defaultCurrencyCode, '5.2-2'))
|
||||||
|
.toEqual('00 005,12 $');
|
||||||
|
expect(formatCurrency(5, 'fr', '$US', defaultCurrencyCode)).toEqual('5,00 $US');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support any currency code name', () => {
|
||||||
|
// currency code is unknown, default formatting options will be used
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'unexisting_ISO_code'))
|
||||||
|
.toEqual('unexisting_ISO_code5.12');
|
||||||
|
// currency code is USD, the pipe will format based on USD but will display "Custom name"
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'Custom name')).toEqual('Custom name5.12');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should round to the default number of digits if no digitsInfo', () => {
|
||||||
|
// IDR has a default number of digits of 0
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'IDR', 'IDR')).toEqual('IDR5');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'IDR', 'IDR', '.2')).toEqual('IDR5.12');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'Custom name', 'IDR')).toEqual('Custom name5');
|
||||||
|
// BHD has a default number of digits of 3
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'BHD', 'BHD')).toEqual('BHD5.123');
|
||||||
|
expect(formatCurrency(5.1234, defaultLocale, 'BHD', 'BHD', '.1-2')).toEqual('BHD5.12');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -13,7 +13,7 @@ import localeZh from '@angular/common/locales/zh';
|
|||||||
import localeFrCA from '@angular/common/locales/fr-CA';
|
import localeFrCA from '@angular/common/locales/fr-CA';
|
||||||
import localeEnAU from '@angular/common/locales/en-AU';
|
import localeEnAU from '@angular/common/locales/en-AU';
|
||||||
import {registerLocaleData} from '../../src/i18n/locale_data';
|
import {registerLocaleData} from '../../src/i18n/locale_data';
|
||||||
import {findLocaleData, getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNbOfCurrencyDigits} from '../../src/i18n/locale_data_api';
|
import {findLocaleData, getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api';
|
||||||
|
|
||||||
{
|
{
|
||||||
describe('locale data api', () => {
|
describe('locale data api', () => {
|
||||||
@ -76,10 +76,10 @@ import {findLocaleData, getCurrencySymbol, getLocaleDateFormat, FormatWidth, get
|
|||||||
|
|
||||||
describe('getNbOfCurrencyDigits', () => {
|
describe('getNbOfCurrencyDigits', () => {
|
||||||
it('should return the correct value', () => {
|
it('should return the correct value', () => {
|
||||||
expect(getNbOfCurrencyDigits('USD')).toEqual(2);
|
expect(getNumberOfCurrencyDigits('USD')).toEqual(2);
|
||||||
expect(getNbOfCurrencyDigits('IDR')).toEqual(0);
|
expect(getNumberOfCurrencyDigits('IDR')).toEqual(0);
|
||||||
expect(getNbOfCurrencyDigits('BHD')).toEqual(3);
|
expect(getNumberOfCurrencyDigits('BHD')).toEqual(3);
|
||||||
expect(getNbOfCurrencyDigits('unexisting_ISO_code')).toEqual(2);
|
expect(getNumberOfCurrencyDigits('unexisting_ISO_code')).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,15 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {DatePipe, registerLocaleData} from '@angular/common';
|
import {DatePipe, registerLocaleData} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
|
||||||
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';
|
|
||||||
import localeEn from '@angular/common/locales/en';
|
import localeEn from '@angular/common/locales/en';
|
||||||
import localeEnExtra from '@angular/common/locales/extra/en';
|
import localeEnExtra from '@angular/common/locales/extra/en';
|
||||||
import localeDe from '@angular/common/locales/de';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import localeHu from '@angular/common/locales/hu';
|
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';
|
||||||
import localeSr from '@angular/common/locales/sr';
|
|
||||||
import localeTh from '@angular/common/locales/th';
|
|
||||||
import localeAr from '@angular/common/locales/ar';
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let date: Date;
|
let date: Date;
|
||||||
@ -28,14 +23,7 @@ import localeAr from '@angular/common/locales/ar';
|
|||||||
expect(pipe.transform(date, pattern)).toEqual(output);
|
expect(pipe.transform(date, pattern)).toEqual(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => { registerLocaleData(localeEn, localeEnExtra); });
|
||||||
registerLocaleData(localeEn, localeEnExtra);
|
|
||||||
registerLocaleData(localeDe);
|
|
||||||
registerLocaleData(localeHu);
|
|
||||||
registerLocaleData(localeSr);
|
|
||||||
registerLocaleData(localeTh);
|
|
||||||
registerLocaleData(localeAr);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
date = new Date(2015, 5, 15, 9, 3, 1, 550);
|
date = new Date(2015, 5, 15, 9, 3, 1, 550);
|
||||||
@ -60,260 +48,21 @@ import localeAr from '@angular/common/locales/ar';
|
|||||||
it('should support ISO string',
|
it('should support ISO string',
|
||||||
() => expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow());
|
() => expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow());
|
||||||
|
|
||||||
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
it('should return null for empty string',
|
||||||
|
() => { expect(pipe.transform('')).toEqual(null); });
|
||||||
|
|
||||||
it('should return null for NaN', () => expect(pipe.transform(Number.NaN)).toEqual(null));
|
it('should return null for NaN', () => { expect(pipe.transform(Number.NaN)).toEqual(null); });
|
||||||
|
|
||||||
it('should support ISO string without time',
|
it('should support ISO string without time',
|
||||||
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
||||||
|
|
||||||
it('should not support other objects',
|
it('should not support other objects',
|
||||||
() => expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/));
|
() => { expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/); });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should format each component correctly', () => {
|
it('should use "mediumDate" as the default format',
|
||||||
const dateFixtures: any = {
|
() => expect(pipe.transform('2017-01-11T10:14:39+0000')).toEqual('Jan 11, 2017'));
|
||||||
G: 'AD',
|
|
||||||
GG: 'AD',
|
|
||||||
GGG: 'AD',
|
|
||||||
GGGG: 'Anno Domini',
|
|
||||||
GGGGG: 'A',
|
|
||||||
y: '2015',
|
|
||||||
yy: '15',
|
|
||||||
yyy: '2015',
|
|
||||||
yyyy: '2015',
|
|
||||||
M: '6',
|
|
||||||
MM: '06',
|
|
||||||
MMM: 'Jun',
|
|
||||||
MMMM: 'June',
|
|
||||||
MMMMM: 'J',
|
|
||||||
L: '6',
|
|
||||||
LL: '06',
|
|
||||||
LLL: 'Jun',
|
|
||||||
LLLL: 'June',
|
|
||||||
LLLLL: 'J',
|
|
||||||
w: '25',
|
|
||||||
ww: '25',
|
|
||||||
W: '3',
|
|
||||||
d: '15',
|
|
||||||
dd: '15',
|
|
||||||
E: 'Mon',
|
|
||||||
EE: 'Mon',
|
|
||||||
EEE: 'Mon',
|
|
||||||
EEEE: 'Monday',
|
|
||||||
EEEEEE: 'Mo',
|
|
||||||
h: '9',
|
|
||||||
hh: '09',
|
|
||||||
H: '9',
|
|
||||||
HH: '09',
|
|
||||||
m: '3',
|
|
||||||
mm: '03',
|
|
||||||
s: '1',
|
|
||||||
ss: '01',
|
|
||||||
S: '6',
|
|
||||||
SS: '55',
|
|
||||||
SSS: '550',
|
|
||||||
a: 'AM',
|
|
||||||
aa: 'AM',
|
|
||||||
aaa: 'AM',
|
|
||||||
aaaa: 'AM',
|
|
||||||
aaaaa: 'a',
|
|
||||||
b: 'morning',
|
|
||||||
bb: 'morning',
|
|
||||||
bbb: 'morning',
|
|
||||||
bbbb: 'morning',
|
|
||||||
bbbbb: 'morning',
|
|
||||||
B: 'in the morning',
|
|
||||||
BB: 'in the morning',
|
|
||||||
BBB: 'in the morning',
|
|
||||||
BBBB: 'in the morning',
|
|
||||||
BBBBB: 'in the morning',
|
|
||||||
};
|
|
||||||
|
|
||||||
const isoStringWithoutTimeFixtures: any = {
|
|
||||||
G: 'AD',
|
|
||||||
GG: 'AD',
|
|
||||||
GGG: 'AD',
|
|
||||||
GGGG: 'Anno Domini',
|
|
||||||
GGGGG: 'A',
|
|
||||||
y: '2015',
|
|
||||||
yy: '15',
|
|
||||||
yyy: '2015',
|
|
||||||
yyyy: '2015',
|
|
||||||
M: '1',
|
|
||||||
MM: '01',
|
|
||||||
MMM: 'Jan',
|
|
||||||
MMMM: 'January',
|
|
||||||
MMMMM: 'J',
|
|
||||||
L: '1',
|
|
||||||
LL: '01',
|
|
||||||
LLL: 'Jan',
|
|
||||||
LLLL: 'January',
|
|
||||||
LLLLL: 'J',
|
|
||||||
w: '1',
|
|
||||||
ww: '01',
|
|
||||||
W: '1',
|
|
||||||
d: '1',
|
|
||||||
dd: '01',
|
|
||||||
E: 'Thu',
|
|
||||||
EE: 'Thu',
|
|
||||||
EEE: 'Thu',
|
|
||||||
EEEE: 'Thursday',
|
|
||||||
EEEEE: 'T',
|
|
||||||
EEEEEE: 'Th',
|
|
||||||
h: '12',
|
|
||||||
hh: '12',
|
|
||||||
H: '0',
|
|
||||||
HH: '00',
|
|
||||||
m: '0',
|
|
||||||
mm: '00',
|
|
||||||
s: '0',
|
|
||||||
ss: '00',
|
|
||||||
S: '0',
|
|
||||||
SS: '00',
|
|
||||||
SSS: '000',
|
|
||||||
a: 'AM',
|
|
||||||
aa: 'AM',
|
|
||||||
aaa: 'AM',
|
|
||||||
aaaa: 'AM',
|
|
||||||
aaaaa: 'a',
|
|
||||||
b: 'midnight',
|
|
||||||
bb: 'midnight',
|
|
||||||
bbb: 'midnight',
|
|
||||||
bbbb: 'midnight',
|
|
||||||
bbbbb: 'midnight',
|
|
||||||
B: 'midnight',
|
|
||||||
BB: 'midnight',
|
|
||||||
BBB: 'midnight',
|
|
||||||
BBBB: 'midnight',
|
|
||||||
BBBBB: 'mi',
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should format with timezones', () => {
|
|
||||||
const dateFixtures: any = {
|
|
||||||
z: /GMT(\+|-)\d/,
|
|
||||||
zz: /GMT(\+|-)\d/,
|
|
||||||
zzz: /GMT(\+|-)\d/,
|
|
||||||
zzzz: /GMT(\+|-)\d{2}\:30/,
|
|
||||||
Z: /(\+|-)\d{2}30/,
|
|
||||||
ZZ: /(\+|-)\d{2}30/,
|
|
||||||
ZZZ: /(\+|-)\d{2}30/,
|
|
||||||
ZZZZ: /GMT(\+|-)\d{2}\:30/,
|
|
||||||
ZZZZZ: /(\+|-)\d{2}\:30/,
|
|
||||||
O: /GMT(\+|-)\d/,
|
|
||||||
OOOO: /GMT(\+|-)\d{2}\:30/,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expect(pipe.transform(date, pattern, '+0430')).toMatch(dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should format common multi component patterns', () => {
|
|
||||||
const dateFixtures: any = {
|
|
||||||
'EEE, M/d/y': 'Mon, 6/15/2015',
|
|
||||||
'EEE, M/d': 'Mon, 6/15',
|
|
||||||
'MMM d': 'Jun 15',
|
|
||||||
'dd/MM/yyyy': '15/06/2015',
|
|
||||||
'MM/dd/yyyy': '06/15/2015',
|
|
||||||
'yMEEEd': '20156Mon15',
|
|
||||||
'MEEEd': '6Mon15',
|
|
||||||
'MMMd': 'Jun15',
|
|
||||||
'EEEE, MMMM d, y': 'Monday, June 15, 2015',
|
|
||||||
'H:mm a': '9:03 AM',
|
|
||||||
'ms': '31',
|
|
||||||
'MM/dd/yy hh:mm': '06/15/15 09:03',
|
|
||||||
'MM/dd/y': '06/15/2015'
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should format with pattern aliases', () => {
|
|
||||||
const dateFixtures: any = {
|
|
||||||
'MM/dd/yyyy': '06/15/2015',
|
|
||||||
shortDate: '6/15/15',
|
|
||||||
mediumDate: 'Jun 15, 2015',
|
|
||||||
longDate: 'June 15, 2015',
|
|
||||||
fullDate: 'Monday, June 15, 2015',
|
|
||||||
short: '6/15/15, 9:03 AM',
|
|
||||||
medium: 'Jun 15, 2015, 9:03:01 AM',
|
|
||||||
long: /June 15, 2015 at 9:03:01 AM GMT(\+|-)\d/,
|
|
||||||
full: /Monday, June 15, 2015 at 9:03:01 AM GMT(\+|-)\d{2}:\d{2}/,
|
|
||||||
shortTime: '9:03 AM',
|
|
||||||
mediumTime: '9:03:01 AM',
|
|
||||||
longTime: /9:03:01 AM GMT(\+|-)\d/,
|
|
||||||
fullTime: /9:03:01 AM GMT(\+|-)\d{2}:\d{2}/,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
|
||||||
expect(pipe.transform(date, pattern)).toMatch(dateFixtures[pattern]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should format invalid in IE ISO date',
|
|
||||||
() => expect(pipe.transform('2017-01-11T12:00:00.014-0500')).toEqual('Jan 11, 2017'));
|
|
||||||
|
|
||||||
it('should format invalid in Safari ISO date',
|
|
||||||
() => expect(pipe.transform('2017-01-20T12:00:00+0000')).toEqual('Jan 20, 2017'));
|
|
||||||
|
|
||||||
// test for the following bugs:
|
|
||||||
// https://github.com/angular/angular/issues/9524
|
|
||||||
// https://github.com/angular/angular/issues/9524
|
|
||||||
it('should format correctly with iso strings that contain time',
|
|
||||||
() => expect(pipe.transform('2017-05-07T22:14:39', 'dd-MM-yyyy HH:mm'))
|
|
||||||
.toMatch(/07-05-2017 \d{2}:\d{2}/));
|
|
||||||
|
|
||||||
// test for issue https://github.com/angular/angular/issues/21491
|
|
||||||
it('should not assume UTC for iso strings in Safari if the timezone is not defined', () => {
|
|
||||||
// this test only works if the timezone is not in UTC
|
|
||||||
// which is the case for BrowserStack when we test Safari
|
|
||||||
if (new Date().getTimezoneOffset() !== 0) {
|
|
||||||
expect(pipe.transform('2018-01-11T13:00:00', 'HH'))
|
|
||||||
.not.toEqual(pipe.transform('2018-01-11T13:00:00Z', 'HH'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// test for the following bugs:
|
|
||||||
// https://github.com/angular/angular/issues/16624
|
|
||||||
// https://github.com/angular/angular/issues/17478
|
|
||||||
it('should show the correct time when the timezone is fixed', () => {
|
|
||||||
expect(pipe.transform('2017-06-13T10:14:39+0000', 'shortTime', '+0000'))
|
|
||||||
.toEqual('10:14 AM');
|
|
||||||
expect(pipe.transform('2017-06-13T10:14:39+0000', 'h:mm a', '+0000')).toEqual('10:14 AM');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove bidi control characters',
|
|
||||||
() => expect(pipe.transform(date, 'MM/dd/yyyy') !.length).toEqual(10));
|
|
||||||
|
|
||||||
it(`should format the date correctly in various locales`, () => {
|
|
||||||
expect(new DatePipe('de').transform(date, 'short')).toEqual('15.06.15, 09:03');
|
|
||||||
expect(new DatePipe('ar').transform(date, 'short')).toEqual('15/6/2015 9:03 ص');
|
|
||||||
expect(new DatePipe('th').transform(date, 'dd-MM-yy')).toEqual('15-06-15');
|
|
||||||
expect(new DatePipe('hu').transform(date, 'a')).toEqual('de.');
|
|
||||||
expect(new DatePipe('sr').transform(date, 'a')).toEqual('пре подне');
|
|
||||||
|
|
||||||
// TODO(ocombe): activate this test when we support local numbers
|
|
||||||
// expect(new DatePipe('mr', [localeMr]).transform(date, 'hh')).toEqual('०९');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw if we use getExtraDayPeriods without loading extra locale data', () => {
|
|
||||||
expect(() => new DatePipe('de').transform(date, 'b'))
|
|
||||||
.toThrowError(/Missing extra locale data for the locale "de"/);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import localeEn from '@angular/common/locales/en';
|
|||||||
import localeEsUS from '@angular/common/locales/es-US';
|
import localeEsUS from '@angular/common/locales/es-US';
|
||||||
import localeFr from '@angular/common/locales/fr';
|
import localeFr from '@angular/common/locales/fr';
|
||||||
import localeAr from '@angular/common/locales/ar';
|
import localeAr from '@angular/common/locales/ar';
|
||||||
import {registerLocaleData, CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
|
import {registerLocaleData, CurrencyPipe, DecimalPipe, PercentPipe, formatNumber} from '@angular/common';
|
||||||
import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal';
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -22,8 +22,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
registerLocaleData(localeAr);
|
registerLocaleData(localeAr);
|
||||||
});
|
});
|
||||||
|
|
||||||
function isNumeric(value: any): boolean { return !isNaN(value - parseFloat(value)); }
|
|
||||||
|
|
||||||
describe('DecimalPipe', () => {
|
describe('DecimalPipe', () => {
|
||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
let pipe: DecimalPipe;
|
let pipe: DecimalPipe;
|
||||||
@ -31,13 +29,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
|
|
||||||
it('should return correct value for numbers', () => {
|
it('should return correct value for numbers', () => {
|
||||||
expect(pipe.transform(12345)).toEqual('12,345');
|
expect(pipe.transform(12345)).toEqual('12,345');
|
||||||
expect(pipe.transform(123, '.2')).toEqual('123.00');
|
|
||||||
expect(pipe.transform(1, '3.')).toEqual('001');
|
|
||||||
expect(pipe.transform(1.1, '3.4-5')).toEqual('001.1000');
|
|
||||||
expect(pipe.transform(1.123456, '3.4-5')).toEqual('001.12346');
|
expect(pipe.transform(1.123456, '3.4-5')).toEqual('001.12346');
|
||||||
expect(pipe.transform(1.1234)).toEqual('1.123');
|
|
||||||
expect(pipe.transform(1.123456, '.2')).toEqual('1.123');
|
|
||||||
expect(pipe.transform(1.123456, '.4')).toEqual('1.1235');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support strings', () => {
|
it('should support strings', () => {
|
||||||
@ -56,10 +48,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
expect(() => pipe.transform('123abc'))
|
expect(() => pipe.transform('123abc'))
|
||||||
.toThrowError(`InvalidPipeArgument: '123abc is not a number' for pipe 'DecimalPipe'`);
|
.toThrowError(`InvalidPipeArgument: '123abc is not a number' for pipe 'DecimalPipe'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if minFractionDigits is explicitly higher than maxFractionDigits', () => {
|
|
||||||
expect(() => pipe.transform('1.1', '3.4-2')).toThrowError(/is higher than the maximum/);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('transform with custom locales', () => {
|
describe('transform with custom locales', () => {
|
||||||
@ -78,26 +66,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return correct value for numbers', () => {
|
it('should return correct value for numbers', () => {
|
||||||
expect(pipe.transform(1.23)).toEqual('123%');
|
expect(pipe.transform(1.23)).toEqual('123%');
|
||||||
expect(pipe.transform(1.2, '.2')).toEqual('120.00%');
|
|
||||||
expect(pipe.transform(1.2, '4.2')).toEqual('0,120.00%');
|
|
||||||
expect(pipe.transform(1.2, '4.2', 'fr')).toEqual('0 120,00 %');
|
|
||||||
expect(pipe.transform(1.2, '4.2', 'ar')).toEqual('0,120.00%');
|
|
||||||
// see issue #20136
|
|
||||||
expect(pipe.transform(0.12345674, '0.0-10')).toEqual('12.345674%');
|
|
||||||
expect(pipe.transform(0, '0.0-10')).toEqual('0%');
|
|
||||||
expect(pipe.transform(0.00, '0.0-10')).toEqual('0%');
|
|
||||||
expect(pipe.transform(1, '0.0-10')).toEqual('100%');
|
|
||||||
expect(pipe.transform(0.1, '0.0-10')).toEqual('10%');
|
|
||||||
expect(pipe.transform(0.12, '0.0-10')).toEqual('12%');
|
|
||||||
expect(pipe.transform(0.123, '0.0-10')).toEqual('12.3%');
|
|
||||||
expect(pipe.transform(12.3456, '0.0-10')).toEqual('1,234.56%');
|
expect(pipe.transform(12.3456, '0.0-10')).toEqual('1,234.56%');
|
||||||
expect(pipe.transform(12.345600, '0.0-10')).toEqual('1,234.56%');
|
|
||||||
expect(pipe.transform(12.345699999, '0.0-6')).toEqual('1,234.57%');
|
|
||||||
expect(pipe.transform(12.345699999, '0.4-6')).toEqual('1,234.5700%');
|
|
||||||
expect(pipe.transform(100, '0.4-6')).toEqual('10,000.0000%');
|
|
||||||
expect(pipe.transform(100, '0.0-10')).toEqual('10,000%');
|
|
||||||
expect(pipe.transform(1.5e2)).toEqual('15,000%');
|
|
||||||
expect(pipe.transform(1e100)).toEqual('1E+102%');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not support other objects', () => {
|
it('should not support other objects', () => {
|
||||||
@ -136,16 +105,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
expect(pipe.transform(5.1234, 'USD', 'Custom name')).toEqual('Custom name5.12');
|
expect(pipe.transform(5.1234, 'USD', 'Custom name')).toEqual('Custom name5.12');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should round to the default number of digits if no digitsInfo', () => {
|
|
||||||
// IDR has a default number of digits of 0
|
|
||||||
expect(pipe.transform(5.1234, 'IDR')).toEqual('IDR5');
|
|
||||||
expect(pipe.transform(5.1234, 'IDR', 'symbol', '.2')).toEqual('IDR5.12');
|
|
||||||
expect(pipe.transform(5.1234, 'IDR', 'Custom name')).toEqual('Custom name5');
|
|
||||||
// BHD has a default number of digits of 3
|
|
||||||
expect(pipe.transform(5.1234, 'BHD')).toEqual('BHD5.123');
|
|
||||||
expect(pipe.transform(5.1234, 'BHD', 'symbol', '.1-2')).toEqual('BHD5.12');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not support other objects', () => {
|
it('should not support other objects', () => {
|
||||||
expect(() => pipe.transform({}))
|
expect(() => pipe.transform({}))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
@ -160,25 +119,5 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isNumeric', () => {
|
|
||||||
it('should return true when passing correct numeric string',
|
|
||||||
() => { expect(isNumeric('2')).toBe(true); });
|
|
||||||
|
|
||||||
it('should return true when passing correct double string',
|
|
||||||
() => { expect(isNumeric('1.123')).toBe(true); });
|
|
||||||
|
|
||||||
it('should return true when passing correct negative string',
|
|
||||||
() => { expect(isNumeric('-2')).toBe(true); });
|
|
||||||
|
|
||||||
it('should return true when passing correct scientific notation string',
|
|
||||||
() => { expect(isNumeric('1e5')).toBe(true); });
|
|
||||||
|
|
||||||
it('should return false when passing incorrect numeric',
|
|
||||||
() => { expect(isNumeric('a')).toBe(false); });
|
|
||||||
|
|
||||||
it('should return false when passing parseable but non numeric',
|
|
||||||
() => { expect(isNumeric('2a')).toBe(false); });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
14
tools/public_api_guard/common/common.d.ts
vendored
14
tools/public_api_guard/common/common.d.ts
vendored
@ -64,6 +64,18 @@ export declare class DeprecatedPercentPipe implements PipeTransform {
|
|||||||
/** @stable */
|
/** @stable */
|
||||||
export declare const DOCUMENT: InjectionToken<Document>;
|
export declare const DOCUMENT: InjectionToken<Document>;
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare function formatCurrency(value: number, locale: string, currency: string, currencyCode?: string, digitsInfo?: string): string;
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare function formatDate(value: string | number | Date, format: string, locale: string, timezone?: string): string;
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare function formatNumber(value: number, locale: string, digitsInfo?: string): string;
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare function formatPercent(value: number, locale: string, digitsInfo?: string): string;
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare enum FormatWidth {
|
export declare enum FormatWidth {
|
||||||
Short = 0,
|
Short = 0,
|
||||||
@ -133,7 +145,7 @@ export declare function getLocaleTimeFormat(locale: string, width: FormatWidth):
|
|||||||
export declare function getLocaleWeekEndRange(locale: string): [WeekDay, WeekDay];
|
export declare function getLocaleWeekEndRange(locale: string): [WeekDay, WeekDay];
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare function getNbOfCurrencyDigits(code: string): number;
|
export declare function getNumberOfCurrencyDigits(code: string): number;
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class HashLocationStrategy extends LocationStrategy {
|
export declare class HashLocationStrategy extends LocationStrategy {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user