diff --git a/src/date.js b/src/date.js index ddc81127d..271739e05 100644 --- a/src/date.js +++ b/src/date.js @@ -39,6 +39,7 @@ function validateRequiredCldr( path, value ) { skip: [ /dates\/calendars\/gregorian\/dateTimeFormats\/availableFormats/, /dates\/calendars\/gregorian\/days\/.*\/short/, + /dates\/timeZoneNames\/zone/, /dates\/timeZoneNames\/metazone/, /globalize-iana/, /supplemental\/metaZones/, diff --git a/src/date/format-properties.js b/src/date/format-properties.js index db25126fb..efe79f569 100644 --- a/src/date/format-properties.js +++ b/src/date/format-properties.js @@ -28,6 +28,42 @@ return function( pattern, cldr, timeZone ) { }, widths = [ "abbreviated", "wide", "narrow" ]; + function getTimeZoneName( length, type ) { + var metaZone, result; + + result = cldr.main([ + "dates/timeZoneNames/zone", + timeZone, + length < 4 ? "short" : "long", + type + ]); + + if ( result ) { + return result; + } + + // 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" + ]); + + // return cldr.main([ + result = cldr.main([ + "dates/timeZoneNames/metazone", + metaZone, + length < 4 ? "short" : "long", + type + ]); + + return result; + } + + function setNumberFormatterPattern( pad ) { + properties.numberFormatters[ pad ] = stringPad( "", pad ); + } + if ( timeZone ) { properties.timeZoneData = { offsets: cldr.get([ "globalize-iana/zoneData", timeZone, "offsets" ]), @@ -35,29 +71,14 @@ return function( pattern, cldr, timeZone ) { }; } - function setNumberFormatterPattern( pad ) { - properties.numberFormatters[ pad ] = stringPad( "", pad ); - } - pattern.replace( datePatternRe, function( current ) { var formatNumber, chr = current.charAt( 0 ), length = current.length, - metaZone, standardTzName, daylightTzName, genericTzName; - if ( timeZone && ( chr === "v" || chr === "z" )) { - - // 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" - ]); - } - if ( chr === "j" ) { // Locale preferred hHKk. @@ -71,31 +92,22 @@ return function( pattern, cldr, timeZone ) { length = 4; } - // z...zzz: fallback to "O" - // zzzz: fallback to "OOOO" + // z...zzz: "{shortRegion}", eg. "PST" or "PDT". + // zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}", + // e.g., "Pacific Standard Time" or "Pacific Daylight Time". // 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" - ]); + standardTzName = getTimeZoneName( length, "standard" ); + daylightTzName = getTimeZoneName( length, "daylight" ); + if ( standardTzName ) { + properties.standardTzName = standardTzName; + } + if ( daylightTzName ) { + properties.daylightTzName = daylightTzName; } - //fall through "O" format - if ( !metaZone || !standardTzName ) { + // Fall through the "O" format in case one name is missing. + if ( !standardTzName || !daylightTzName ) { chr = "O"; if ( length < 4 ) { length = 1; @@ -103,25 +115,15 @@ return function( pattern, cldr, timeZone ) { } } - // v: fallback to "VVVV" - // vvvv: fallback to "VVVV" + // v...vvv: "{shortRegion}", eg. "PT". + // vvvv: "{regionName} {Time}" or "{regionName} {Time}", + // e.g., "Pacific Time" // 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" - ]); - } + genericTzName = getTimeZoneName( length, "generic" ); - //fall through "V" format - if ( !metaZone || !genericTzName ) { + // Fall back to "V" format. + if ( !genericTzName ) { chr = "V"; length = 4; } @@ -290,11 +292,6 @@ return function( pattern, cldr, timeZone ) { break; // Zone - case "z": - properties.standardTzName = standardTzName; - properties.daylightTzName = daylightTzName; - break; - case "v": if ( length !== 1 && length !== 4 ) { throw createErrorUnsupportedFeature({ @@ -355,7 +352,6 @@ return function( pattern, cldr, timeZone ) { properties.gmtZeroFormat = cldr.main( "dates/timeZoneNames/gmtZeroFormat" ); properties.tzLongHourFormat = cldr.main( "dates/timeZoneNames/hourFormat" ); - // FIXME do we need this Z? /* falls through */ case "Z": case "X": diff --git a/src/date/format.js b/src/date/format.js index 4b1668dbc..b7831e0ce 100644 --- a/src/date/format.js +++ b/src/date/format.js @@ -64,6 +64,23 @@ return function( date, numberFormatters, properties ) { } } + // z...zzz: "{shortRegion}", e.g., "PST" or "PDT". + // zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}", + // e.g., "Pacific Standard Time" or "Pacific Daylight Time". + if ( chr === "z" ) { + if ( date.isDST ) { + value = date.isDST() ? properties.daylightTzName : properties.standardTzName; + } + + // Fall back to "O" format. + if ( !value ) { + chr = "O"; + if ( length < 4 ) { + length = 1; + } + } + } + switch ( chr ) { // Era @@ -216,22 +233,13 @@ return function( date, numberFormatters, properties ) { // Zone case "z": + break; - // z...zzz: "{shortRegion}", eg. "PST" or "PDT". - // zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}", - // eg. "Pacific Standard Time" or "Pacific Daylight Time". - if ( date.isDST && properties.standardTzName ) { - value = date.isDST() && properties.daylightTzName ? - properties.daylightTzName : properties.standardTzName; - break; - } - - /* falls through */ case "v": - //v...vvv: "{shortRegion}", eg. "PT". - //vvvv: "{regionName} {Time}", - //eg. "Pacific Time" + // v...vvv: "{shortRegion}", eg. "PT". + // vvvv: "{regionName} {Time}", + // e.g., "Pacific Time". if ( properties.genericTzName ) { value = properties.genericTzName; break; @@ -240,8 +248,7 @@ return function( date, numberFormatters, properties ) { /* falls through */ case "V": - //VVVV: "{explarCity} {Time}", - //eg. "Los Angeles Time" + //VVVV: "{explarCity} {Time}", e.g., "Los Angeles Time" if ( properties.timeZoneName ) { value = properties.timeZoneName; break; diff --git a/test/unit/date/format-properties.js b/test/unit/date/format-properties.js index fc00bf2c3..e44b2c28f 100644 --- a/test/unit/date/format-properties.js +++ b/test/unit/date/format-properties.js @@ -2,6 +2,9 @@ define([ "cldr", "src/date/format-properties", "json!cldr-data/main/en/ca-gregorian.json", + "json!cldr-data/main/en/timeZoneNames.json", + "json!cldr-data/main/en-GB/ca-gregorian.json", + "json!cldr-data/main/en-GB/timeZoneNames.json", "json!cldr-data/supplemental/likelySubtags.json", "json!cldr-data/supplemental/timeData.json", "json!cldr-data/supplemental/weekData.json", @@ -9,12 +12,16 @@ define([ "cldr/event", "cldr/supplemental" -], function( Cldr, properties, enCaGregorian, likelySubtags, timeData, weekData, metaZones ) { +], function( Cldr, properties, enCaGregorian, enTimeZoneNames, enGbCaGregorian, enGbTimeZoneNames, + likelySubtags, timeData, weekData, metaZones ) { var cldr; Cldr.load( enCaGregorian, + enTimeZoneNames, + enGbCaGregorian, + enGbTimeZoneNames, likelySubtags, timeData, weekData, @@ -126,20 +133,42 @@ QUnit.test( "should return dayPeriods property for period (a)", function( assert QUnit.test( "should return standardTzName and daylightTzName properties for zone (z|zz|zzz|zzzz|zzzzz)", function( assert ) { - var timeZone = "America/Los_Angeles"; + var timeZone, + enGb = new Cldr( "en-GB" ); + + timeZone = "America/Los_Angeles"; [ "z", "zz", "zzz" ].forEach(function( pattern ) { assert.equal( properties( pattern, cldr, timeZone ).standardTzName, "PST" ); assert.equal( properties( pattern, cldr, timeZone ).daylightTzName, "PDT" ); }); - [ "zzzz", "zzzzz" ].forEach(function( pattern ) { assert.equal( properties( pattern, cldr, timeZone ).standardTzName, "Pacific Standard Time" ); assert.equal( properties( pattern, cldr, timeZone ).daylightTzName, "Pacific Daylight Time" ); }); + + // Test for two things: + // - daylightTzName using the zone data (primary), not the metazone (secondary try); + // - standardTzName being undefined, therefore requiring the O fallback properties; + timeZone = "Europe/London"; + [ "z", "zz", "zzz" ].forEach(function( pattern ) { + var formatProperties = properties( pattern, enGb, timeZone ); + assert.ok( !( "standardTzName" in formatProperties ) ); + assert.equal( formatProperties.daylightTzName, "BST" ); + assert.ok( "gmtFormat" in formatProperties ); + assert.ok( "gmtZeroFormat" in formatProperties ); + assert.ok( "tzLongHourFormat" in formatProperties ); + }); + [ "zzzz", "zzzzz" ].forEach(function( pattern ) { + var formatProperties = properties( pattern, enGb, timeZone ); + assert.ok( !( "standardTzName" in formatProperties ) ); + assert.equal( formatProperties.daylightTzName, "British Summer Time" ); + assert.ok( "gmtFormat" in formatProperties ); + assert.ok( "gmtZeroFormat" in formatProperties ); + assert.ok( "tzLongHourFormat" in formatProperties ); + }); }); -QUnit.test( "should return standardTzName and daylightTzName properties for zone (v|vvvv)", - function( assert ) { +QUnit.test( "should return genericTzName property for zone (v|vvvv)", function( assert ) { var pattern, timeZone = "America/Los_Angeles"; @@ -150,8 +179,7 @@ QUnit.test( "should return standardTzName and daylightTzName properties for zone assert.equal( properties( pattern, cldr, timeZone ).genericTzName, "Pacific Time" ); }); -QUnit.test( "should return standardTzName and daylightTzName properties for zone (VV|VVV|VVVV)", - function( assert ) { +QUnit.test( "should return timeZoneName properties for zone (VV|VVV|VVVV)", function( assert ) { var pattern, timeZone = "America/Los_Angeles"; diff --git a/test/unit/date/format.js b/test/unit/date/format.js index 48e40a3fe..687edf2f2 100644 --- a/test/unit/date/format.js +++ b/test/unit/date/format.js @@ -7,6 +7,7 @@ define([ "json!cldr-data/main/en/ca-gregorian.json", "json!cldr-data/main/en/timeZoneNames.json", "json!cldr-data/main/en-GB/ca-gregorian.json", + "json!cldr-data/main/en-GB/timeZoneNames.json", "json!cldr-data/main/en-IN/ca-gregorian.json", "json!cldr-data/main/pt/ca-gregorian.json", "json!cldr-data/main/ru/ca-gregorian.json", @@ -19,8 +20,8 @@ define([ "cldr/event", "cldr/supplemental" ], function( Cldr, format, formatProperties, stringPad, deCaGregorian, enCaGregorian, - enTimeZoneNames, enGbCaGregorian, enInCaGregorian, ptCaGregorian, ruCaGregorian, likelySubtags, - timeData, weekData, metaZones, ianaTimezoneData ) { + enTimeZoneNames, enGbCaGregorian, enGbTimeZoneNames, enInCaGregorian, ptCaGregorian, + ruCaGregorian, likelySubtags, timeData, weekData, metaZones, ianaTimezoneData ) { var cldr, year0 = new Date( -62167190400000 ), @@ -48,6 +49,7 @@ Cldr.load( deCaGregorian, enCaGregorian, enGbCaGregorian, + enGbTimeZoneNames, enInCaGregorian, enTimeZoneNames, likelySubtags, @@ -60,12 +62,6 @@ Cldr.load( cldr = new Cldr( "en" ); -// test exemplerCity when metaZones not found for a given timeZone -// zzzz: "{regionName} {Standard Time}" or "{regionName} {Daylight Time}" -// z...zzz: fall back to "O" format -// vvvv: "{regionName} {Time}" -// v...vvv: fall back to "O" format - Cldr.load({ "main": { "en": { @@ -1026,10 +1022,12 @@ QUnit.test( "should format various milliseconds (A+)", function( assert ) { * Zone */ QUnit.test( "should format timezone (z)", function( assert ) { + var date, + enGb = new Cldr( "en-GB" ); // Test for country with Daylight Savings and Standard time, e.g., Pacific Standard Time and // Pacific Daylight Time. - var date = new Date( 2017, 0, 1 ); + date = new Date( 2017, 0, 1 ); assert.dateFormatWithTimezone( date, "z", "America/Los_Angeles", cldr, [{ type: "zone", value: "PST" @@ -1065,6 +1063,45 @@ QUnit.test( "should format timezone (z)", function( assert ) { value: "Pacific Daylight Time" }]); + // Test for two things: + // - daylightTzName using the zone data (primary), not the metazone (secondary try); + // - standardTzName being undefined, therefore requiring the O fallback properties; + date = new Date( "2015-06-01T12:32:46" ); + assert.dateFormatWithTimezone( date, "z", "Europe/London", enGb, [{ + type: "zone", + value: "BST" + }]); + assert.dateFormatWithTimezone( date, "zz", "Europe/London", enGb, [{ + type: "zone", + value: "BST" + }]); + assert.dateFormatWithTimezone( date, "zzz", "Europe/London", enGb, [{ + type: "zone", + value: "BST" + }]); + assert.dateFormatWithTimezone( date, "zzzz", "Europe/London", enGb, [{ + type: "zone", + value: "British Summer Time" + }]); + + date = new Date( "2015-01-01T12:32:46" ); + assert.dateFormatWithTimezone( date, "z", "Europe/London", enGb, [{ + type: "zone", + value: "GMT" + }]); + assert.dateFormatWithTimezone( date, "zz", "Europe/London", enGb, [{ + type: "zone", + value: "GMT" + }]); + assert.dateFormatWithTimezone( date, "zzz", "Europe/London", enGb, [{ + type: "zone", + value: "GMT" + }]); + assert.dateFormatWithTimezone( date, "zzzz", "Europe/London", enGb, [{ + type: "zone", + value: "GMT" + }]); + // Test for country with only standard time, e.g., long: Indian Standard Time. // This test also covers the case where timezone name is undefined like short timezone name for // Asia/Calcutta and should fall through 'O' format.