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.
|
||||
*/
|
||||
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 {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 {CommonModule, DeprecatedI18NPipesModule} from './common_module';
|
||||
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';
|
||||
|
||||
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 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]*)/;
|
||||
|
@ -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);
|
||||
format = namedFormat || format;
|
||||
|
||||
|
@ -165,8 +184,10 @@ function padNumber(
|
|||
neg = minusSign;
|
||||
}
|
||||
}
|
||||
let strNum = '' + num;
|
||||
while (strNum.length < digits) strNum = '0' + strNum;
|
||||
let strNum = String(num);
|
||||
while (strNum.length < digits) {
|
||||
strNum = '0' + strNum;
|
||||
}
|
||||
if (trim) {
|
||||
strNum = strNum.substr(strNum.length - digits);
|
||||
}
|
||||
|
@ -607,3 +628,90 @@ function convertTimezoneToLocal(date: Date, timezone: string, reverse: boolean):
|
|||
const timezoneOffset = timezoneToOffset(timezone, 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
|
||||
*/
|
||||
|
||||
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+))?)?$/;
|
||||
const MAX_DIGITS = 22;
|
||||
|
@ -18,34 +18,19 @@ const DIGIT_CHAR = '#';
|
|||
const CURRENCY_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
|
||||
*/
|
||||
function formatNumber(
|
||||
value: number | string, pattern: ParsedNumberFormat, locale: string, groupSymbol: NumberSymbol,
|
||||
function formatNumberToLocaleString(
|
||||
value: number, pattern: ParsedNumberFormat, locale: string, groupSymbol: NumberSymbol,
|
||||
decimalSymbol: NumberSymbol, digitsInfo?: string, isPercent = false): string {
|
||||
let formattedText = '';
|
||||
let isZero = false;
|
||||
const num = strToNumber(value);
|
||||
|
||||
if (!isFinite(num)) {
|
||||
if (!isFinite(value)) {
|
||||
formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity);
|
||||
} else {
|
||||
let parsedNumber = parseNumber(num);
|
||||
let parsedNumber = parseNumber(value);
|
||||
|
||||
if (isPercent) {
|
||||
parsedNumber = toPercent(parsedNumber);
|
||||
|
@ -128,7 +113,7 @@ function formatNumber(
|
|||
}
|
||||
}
|
||||
|
||||
if (num < 0 && !isZero) {
|
||||
if (value < 0 && !isZero) {
|
||||
formattedText = pattern.negPre + formattedText + pattern.negSuf;
|
||||
} else {
|
||||
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(
|
||||
value: number | string, locale: string, currency: string, currencyCode?: string,
|
||||
value: number, locale: string, currency: string, currencyCode?: string,
|
||||
digitsInfo?: string): string {
|
||||
const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency);
|
||||
const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
||||
|
||||
pattern.minFrac = getNbOfCurrencyDigits(currencyCode !);
|
||||
pattern.minFrac = getNumberOfCurrencyDigits(currencyCode !);
|
||||
pattern.maxFrac = pattern.minFrac;
|
||||
|
||||
const res = formatNumber(
|
||||
const res = formatNumberToLocaleString(
|
||||
value, pattern, locale, NumberSymbol.CurrencyGroup, NumberSymbol.CurrencyDecimal, digitsInfo);
|
||||
return res
|
||||
.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 pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
||||
const res = formatNumber(
|
||||
const res = formatNumberToLocaleString(
|
||||
value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo, true);
|
||||
return res.replace(
|
||||
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 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 {
|
||||
|
@ -335,7 +352,7 @@ function parseNumber(num: number): ParsedNumber {
|
|||
digits = [];
|
||||
// Convert string to array of digits without leading/trailing zeros.
|
||||
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 {
|
||||
const result: number = parseInt(text);
|
||||
if (isNaN(result)) {
|
||||
|
|
|
@ -560,7 +560,7 @@ const DEFAULT_NB_OF_CURRENCY_DIGITS = 2;
|
|||
*
|
||||
* @experimental i18n support is experimental.
|
||||
*/
|
||||
export function getNbOfCurrencyDigits(code: string): number {
|
||||
export function getNumberOfCurrencyDigits(code: string): number {
|
||||
let digits;
|
||||
const currency = CURRENCIES_EN[code];
|
||||
if (currency) {
|
||||
|
|
|
@ -10,41 +10,36 @@ import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
|||
import {formatDate} from '../i18n/format_date';
|
||||
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
|
||||
/**
|
||||
* @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]]]`
|
||||
* @description
|
||||
*
|
||||
* 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).
|
||||
* - `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.
|
||||
* - `'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`)
|
||||
* - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (e.g. `June 15, 2015 at 9:03:01 AM GMT+1`)
|
||||
* - `'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`).
|
||||
* - `'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
|
||||
* 9:03:01 AM GMT+01:00`)
|
||||
* - `'shortDate'`: equivalent to `'M/d/yy'` (e.g. `6/15/15`)
|
||||
* - `'mediumDate'`: equivalent to `'MMM d, y'` (e.g. `Jun 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`)
|
||||
* - `'shortTime'`: equivalent to `'h:mm a'` (e.g. `9:03 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`)
|
||||
* - `'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
|
||||
* abbreviations, but for general use, use a time zone offset, for example,
|
||||
* `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
|
||||
* 9:03:01 AM GMT+01:00`).
|
||||
* - `'shortDate'`: equivalent to `'M/d/yy'` (e.g. `6/15/15`).
|
||||
* - `'mediumDate'`: equivalent to `'MMM d, y'` (e.g. `Jun 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`).
|
||||
* - `'shortTime'`: equivalent to `'h:mm a'` (e.g. `9:03 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`).
|
||||
* - `'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
|
||||
* abbreviations, but for general use, use a time zone offset (e.g. `'+0430'`).
|
||||
* 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
|
||||
* default)
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default).
|
||||
*
|
||||
*
|
||||
* | 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 {
|
||||
if (value == null || value === '' || value !== value) return null;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
value = value.trim();
|
||||
try {
|
||||
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 {ISO8601_DATE_REGEX, isoStringToDate} from '../date_pipe';
|
||||
import {ISO8601_DATE_REGEX, isoStringToDate} from '../../i18n/format_date';
|
||||
import {invalidPipeArgumentError} from '../invalid_pipe_argument_error';
|
||||
import {DateFormatter} from './intl';
|
||||
|
||||
|
|
|
@ -7,29 +7,28 @@
|
|||
*/
|
||||
|
||||
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 {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
* @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]]`
|
||||
* @description
|
||||
*
|
||||
* 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:
|
||||
* - `digitInfo` is a `string` which has a following format: <br>
|
||||
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>
|
||||
* Where:
|
||||
* - `value` is a number
|
||||
* - `digitInfo` is a `string` which has a following format: <br>
|
||||
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
|
||||
* - `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`.
|
||||
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
*
|
||||
* For more information on the acceptable range for each of these numbers and other
|
||||
* details see your native internationalization library.
|
||||
* - `minFractionDigits` is the minimum number of digits after the decimal point. Defaults to `0`.
|
||||
* - `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
|
||||
* default).
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -47,7 +46,8 @@ export class DecimalPipe implements PipeTransform {
|
|||
locale = locale || this._locale;
|
||||
|
||||
try {
|
||||
return formatDecimal(value, locale, digitsInfo);
|
||||
const num = strToNumber(value);
|
||||
return formatNumber(num, locale, digitsInfo);
|
||||
} catch (error) {
|
||||
throw invalidPipeArgumentError(DecimalPipe, error.message);
|
||||
}
|
||||
|
@ -56,16 +56,18 @@ export class DecimalPipe implements PipeTransform {
|
|||
|
||||
/**
|
||||
* @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]]`
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Formats a number as percentage.
|
||||
*
|
||||
* - `digitInfo` See {@link DecimalPipe} for a detailed description.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
* 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
|
||||
* default).
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -83,7 +85,8 @@ export class PercentPipe implements PipeTransform {
|
|||
locale = locale || this._locale;
|
||||
|
||||
try {
|
||||
return formatPercent(value, locale, digitsInfo);
|
||||
const num = strToNumber(value);
|
||||
return formatPercent(num, locale, digitsInfo);
|
||||
} catch (error) {
|
||||
throw invalidPipeArgumentError(PercentPipe, error.message);
|
||||
}
|
||||
|
@ -92,25 +95,28 @@ export class PercentPipe implements PipeTransform {
|
|||
|
||||
/**
|
||||
* @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]]]]`
|
||||
* @description
|
||||
*
|
||||
* 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
|
||||
* 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`).
|
||||
* - `symbol`(default): use symbol (e.g. `$`).
|
||||
* - `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 `$`).
|
||||
* - `string`: use this value instead of a code or a symbol
|
||||
* - boolean (deprecated from v5): `true` for symbol and false for `code`
|
||||
* - `string`: use this value instead of a code or a symbol.
|
||||
* - 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.
|
||||
* - `digitInfo` See {@link DecimalPipe} for a detailed description.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default)
|
||||
* - `digitInfo` See {@link DecimalPipe} for more details.
|
||||
* - `locale` is a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
||||
* default).
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -148,7 +154,8 @@ export class CurrencyPipe implements PipeTransform {
|
|||
}
|
||||
|
||||
try {
|
||||
return formatCurrency(value, locale, currency, currencyCode, digitsInfo);
|
||||
const num = strToNumber(value);
|
||||
return formatCurrency(num, locale, currency, currencyCode, digitsInfo);
|
||||
} catch (error) {
|
||||
throw invalidPipeArgumentError(CurrencyPipe, error.message);
|
||||
}
|
||||
|
@ -158,3 +165,17 @@ export class CurrencyPipe implements PipeTransform {
|
|||
function isEmpty(value: any): boolean {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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"/);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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 localeEnAU from '@angular/common/locales/en-AU';
|
||||
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', () => {
|
||||
|
@ -76,10 +76,10 @@ import {findLocaleData, getCurrencySymbol, getLocaleDateFormat, FormatWidth, get
|
|||
|
||||
describe('getNbOfCurrencyDigits', () => {
|
||||
it('should return the correct value', () => {
|
||||
expect(getNbOfCurrencyDigits('USD')).toEqual(2);
|
||||
expect(getNbOfCurrencyDigits('IDR')).toEqual(0);
|
||||
expect(getNbOfCurrencyDigits('BHD')).toEqual(3);
|
||||
expect(getNbOfCurrencyDigits('unexisting_ISO_code')).toEqual(2);
|
||||
expect(getNumberOfCurrencyDigits('USD')).toEqual(2);
|
||||
expect(getNumberOfCurrencyDigits('IDR')).toEqual(0);
|
||||
expect(getNumberOfCurrencyDigits('BHD')).toEqual(3);
|
||||
expect(getNumberOfCurrencyDigits('unexisting_ISO_code')).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
*/
|
||||
|
||||
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 localeEnExtra from '@angular/common/locales/extra/en';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
import localeHu from '@angular/common/locales/hu';
|
||||
import localeSr from '@angular/common/locales/sr';
|
||||
import localeTh from '@angular/common/locales/th';
|
||||
import localeAr from '@angular/common/locales/ar';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';
|
||||
|
||||
{
|
||||
let date: Date;
|
||||
|
@ -28,14 +23,7 @@ import localeAr from '@angular/common/locales/ar';
|
|||
expect(pipe.transform(date, pattern)).toEqual(output);
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
registerLocaleData(localeEn, localeEnExtra);
|
||||
registerLocaleData(localeDe);
|
||||
registerLocaleData(localeHu);
|
||||
registerLocaleData(localeSr);
|
||||
registerLocaleData(localeTh);
|
||||
registerLocaleData(localeAr);
|
||||
});
|
||||
beforeAll(() => { registerLocaleData(localeEn, localeEnExtra); });
|
||||
|
||||
beforeEach(() => {
|
||||
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',
|
||||
() => 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',
|
||||
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
||||
|
||||
it('should not support other objects',
|
||||
() => expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/));
|
||||
() => { expect(() => pipe.transform({})).toThrowError(/InvalidPipeArgument/); });
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
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(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"/);
|
||||
});
|
||||
it('should use "mediumDate" as the default format',
|
||||
() => expect(pipe.transform('2017-01-11T10:14:39+0000')).toEqual('Jan 11, 2017'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ 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 {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';
|
||||
|
||||
{
|
||||
|
@ -22,8 +22,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||
registerLocaleData(localeAr);
|
||||
});
|
||||
|
||||
function isNumeric(value: any): boolean { return !isNaN(value - parseFloat(value)); }
|
||||
|
||||
describe('DecimalPipe', () => {
|
||||
describe('transform', () => {
|
||||
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', () => {
|
||||
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.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', () => {
|
||||
|
@ -56,10 +48,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||
expect(() => pipe.transform('123abc'))
|
||||
.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', () => {
|
||||
|
@ -78,26 +66,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||
describe('transform', () => {
|
||||
it('should return correct value for numbers', () => {
|
||||
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.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', () => {
|
||||
|
@ -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');
|
||||
});
|
||||
|
||||
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', () => {
|
||||
expect(() => pipe.transform({}))
|
||||
.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); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -64,6 +64,18 @@ export declare class DeprecatedPercentPipe implements PipeTransform {
|
|||
/** @stable */
|
||||
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 */
|
||||
export declare enum FormatWidth {
|
||||
Short = 0,
|
||||
|
@ -133,7 +145,7 @@ export declare function getLocaleTimeFormat(locale: string, width: FormatWidth):
|
|||
export declare function getLocaleWeekEndRange(locale: string): [WeekDay, WeekDay];
|
||||
|
||||
/** @experimental */
|
||||
export declare function getNbOfCurrencyDigits(code: string): number;
|
||||
export declare function getNumberOfCurrencyDigits(code: string): number;
|
||||
|
||||
/** @stable */
|
||||
export declare class HashLocationStrategy extends LocationStrategy {
|
||||
|
|
Loading…
Reference in New Issue