parent
3143d188ae
commit
b716046b97
|
@ -12,5 +12,6 @@ export {ObservablePipe} from './src/change_detection/pipes/observable_pipe';
|
||||||
export {JsonPipe} from './src/change_detection/pipes/json_pipe';
|
export {JsonPipe} from './src/change_detection/pipes/json_pipe';
|
||||||
export {IterableChanges} from './src/change_detection/pipes/iterable_changes';
|
export {IterableChanges} from './src/change_detection/pipes/iterable_changes';
|
||||||
export {KeyValueChanges} from './src/change_detection/pipes/keyvalue_changes';
|
export {KeyValueChanges} from './src/change_detection/pipes/keyvalue_changes';
|
||||||
|
export {DatePipe} from './src/change_detection/pipes/date_pipe';
|
||||||
export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe';
|
export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe';
|
||||||
export {LimitToPipe} from './src/change_detection/pipes/limit_to_pipe';
|
export {LimitToPipe} from './src/change_detection/pipes/limit_to_pipe';
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {UpperCaseFactory} from './pipes/uppercase_pipe';
|
||||||
import {LowerCaseFactory} from './pipes/lowercase_pipe';
|
import {LowerCaseFactory} from './pipes/lowercase_pipe';
|
||||||
import {JsonPipe} from './pipes/json_pipe';
|
import {JsonPipe} from './pipes/json_pipe';
|
||||||
import {LimitToPipeFactory} from './pipes/limit_to_pipe';
|
import {LimitToPipeFactory} from './pipes/limit_to_pipe';
|
||||||
|
import {DatePipe} from './pipes/date_pipe';
|
||||||
import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
|
import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
|
||||||
import {NullPipeFactory} from './pipes/null_pipe';
|
import {NullPipeFactory} from './pipes/null_pipe';
|
||||||
import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
||||||
|
@ -101,6 +102,15 @@ export const percent: List<PipeFactory> =
|
||||||
export const currency: List<PipeFactory> =
|
export const currency: List<PipeFactory> =
|
||||||
CONST_EXPR([CONST_EXPR(new CurrencyPipe()), CONST_EXPR(new NullPipeFactory())]);
|
CONST_EXPR([CONST_EXPR(new CurrencyPipe()), CONST_EXPR(new NullPipeFactory())]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date/time formatter.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
export const date: List<PipeFactory> =
|
||||||
|
CONST_EXPR([CONST_EXPR(new DatePipe()), CONST_EXPR(new NullPipeFactory())]);
|
||||||
|
|
||||||
|
|
||||||
export const defaultPipes = CONST_EXPR({
|
export const defaultPipes = CONST_EXPR({
|
||||||
"iterableDiff": iterableDiff,
|
"iterableDiff": iterableDiff,
|
||||||
"keyValDiff": keyValDiff,
|
"keyValDiff": keyValDiff,
|
||||||
|
@ -111,7 +121,8 @@ export const defaultPipes = CONST_EXPR({
|
||||||
"limitTo": limitTo,
|
"limitTo": limitTo,
|
||||||
"number": decimal,
|
"number": decimal,
|
||||||
"percent": percent,
|
"percent": percent,
|
||||||
"currency": currency
|
"currency": currency,
|
||||||
|
"date": date
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import {
|
||||||
|
isDate,
|
||||||
|
isNumber,
|
||||||
|
isPresent,
|
||||||
|
Date,
|
||||||
|
DateWrapper,
|
||||||
|
CONST,
|
||||||
|
FunctionWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {DateFormatter} from 'angular2/src/facade/intl';
|
||||||
|
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {Pipe, BasePipe, PipeFactory} from './pipe';
|
||||||
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
|
||||||
|
// TODO: move to a global configable location along with other i18n components.
|
||||||
|
var defaultLocale: string = 'en-US';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a date value to a string based on the requested format.
|
||||||
|
*
|
||||||
|
* # Usage
|
||||||
|
*
|
||||||
|
* expression | date[:format]
|
||||||
|
*
|
||||||
|
* where `expression` is a date object or a number (milliseconds since UTC epoch) and
|
||||||
|
* `format` indicates which date/time components to include:
|
||||||
|
*
|
||||||
|
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
||||||
|
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
||||||
|
* | era | G | G (AD) | GGGG (Anno Domini)| - | - |
|
||||||
|
* | year | y | - | - | y (2015) | yy (15) |
|
||||||
|
* | month | M | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
||||||
|
* | day | d | - | - | d (3) | dd (03) |
|
||||||
|
* | weekday | E | EEE (Sun) | EEEE (Sunday) | - | - |
|
||||||
|
* | hour | j | - | - | j (13) | jj (13) |
|
||||||
|
* | hour12 | h | - | - | h (1 PM) | hh (01 PM)|
|
||||||
|
* | hour24 | H | - | - | H (13) | HH (13) |
|
||||||
|
* | minute | m | - | - | m (5) | mm (05) |
|
||||||
|
* | second | s | - | - | s (9) | ss (09) |
|
||||||
|
* | timezone | z | - | z (Pacific Standard Time)| - | - |
|
||||||
|
* | timezone | Z | Z (GMT-8:00) | - | - | - |
|
||||||
|
*
|
||||||
|
* In javascript, only the components specified will be respected (not the ordering,
|
||||||
|
* punctuations, ...) and details of the the formatting will be dependent on the locale.
|
||||||
|
* On the other hand in Dart version, you can also include quoted text as well as some extra
|
||||||
|
* date/time components such as quarter. For more information see:
|
||||||
|
* https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/intl/intl.DateFormat.
|
||||||
|
*
|
||||||
|
* `format` can also be one of the following predefined formats:
|
||||||
|
*
|
||||||
|
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. Sep 3, 2010, 12:05:08 PM for en-US)
|
||||||
|
* - `'short'`: equivalent to `'yMdjm'` (e.g. 9/3/2010, 12:05 PM for en-US)
|
||||||
|
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. Friday, September 3, 2010 for en-US)
|
||||||
|
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. September 3, 2010)
|
||||||
|
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. Sep 3, 2010 for en-US)
|
||||||
|
* - `'shortDate'`: equivalent to `'yMd'` (e.g. 9/3/2010 for en-US)
|
||||||
|
* - `'mediumTime'`: equivalent to `'jms'` (e.g. 12:05:08 PM for en-US)
|
||||||
|
* - `'shortTime'`: equivalent to `'jm'` (e.g. 12:05 PM for en-US)
|
||||||
|
*
|
||||||
|
* Timezone of the formatted text will be the local system timezone of the end-users machine.
|
||||||
|
*
|
||||||
|
* # Examples
|
||||||
|
*
|
||||||
|
* Assuming `dateObj` is (year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11)
|
||||||
|
* in the _local_ time and locale is 'en-US':
|
||||||
|
*
|
||||||
|
* {{ dateObj | date }} // output is 'Jun 15, 2015'
|
||||||
|
* {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
|
||||||
|
* {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
|
||||||
|
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/pipes
|
||||||
|
*/
|
||||||
|
@CONST()
|
||||||
|
export class DatePipe extends BasePipe implements PipeFactory {
|
||||||
|
static _ALIASES = {
|
||||||
|
'medium': 'yMMMdjms',
|
||||||
|
'short': 'yMdjm',
|
||||||
|
'fullDate': 'yMMMMEEEEd',
|
||||||
|
'longDate': 'yMMMMd',
|
||||||
|
'mediumDate': 'yMMMd',
|
||||||
|
'shortDate': 'yMd',
|
||||||
|
'mediumTime': 'jms',
|
||||||
|
'shortTime': 'jm'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
transform(value, args: List<any>): string {
|
||||||
|
var pattern: string = isPresent(args) && args.length > 0 ? args[0] : 'mediumDate';
|
||||||
|
if (isNumber(value)) {
|
||||||
|
value = DateWrapper.fromMillis(value);
|
||||||
|
}
|
||||||
|
if (StringMapWrapper.contains(DatePipe._ALIASES, pattern)) {
|
||||||
|
pattern = <string>StringMapWrapper.get(DatePipe._ALIASES, pattern);
|
||||||
|
}
|
||||||
|
return DateFormatter.format(value, defaultLocale, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(obj): boolean { return isDate(obj) || isNumber(obj); }
|
||||||
|
|
||||||
|
create(cdRef: ChangeDetectorRef): Pipe { return this }
|
||||||
|
}
|
|
@ -40,3 +40,21 @@ class NumberFormatter {
|
||||||
return formatter.format(number);
|
return formatter.format(number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DateFormatter {
|
||||||
|
static RegExp _multiPartRegExp = new RegExp(r'^([yMdE]+)([Hjms]+)$');
|
||||||
|
|
||||||
|
static String format(DateTime date, String locale, String pattern) {
|
||||||
|
locale = _normalizeLocale(locale);
|
||||||
|
var formatter = new DateFormat(null, locale);
|
||||||
|
var matches = _multiPartRegExp.firstMatch(pattern);
|
||||||
|
if (matches != null) {
|
||||||
|
// Support for patterns which have known date and time components.
|
||||||
|
formatter.addPattern(matches[1]);
|
||||||
|
formatter.addPattern(matches[2], ', ');
|
||||||
|
} else {
|
||||||
|
formatter.addPattern(pattern);
|
||||||
|
}
|
||||||
|
return formatter.format(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,9 +14,7 @@ declare module Intl {
|
||||||
format(value: number): string;
|
format(value: number): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
var NumberFormat: {
|
var NumberFormat: { new (locale?: string, options?: NumberFormatOptions): NumberFormat; }
|
||||||
new (locale?: string, options?: NumberFormatOptions): NumberFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DateTimeFormatOptions {
|
interface DateTimeFormatOptions {
|
||||||
localeMatcher?: string;
|
localeMatcher?: string;
|
||||||
|
@ -37,9 +35,7 @@ declare module Intl {
|
||||||
format(date?: Date | number): string;
|
format(date?: Date | number): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
var DateTimeFormat: {
|
var DateTimeFormat: { new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat; }
|
||||||
new (locale?: string, options?: DateTimeFormatOptions): DateTimeFormat;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NumberFormatStyle {
|
export enum NumberFormatStyle {
|
||||||
|
@ -71,3 +67,78 @@ export class NumberFormatter {
|
||||||
return new Intl.NumberFormat(locale, intlOptions).format(number);
|
return new Intl.NumberFormat(locale, intlOptions).format(number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function digitCondition(len: int): string {
|
||||||
|
return len == 2 ? '2-digit' : 'numeric';
|
||||||
|
}
|
||||||
|
function nameCondition(len: int): string {
|
||||||
|
return len < 4 ? 'short' : 'long';
|
||||||
|
}
|
||||||
|
function extractComponents(pattern: string): Intl.DateTimeFormatOptions {
|
||||||
|
var ret: Intl.DateTimeFormatOptions = {};
|
||||||
|
var i = 0, j;
|
||||||
|
while (i < pattern.length) {
|
||||||
|
j = i;
|
||||||
|
while (j < pattern.length && pattern[j] == pattern[i]) j++;
|
||||||
|
let len = j - i;
|
||||||
|
switch (pattern[i]) {
|
||||||
|
case 'G':
|
||||||
|
ret.era = nameCondition(len);
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
ret.year = digitCondition(len);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
if (len >= 3)
|
||||||
|
ret.month = nameCondition(len);
|
||||||
|
else
|
||||||
|
ret.month = digitCondition(len);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
ret.day = digitCondition(len);
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
ret.weekday = nameCondition(len);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
ret.hour = digitCondition(len);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
ret.hour = digitCondition(len);
|
||||||
|
ret.hour12 = true;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
ret.hour = digitCondition(len);
|
||||||
|
ret.hour12 = false;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
ret.minute = digitCondition(len);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
ret.second = digitCondition(len);
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
ret.timeZoneName = 'long';
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
ret.timeZoneName = 'short';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dateFormatterCache: Map<string, Intl.DateTimeFormat> = new Map<string, Intl.DateTimeFormat>();
|
||||||
|
|
||||||
|
export class DateFormatter {
|
||||||
|
static format(date: Date, locale: string, pattern: string): string {
|
||||||
|
var key = locale + pattern;
|
||||||
|
if (dateFormatterCache.has(key)) {
|
||||||
|
return dateFormatterCache.get(key).format(date);
|
||||||
|
}
|
||||||
|
var formatter = new Intl.DateTimeFormat(locale, extractComponents(pattern));
|
||||||
|
dateFormatterCache.set(key, formatter);
|
||||||
|
return formatter.format(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ bool isStringMap(obj) => obj is Map;
|
||||||
bool isArray(obj) => obj is List;
|
bool isArray(obj) => obj is List;
|
||||||
bool isPromise(obj) => obj is Future;
|
bool isPromise(obj) => obj is Future;
|
||||||
bool isNumber(obj) => obj is num;
|
bool isNumber(obj) => obj is num;
|
||||||
|
bool isDate(obj) => obj is DateTime;
|
||||||
|
|
||||||
String stringify(obj) => obj.toString();
|
String stringify(obj) => obj.toString();
|
||||||
|
|
||||||
|
@ -232,8 +233,12 @@ class Json {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DateWrapper {
|
class DateWrapper {
|
||||||
|
static DateTime create(int year, [int month = 1, int day = 1, int hour = 0,
|
||||||
|
int minutes = 0, int seconds = 0, int milliseconds = 0]) {
|
||||||
|
return new DateTime(year, month, day, hour, minutes, seconds, milliseconds);
|
||||||
|
}
|
||||||
static DateTime fromMillis(int ms) {
|
static DateTime fromMillis(int ms) {
|
||||||
return new DateTime.fromMillisecondsSinceEpoch(ms);
|
return new DateTime.fromMillisecondsSinceEpoch(ms, isUtc: true);
|
||||||
}
|
}
|
||||||
static int toMillis(DateTime date) {
|
static int toMillis(DateTime date) {
|
||||||
return date.millisecondsSinceEpoch;
|
return date.millisecondsSinceEpoch;
|
||||||
|
@ -241,7 +246,7 @@ class DateWrapper {
|
||||||
static DateTime now() {
|
static DateTime now() {
|
||||||
return new DateTime.now();
|
return new DateTime.now();
|
||||||
}
|
}
|
||||||
static toJson(DateTime date) {
|
static String toJson(DateTime date) {
|
||||||
return date.toUtc().toIso8601String();
|
return date.toUtc().toIso8601String();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,10 @@ export function isNumber(obj): boolean {
|
||||||
return typeof obj === 'number';
|
return typeof obj === 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDate(obj): boolean {
|
||||||
|
return obj instanceof Date && !isNaN(obj.valueOf());
|
||||||
|
}
|
||||||
|
|
||||||
export function stringify(token): string {
|
export function stringify(token): string {
|
||||||
if (typeof token === 'string') {
|
if (typeof token === 'string') {
|
||||||
return token;
|
return token;
|
||||||
|
@ -282,8 +286,12 @@ export class Json {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DateWrapper {
|
export class DateWrapper {
|
||||||
static fromMillis(ms): Date { return new Date(ms); }
|
static create(year: int, month: int = 1, day: int = 1, hour: int = 0, minutes: int = 0,
|
||||||
static toMillis(date: Date): number { return date.getTime(); }
|
seconds: int = 0, milliseconds: int = 0): Date {
|
||||||
|
return new Date(year, month - 1, day, hour, minutes, seconds, milliseconds);
|
||||||
|
}
|
||||||
|
static fromMillis(ms: int): Date { return new Date(ms); }
|
||||||
|
static toMillis(date: Date): int { return date.getTime(); }
|
||||||
static now(): Date { return new Date(); }
|
static now(): Date { return new Date(); }
|
||||||
static toJson(date): string { return date.toJSON(); }
|
static toJson(date: Date): string { return date.toJSON(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {DatePipe} from 'angular2/src/change_detection/pipes/date_pipe';
|
||||||
|
import {DateWrapper} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("DatePipe", () => {
|
||||||
|
var date;
|
||||||
|
var pipe;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
date = DateWrapper.create(2015, 6, 15, 21, 43, 11);
|
||||||
|
pipe = new DatePipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("supports", () => {
|
||||||
|
it("should support date", () => { expect(pipe.supports(date)).toBe(true); });
|
||||||
|
it("should support int", () => { expect(pipe.supports(123456789)).toBe(true); });
|
||||||
|
|
||||||
|
it("should not support other objects", () => {
|
||||||
|
expect(pipe.supports(new Object())).toBe(false);
|
||||||
|
expect(pipe.supports(null)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("transform", () => {
|
||||||
|
it('should format each component correctly', () => {
|
||||||
|
expect(pipe.transform(date, ['y'])).toEqual('2015');
|
||||||
|
expect(pipe.transform(date, ['yy'])).toEqual('15');
|
||||||
|
expect(pipe.transform(date, ['M'])).toEqual('6');
|
||||||
|
expect(pipe.transform(date, ['MM'])).toEqual('06');
|
||||||
|
expect(pipe.transform(date, ['MMM'])).toEqual('Jun');
|
||||||
|
expect(pipe.transform(date, ['MMMM'])).toEqual('June');
|
||||||
|
expect(pipe.transform(date, ['d'])).toEqual('15');
|
||||||
|
expect(pipe.transform(date, ['E'])).toEqual('Mon');
|
||||||
|
expect(pipe.transform(date, ['EEEE'])).toEqual('Monday');
|
||||||
|
expect(pipe.transform(date, ['H'])).toEqual('21');
|
||||||
|
expect(pipe.transform(date, ['j'])).toEqual('9 PM');
|
||||||
|
expect(pipe.transform(date, ['m'])).toEqual('43');
|
||||||
|
expect(pipe.transform(date, ['s'])).toEqual('11');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format common multi component patterns', () => {
|
||||||
|
expect(pipe.transform(date, ['yMEd'])).toEqual('Mon, 6/15/2015');
|
||||||
|
expect(pipe.transform(date, ['MEd'])).toEqual('Mon, 6/15');
|
||||||
|
expect(pipe.transform(date, ['MMMd'])).toEqual('Jun 15');
|
||||||
|
expect(pipe.transform(date, ['yMMMMEEEEd'])).toEqual('Monday, June 15, 2015');
|
||||||
|
expect(pipe.transform(date, ['jms'])).toEqual('9:43:11 PM');
|
||||||
|
expect(pipe.transform(date, ['ms'])).toEqual('43:11');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format with pattern aliases', () => {
|
||||||
|
expect(pipe.transform(date, ['medium'])).toEqual('Jun 15, 2015, 9:43:11 PM');
|
||||||
|
expect(pipe.transform(date, ['short'])).toEqual('6/15/2015, 9:43 PM');
|
||||||
|
expect(pipe.transform(date, ['fullDate'])).toEqual('Monday, June 15, 2015');
|
||||||
|
expect(pipe.transform(date, ['longDate'])).toEqual('June 15, 2015');
|
||||||
|
expect(pipe.transform(date, ['mediumDate'])).toEqual('Jun 15, 2015');
|
||||||
|
expect(pipe.transform(date, ['shortDate'])).toEqual('6/15/2015');
|
||||||
|
expect(pipe.transform(date, ['mediumTime'])).toEqual('9:43:11 PM');
|
||||||
|
expect(pipe.transform(date, ['shortTime'])).toEqual('9:43 PM');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue