- add regular expression to parse date parts - add date part creator function - replace tokens in pattern to parsed parts
This commit is contained in:
parent
be48ba1b06
commit
324f0147f6
|
@ -61,17 +61,25 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should format common multi component patterns', () => {
|
||||
expect(pipe.transform(date, 'E, M/d/y')).toEqual('Mon, 6/15/2015');
|
||||
expect(pipe.transform(date, 'E, M/d')).toEqual('Mon, 6/15');
|
||||
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
|
||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
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');
|
||||
expect(pipe.transform(date, 'jm')).toEqual('9:43');
|
||||
});
|
||||
|
||||
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, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
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');
|
||||
|
|
|
@ -70,78 +70,183 @@ export class NumberFormatter {
|
|||
return new Intl.NumberFormat(locale, intlOptions).format(num);
|
||||
}
|
||||
}
|
||||
var DATE_FORMATS_SPLIT =
|
||||
/((?:[^yMLdHhmsaZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|Z|G+|w+))(.*)/;
|
||||
|
||||
function digitCondition(len: number): string {
|
||||
return len == 2 ? '2-digit' : 'numeric';
|
||||
var PATTERN_ALIASES = {
|
||||
yMMMdjms: datePartGetterFactory(combine([
|
||||
digitCondition('year', 1),
|
||||
nameCondition('month', 3),
|
||||
digitCondition('day', 1),
|
||||
digitCondition('hour', 1),
|
||||
digitCondition('minute', 1),
|
||||
digitCondition('second', 1),
|
||||
])),
|
||||
yMdjm: datePartGetterFactory(combine([
|
||||
digitCondition('year', 1),
|
||||
digitCondition('month', 1),
|
||||
digitCondition('day', 1),
|
||||
digitCondition('hour', 1),
|
||||
digitCondition('minute', 1)
|
||||
])),
|
||||
yMMMMEEEEd: datePartGetterFactory(combine([
|
||||
digitCondition('year', 1),
|
||||
nameCondition('month', 4),
|
||||
nameCondition('weekday', 4),
|
||||
digitCondition('day', 1)
|
||||
])),
|
||||
yMMMMd: datePartGetterFactory(
|
||||
combine([digitCondition('year', 1), nameCondition('month', 4), digitCondition('day', 1)])),
|
||||
yMMMd: datePartGetterFactory(
|
||||
combine([digitCondition('year', 1), nameCondition('month', 3), digitCondition('day', 1)])),
|
||||
yMd: datePartGetterFactory(
|
||||
combine([digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1)])),
|
||||
jms: datePartGetterFactory(combine(
|
||||
[digitCondition('hour', 1), digitCondition('second', 1), digitCondition('minute', 1)])),
|
||||
jm: datePartGetterFactory(combine([digitCondition('hour', 1), digitCondition('minute', 1)]))
|
||||
};
|
||||
|
||||
var DATE_FORMATS = {
|
||||
yyyy: datePartGetterFactory(digitCondition('year', 4)),
|
||||
yy: datePartGetterFactory(digitCondition('year', 2)),
|
||||
y: datePartGetterFactory(digitCondition('year', 1)),
|
||||
MMMM: datePartGetterFactory(nameCondition('month', 4)),
|
||||
MMM: datePartGetterFactory(nameCondition('month', 3)),
|
||||
MM: datePartGetterFactory(digitCondition('month', 2)),
|
||||
M: datePartGetterFactory(digitCondition('month', 1)),
|
||||
LLLL: datePartGetterFactory(nameCondition('month', 4)),
|
||||
dd: datePartGetterFactory(digitCondition('day', 2)),
|
||||
d: datePartGetterFactory(digitCondition('day', 1)),
|
||||
HH: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false))),
|
||||
H: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
|
||||
hh: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true))),
|
||||
h: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
||||
jj: datePartGetterFactory(digitCondition('hour', 2)),
|
||||
j: datePartGetterFactory(digitCondition('hour', 1)),
|
||||
mm: datePartGetterFactory(digitCondition('minute', 2)),
|
||||
m: datePartGetterFactory(digitCondition('minute', 1)),
|
||||
ss: datePartGetterFactory(digitCondition('second', 2)),
|
||||
s: datePartGetterFactory(digitCondition('second', 1)),
|
||||
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
|
||||
// we can be just safely rely on using `sss` since we currently don't support single or two digit
|
||||
// fractions
|
||||
sss: datePartGetterFactory(digitCondition('second', 3)),
|
||||
EEEE: datePartGetterFactory(nameCondition('weekday', 4)),
|
||||
EEE: datePartGetterFactory(nameCondition('weekday', 3)),
|
||||
EE: datePartGetterFactory(nameCondition('weekday', 2)),
|
||||
E: datePartGetterFactory(nameCondition('weekday', 1)),
|
||||
a: hourClockExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
||||
Z: datePartGetterFactory({timeZoneName: 'long'}),
|
||||
z: datePartGetterFactory({timeZoneName: 'short'}),
|
||||
ww: datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
|
||||
// first Thursday of the year. not support ?
|
||||
w: datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
|
||||
// of the year not support ?
|
||||
G: datePartGetterFactory(nameCondition('era', 1)),
|
||||
GG: datePartGetterFactory(nameCondition('era', 2)),
|
||||
GGG: datePartGetterFactory(nameCondition('era', 3)),
|
||||
GGGG: datePartGetterFactory(nameCondition('era', 4))
|
||||
};
|
||||
|
||||
|
||||
function hourClockExtracter(inner: (date: Date, locale: string) => string): (
|
||||
date: Date, locale: string) => string {
|
||||
return function(date: Date, locale: string): string {
|
||||
var result = inner(date, locale);
|
||||
|
||||
return result.split(' ')[1];
|
||||
};
|
||||
}
|
||||
function nameCondition(len: number): string {
|
||||
return len < 4 ? 'short' : 'long';
|
||||
|
||||
function hourExtracter(inner: (date: Date, locale: string) => string): (date: Date,
|
||||
locale: string) => string {
|
||||
return function(date: Date, locale: string): string {
|
||||
var result = inner(date, locale);
|
||||
|
||||
return result.split(' ')[0];
|
||||
};
|
||||
}
|
||||
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;
|
||||
|
||||
function hour12Modify(options: Intl.DateTimeFormatOptions,
|
||||
value: boolean): Intl.DateTimeFormatOptions {
|
||||
options.hour12 = value;
|
||||
return options;
|
||||
}
|
||||
|
||||
function digitCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
|
||||
var result = {};
|
||||
result[prop] = len == 2 ? '2-digit' : 'numeric';
|
||||
return result;
|
||||
}
|
||||
function nameCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
|
||||
var result = {};
|
||||
result[prop] = len < 4 ? 'short' : 'long';
|
||||
return result;
|
||||
}
|
||||
|
||||
function combine(options: Intl.DateTimeFormatOptions[]): Intl.DateTimeFormatOptions {
|
||||
var result = {};
|
||||
|
||||
options.forEach(option => { (<any>Object).assign(result, option); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function datePartGetterFactory(ret: Intl.DateTimeFormatOptions): (date: Date, locale: string) =>
|
||||
string {
|
||||
return function(date: Date, locale: string): string {
|
||||
return new Intl.DateTimeFormat(locale, ret).format(date);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var datePartsFormatterCache: Map<string, string[]> = new Map<string, string[]>();
|
||||
|
||||
function dateFormatter(format: string, date: Date, locale: string): string {
|
||||
var text = '';
|
||||
var match;
|
||||
var fn;
|
||||
var parts: string[] = [];
|
||||
if (PATTERN_ALIASES[format]) {
|
||||
return PATTERN_ALIASES[format](date, locale);
|
||||
}
|
||||
return ret;
|
||||
|
||||
|
||||
if (datePartsFormatterCache.has(format)) {
|
||||
parts = datePartsFormatterCache.get(format);
|
||||
} else {
|
||||
var matchs = DATE_FORMATS_SPLIT.exec(format);
|
||||
|
||||
while (format) {
|
||||
match = DATE_FORMATS_SPLIT.exec(format);
|
||||
if (match) {
|
||||
parts = concat(parts, match, 1);
|
||||
format = parts.pop();
|
||||
} else {
|
||||
parts.push(format);
|
||||
format = null;
|
||||
}
|
||||
}
|
||||
|
||||
datePartsFormatterCache.set(format, parts);
|
||||
}
|
||||
|
||||
parts.forEach(part => {
|
||||
fn = DATE_FORMATS[part];
|
||||
text += fn ? fn(date, locale) :
|
||||
part === "''" ? "'" : part.replace(/(^'|'$)/g, '').replace(/''/g, "'");
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
var dateFormatterCache: Map<string, Intl.DateTimeFormat> = new Map<string, Intl.DateTimeFormat>();
|
||||
var slice = [].slice;
|
||||
function concat(array1, array2, index): string[] {
|
||||
return array1.concat(slice.call(array2, index));
|
||||
}
|
||||
|
||||
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);
|
||||
return dateFormatter(pattern, date, locale);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue