Skip to content

Commit

Permalink
Date: Olson-timezone-support (real 'z', 'v', 'V')
Browse files Browse the repository at this point in the history
  • Loading branch information
rajavelmani committed Mar 1, 2017
1 parent 26f6b01 commit 3c8928c
Show file tree
Hide file tree
Showing 9 changed files with 781 additions and 35 deletions.
8 changes: 4 additions & 4 deletions doc/api/date/date-formatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ are: `full`, `long`, `medium`, and `short`.
| `{ date: "full" }` | `"Monday, November 1, 2010"` |
| `{ time: "short" }` | `"5:55 PM"` |
| `{ time: "medium" }` | `"5:55:00 PM"` |
| `{ time: "long" }` | `"5:55:00 PM GMT-2"` |
| `{ time: "full" }` | `"5:55:00 PM GMT-02:00"` |
| `{ time: "long" }` | `"5:55:00 PM PST"` |
| `{ time: "full" }` | `"5:55:00 PM Pacific Standard Time"` |
| `{ datetime: "short" }` | `"11/1/10, 5:55 PM"` |
| `{ datetime: "medium" }` | `"Nov 1, 2010, 5:55:00 PM"` |
| `{ datetime: "long" }` | `"November 1, 2010 at 5:55:00 PM GMT-2"` |
| `{ datetime: "full" }` | `"Monday, November 1, 2010 at 5:55:00 PM GMT-02:00"` |
| `{ datetime: "long" }` | `"November 1, 2010 at 5:55:00 PM PST"` |
| `{ datetime: "full" }` | `"Monday, November 1, 2010 at 5:55:00 PM Pacific Standard Time"` |

For comparison, follow the same formatter `{ datetime: "short" }` on different locales.

Expand Down
4 changes: 2 additions & 2 deletions src/date.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function validateRequiredCldr( path, value ) {
*/
Globalize.dateFormatter =
Globalize.prototype.dateFormatter = function( options ) {
var args, cldr, numberFormatters, pad, pattern, properties, returnFn;
var args, cldr, numberFormatters, pad, pattern, properties, returnFn, timeZone;

validateParameterTypePlainObject( options, "options" );

Expand All @@ -64,7 +64,7 @@ Globalize.prototype.dateFormatter = function( options ) {

cldr.on( "get", validateRequiredCldr );
pattern = dateExpandPattern( options, cldr );
properties = dateFormatProperties( pattern, cldr );
properties = dateFormatProperties( pattern, cldr, timeZone );
cldr.off( "get", validateRequiredCldr );

// Create needed number formatters.
Expand Down
148 changes: 137 additions & 11 deletions src/date/format-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ define([
"./pattern-re",
"../common/create-error/unsupported-feature",
"../number/symbol",
"../util/string/pad"
"../util/string/pad",
"../common/format-message"
], function( dateFirstDayOfWeek, datePatternRe, createErrorUnsupportedFeature, numberSymbol,
stringPad ) {
stringPad, formatMessage ) {

/**
* properties( pattern, cldr )
Expand All @@ -19,13 +20,28 @@ define([
*
* TODO Support other calendar types.
*/
return function( pattern, cldr ) {
return function( pattern, cldr, timeZone ) {
var properties = {
numberFormatters: {},
pattern: pattern,
timeSeparator: numberSymbol( "timeSeparator", cldr )
},
widths = [ "abbreviated", "wide", "narrow" ];
widths = [ "abbreviated", "wide", "narrow" ],
metaZone,
exemplarCity;

if ( timeZone ) {

// The latest metazone data of the metazone array.
//TODO expand to support the historic metazones based on the given date.
metaZone = cldr.supplemental([
"metaZones/metazoneInfo/timezone", timeZone, 0,
"usesMetazone/_mzone"
]),
exemplarCity = cldr.main([
"dates/timeZoneNames/zone", timeZone, "exemplarCity"
]);
}

function setNumberFormatterPattern( pad ) {
properties.numberFormatters[ pad ] = stringPad( "", pad );
Expand All @@ -34,7 +50,10 @@ return function( pattern, cldr ) {
pattern.replace( datePatternRe, function( current ) {
var formatNumber,
chr = current.charAt( 0 ),
length = current.length;
length = current.length,
standardTzName,
daylightTzName,
genericTzName;

if ( chr === "j" ) {

Expand All @@ -49,6 +68,62 @@ return function( pattern, cldr ) {
length = 4;
}

// z...zzz: fallback to "O"
// zzzz: fallback to "OOOO"
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
if ( chr === "z" ) {
if ( metaZone ) {

//z...zzz: "{shortRegion}", eg. "PST" or "PDT".
//zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}",
//eg. "Pacific Standard Time" or "Pacific Daylight Time".
standardTzName = cldr.main([
"dates/timeZoneNames/metazone",
metaZone,
length < 4 ? "short" : "long",
"standard"
]);
daylightTzName = cldr.main([
"dates/timeZoneNames/metazone",
metaZone,
length < 4 ? "short" : "long",
"daylight"
]);
}

//fall through "O" format
if ( !metaZone || !standardTzName ) {
chr = "O";
if ( length < 4 ) {
length = 1;
}
}
}

// v: fallback to "VVVV"
// vvvv: fallback to "VVVV"
// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
if ( chr === "v" ) {
if ( metaZone ) {

//v...vvv: "{shortRegion}", eg. "PT".
//vvvv: "{regionName} {Time}" or "{regionName} {Time}",
//eg. "Pacific Time"
genericTzName = cldr.main([
"dates/timeZoneNames/metazone",
metaZone,
length === 1 ? "short" : "long",
"generic"
]);
}

//fall through "V" format
if ( !metaZone || !genericTzName ) {
chr = "V";
length = 4;
}
}

switch ( chr ) {

// Era
Expand Down Expand Up @@ -208,6 +283,63 @@ return function( pattern, cldr ) {

// Zone
case "z":
properties.standardTzName = standardTzName;
properties.daylightTzName = daylightTzName;
break;
case "v":
if ( length !== 1 && length !== 4 ) {
throw createErrorUnsupportedFeature({
feature: "timezone pattern `" + pattern + "`"
});
}
properties.genericTzName = genericTzName;
break;
case "V":

if ( length === 1 ) {
throw createErrorUnsupportedFeature({
feature: "timezone pattern `" + pattern + "`"
});
}

//fall through "{0} {Time}""
//for VVVV format
if ( timeZone ) {
var TzName;

if ( length === 2 ) {
TzName = timeZone;
}

if ( exemplarCity ) {
if ( length === 3 ) {
TzName = exemplarCity;
} else if ( length === 4 ) {
TzName = formatMessage(
cldr.main(
"dates/timeZoneNames/regionFormat"
),
[ exemplarCity ]
);
}
} else if ( !exemplarCity && length === 4 ) {
var unKnownExemplarCity = cldr.main([
"dates/timeZoneNames/zone/Etc/Unknown/exemplarCity"
]);
TzName = formatMessage(
cldr.main(
"dates/timeZoneNames/regionFormat"
),
[ unKnownExemplarCity ]
);
}
if ( TzName ) {
properties.TzName = TzName;
break;
}
}

/* falls through */
case "O":

// O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
Expand All @@ -223,12 +355,6 @@ return function( pattern, cldr ) {
setNumberFormatterPattern( 1 );
setNumberFormatterPattern( 2 );
break;

case "v":
case "V":
throw createErrorUnsupportedFeature({
feature: "timezone pattern `" + chr + "`"
});
}

if ( formatNumber ) {
Expand Down
33 changes: 33 additions & 0 deletions src/date/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,39 @@ return function( date, numberFormatters, properties ) {

// Zone
case "z":

// z...zzz: "{shortRegion}", eg. "PST" or "PDT".
// zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}",
// eg. "Pacific Standard Time" or "Pacific Daylight Time".
// TODO: date.isDST() FIXME
if ( date.isDST && properties.standardTzName ) {
ret = date.isDST() && properties.daylightTzName ?
properties.daylightTzName : properties.standardTzName;
break;
}

/* falls through */
case "v":

//v...vvv: "{shortRegion}", eg. "PT".
//vvvv: "{regionName} {Time}",
//eg. "Pacific Time"
if ( properties.genericTzName ) {
ret = properties.genericTzName;
break;
}

/* falls through */
case "V":

//VVVV: "{explarCity} {Time}",
//eg. "Los Angeles Time"
if ( properties.TzName ) {
ret = properties.TzName;
break;
}

/* falls through */
case "O":

// O: "{gmtFormat}+H;{gmtFormat}-H" or "{gmtZeroFormat}", eg. "GMT-8" or "GMT".
Expand Down
120 changes: 120 additions & 0 deletions src/magicDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@

define( function() {
var MagicDate = function( date, timeZonedata ) {
this.date = new Date( date.getTime() );
this.timeZoneData = timeZonedata;
this.date = new Date( this.date.getTime() - this.getDaylightAdjustment() * 60 * 1000 );
};

MagicDate.prototype.getFullYear = function() {
return this.date.getUTCFullYear();
};

MagicDate.prototype.getMonth = function() {
return this.date.getUTCMonth();
};

MagicDate.prototype.getDate = function() {
return this.date.getUTCDate();
};

MagicDate.prototype.getMinutes = function() {
return this.date.getUTCMinutes();
};

MagicDate.prototype.getSeconds = function() {
return this.date.getUTCSeconds();
};

MagicDate.prototype.getHours = function() {
return this.date.getUTCHours();
};

MagicDate.prototype.getMinutes = function() {
return this.date.getUTCMinutes();
};

MagicDate.prototype.getSeconds = function() {
return this.date.getUTCSeconds();
};

MagicDate.prototype.getMilliseconds = function() {
return this.date.getUTCMilliseconds();
};

MagicDate.prototype.getTime = function() {
return this.date.getTime() + this.getDaylightAdjustment() * 60 * 60 * 1000;
};

MagicDate.prototype.setFullYear = function( year ) {
return this.date.setUTCFullYear( year );
};

MagicDate.prototype.setMonth = function( month ) {
return this.date.setUTCMonth( month );
};

MagicDate.prototype.setDate = function( date ) {
return this.date.setUTCDate( date );
};

MagicDate.prototype.setMinutes = function( minutes ) {
return this.date.setUTCMinutes( minutes );
};

MagicDate.prototype.setSeconds = function( seconds ) {
return this.date.setUTCSeconds( seconds );
};

MagicDate.prototype.setHours = function( hour ) {
return this.date.setUTCHours( hour );
};

MagicDate.prototype.setMinutes = function( minutes ) {
return this.date.setUTCMinutes( minutes );
};

MagicDate.prototype.setSeconds = function( seconds ) {
return this.date.setUTCSeconds( seconds );
};

MagicDate.prototype.setMilliseconds = function( milliseconds ) {
return this.date.setUTCMilliseconds( milliseconds );
};

MagicDate.prototype.setTime = function( time ) {
return this.date.setTime( time );
};

MagicDate.prototype.isDST = function() {
return this.getDaylightAdjustment() > 0;
};

MagicDate.prototype.getStdOffset = function() {
var stdOffset = -1;
if ( this.timeZoneData.offsets > 1 ) {
stdOffset *= Math.max(
this.timeZoneData.offsets[ this.timeZoneData.offsets.length - 1 ],
this.timeZoneData.offsets[ this.timeZoneData.offsets.length - 2 ]
);
} else {
stdOffset *= this.timeZoneData.offsets[ this.timeZoneData.offsets.length - 1 ];
}
return stdOffset;
};

MagicDate.prototype.getOffset = function() {
return this.getStdOffset() - this.getDaylightAdjustment();
};

MagicDate.prototype.getDaylightAdjustment = function() {
var index = 0;
while ( index < this.timeZoneData.untils.length - 1 &&
this.date.getTime() >= this.timeZoneData.untils[ index ] ) {
index++;
}
return index === 0 ? 0 : this.timeZoneData.offsets[ index ];
};

return MagicDate;
});
1 change: 1 addition & 0 deletions test/unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require([
"./unit/date/format-properties",
"./unit/date/parse-properties",
"./unit/date/tokenizer-properties",
"./unit/date/magicDate",

"./unit/date/format",
"./unit/date/tokenizer",
Expand Down
Loading

0 comments on commit 3c8928c

Please sign in to comment.