source: [view] 
                    
                    define("dojo/date/locale", ["dojo", "dojo/date", "dojo/cldr/supplemental", "dojo/regexp", "dojo/string", "dojo/i18n", "i18n!dojo/cldr/nls/gregorian"], function(dojo) {
dojo.getObject("date.locale", true, dojo);
// Localization methods for Date.   Honor local customs using locale-dependent dojo.cldr data.
// Load the bundles containing localization information for
// names and formats
//NOTE: Everything in this module assumes Gregorian calendars.
// Other calendars will be implemented in separate modules.
(function(){
 // Format a pattern without literals
 function formatPattern(dateObject, bundle, options, pattern){
  return pattern.replace(/([a-z])\1*/ig, function(match){
   var s, pad,
    c = match.charAt(0),
    l = match.length,
    widthList = ["abbr", "wide", "narrow"];
   switch(c){
    case 'G':
     s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
     break;
    case 'y':
     s = dateObject.getFullYear();
     switch(l){
      case 1:
       break;
      case 2:
       if(!options.fullYear){
        s = String(s); s = s.substr(s.length - 2);
        break;
       }
       // fallthrough
      default:
       pad = true;
     }
     break;
    case 'Q':
    case 'q':
     s = Math.ceil((dateObject.getMonth()+1)/3);
//     switch(l){
//      case 1: case 2:
       pad = true;
//       break;
//      case 3: case 4: // unimplemented
//     }
     break;
    case 'M':
     var m = dateObject.getMonth();
     if(l<3){
      s = m+1; pad = true;
     }else{
      var propM = ["months", "format", widthList[l-3]].join("-");
      s = bundle[propM][m];
     }
     break;
    case 'w':
     var firstDay = 0;
     s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
     break;
    case 'd':
     s = dateObject.getDate(); pad = true;
     break;
    case 'D':
     s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
     break;
    case 'E':
     var d = dateObject.getDay();
     if(l<3){
      s = d+1; pad = true;
     }else{
      var propD = ["days", "format", widthList[l-3]].join("-");
      s = bundle[propD][d];
     }
     break;
    case 'a':
     var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
     s = options[timePeriod] || bundle['dayPeriods-format-wide-' + timePeriod];
     break;
    case 'h':
    case 'H':
    case 'K':
    case 'k':
     var h = dateObject.getHours();
     // strange choices in the date format make it impossible to write this succinctly
     switch (c){
      case 'h': // 1-12
       s = (h % 12) || 12;
       break;
      case 'H': // 0-23
       s = h;
       break;
      case 'K': // 0-11
       s = (h % 12);
       break;
      case 'k': // 1-24
       s = h || 24;
       break;
     }
     pad = true;
     break;
    case 'm':
     s = dateObject.getMinutes(); pad = true;
     break;
    case 's':
     s = dateObject.getSeconds(); pad = true;
     break;
    case 'S':
     s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
     break;
    case 'v': // FIXME: don't know what this is. seems to be same as z?
    case 'z':
     // We only have one timezone to offer; the one from the browser
     s = dojo.date.locale._getZone(dateObject, true, options);
     if(s){break;}
     l=4;
     // fallthrough... use GMT if tz not available
    case 'Z':
     var offset = dojo.date.locale._getZone(dateObject, false, options);
     var tz = [
      (offset<=0 ? "+" : "-"),
      dojo.string.pad(Math.floor(Math.abs(offset)/60), 2),
      dojo.string.pad(Math.abs(offset)% 60, 2)
     ];
     if(l==4){
      tz.splice(0, 0, "GMT");
      tz.splice(3, 0, ":");
     }
     s = tz.join("");
     break;
//    case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e':
//     console.log(match+" modifier unimplemented");
    default:
     throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
   }
   if(pad){ s = dojo.string.pad(s, l); }
   return s;
  });
 }
       
 dojo.date.locale.__FormatOptions = function(){
 // selector: String
 //  choice of 'time','date' (default: date and time)
 // formatLength: String
 //  choice of long, short, medium or full (plus any custom additions).  Defaults to 'short'
 // datePattern:String
 //  override pattern with this string
 // timePattern:String
 //  override pattern with this string
 // am: String
 //  override strings for am in times
 // pm: String
 //  override strings for pm in times
 // locale: String
 //  override the locale used to determine formatting rules
 // fullYear: Boolean
 //  (format only) use 4 digit years whenever 2 digit years are called for
 // strict: Boolean
 //  (parse only) strict parsing, off by default
  this.selector = selector;
  this.formatLength = formatLength;
  this.datePattern = datePattern;
  this.timePattern = timePattern;
  this.am = am;
  this.pm = pm;
  this.locale = locale;
  this.fullYear = fullYear;
  this.strict = strict;
 }
       
dojo.date.locale._getZone = function(/*Date*/dateObject, /*boolean*/getName, /*dojo.date.locale.__FormatOptions?*/options){
 // summary:
 //  Returns the zone (or offset) for the given date and options.  This
 //  is broken out into a separate function so that it can be overridden
 //  by timezone-aware code.
 //
 // dateObject:
 //  the date and/or time being formatted.
 //
 // getName:
 //  Whether to return the timezone string (if true), or the offset (if false)
 //
 // options:
 //  The options being used for formatting
 if(getName){
  return dojo.date.getTimezoneName(dateObject);
 }else{
  return dateObject.getTimezoneOffset();
 }
};
dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){
 // summary:
 //  Format a Date object as a String, using locale-specific settings.
 //
 // description:
 //  Create a string from a Date object using a known localized pattern.
 //  By default, this method formats both date and time from dateObject.
 //  Formatting patterns are chosen appropriate to the locale.  Different
 //  formatting lengths may be chosen, with "full" used by default.
 //  Custom patterns may be used or registered with translations using
 //  the dojo.date.locale.addCustomFormats method.
 //  Formatting patterns are implemented using [the syntax described at
 //  unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
 //
 // dateObject:
 //  the date and/or time to be formatted.  If a time only is formatted,
 //  the values in the year, month, and day fields are irrelevant.  The
 //  opposite is true when formatting only dates.
 options = options || {};
 var locale = dojo.i18n.normalizeLocale(options.locale),
  formatLength = options.formatLength || 'short',
  bundle = dojo.date.locale._getGregorianBundle(locale),
  str = [],
  sauce = dojo.hitch(this, formatPattern, dateObject, bundle, options);
 if(options.selector == "year"){
  return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce);
 }
 var pattern;
 if(options.selector != "date"){
  pattern = options.timePattern || bundle["timeFormat-"+formatLength];
  if(pattern){str.push(_processPattern(pattern, sauce));}
 }
 if(options.selector != "time"){
  pattern = options.datePattern || bundle["dateFormat-"+formatLength];
  if(pattern){str.push(_processPattern(pattern, sauce));}
 }
 return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
  function(match, key){ return str[key]; }); // String
};
dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){
 // summary:
 //  Builds the regular needed to parse a localized date
 return dojo.date.locale._parseInfo(options).regexp; // String
};
dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){
 options = options || {};
 var locale = dojo.i18n.normalizeLocale(options.locale),
  bundle = dojo.date.locale._getGregorianBundle(locale),
  formatLength = options.formatLength || 'short',
  datePattern = options.datePattern || bundle["dateFormat-" + formatLength],
  timePattern = options.timePattern || bundle["timeFormat-" + formatLength],
  pattern;
 if(options.selector == 'date'){
  pattern = datePattern;
 }else if(options.selector == 'time'){
  pattern = timePattern;
 }else{
  pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
   function(match, key){ return [timePattern, datePattern][key]; });
 }
 var tokens = [],
  re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
 return {regexp: re, tokens: tokens, bundle: bundle};
};
dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){
 // summary:
 //  Convert a properly formatted string to a primitive Date object,
 //  using locale-specific settings.
 //
 // description:
 //  Create a Date object from a string using a known localized pattern.
 //  By default, this method parses looking for both date and time in the string.
 //  Formatting patterns are chosen appropriate to the locale.  Different
 //  formatting lengths may be chosen, with "full" used by default.
 //  Custom patterns may be used or registered with translations using
 //  the dojo.date.locale.addCustomFormats method.
 //
 //  Formatting patterns are implemented using [the syntax described at
 //  unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
 //  When two digit years are used, a century is chosen according to a sliding
 //  window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns.
 //  year < 100CE requires strict mode.
 //
 // value:
 //  A string representation of a date
 // remove non-printing bidi control chars from input and pattern
 var controlChars = /[\u200E\u200F\u202A\u202E]/g,
  info = dojo.date.locale._parseInfo(options),
  tokens = info.tokens, bundle = info.bundle,
  re = new RegExp("^" + info.regexp.replace(controlChars, "") + "$",
   info.strict ? "" : "i"),
  match = re.exec(value && value.replace(controlChars, ""));
 if(!match){ return null; } // null
 var widthList = ['abbr', 'wide', 'narrow'],
  result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end
  amPm = "",
  valid = dojo.every(match, function(v, i){
  if(!i){return true;}
  var token=tokens[i-1];
  var l=token.length;
  switch(token.charAt(0)){
   case 'y':
    if(l != 2 && options.strict){
     //interpret year literally, so '5' would be 5 A.D.
     result[0] = v;
    }else{
     if(v<100){
      v = Number(v);
      //choose century to apply, according to a sliding window
      //of 80 years before and 20 years after present year
      var year = '' + new Date().getFullYear(),
       century = year.substring(0, 2) * 100,
       cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99),
       num = (v < cutoff) ? century + v : century - 100 + v;
      result[0] = num;
     }else{
      //we expected 2 digits and got more...
      if(options.strict){
       return false;
      }
      //interpret literally, so '150' would be 150 A.D.
      //also tolerate '1950', if 'yyyy' input passed to 'yy' format
      result[0] = v;
     }
    }
    break;
   case 'M':
    if(l>2){
     var months = bundle['months-format-' + widthList[l-3]].concat();
     if(!options.strict){
      //Tolerate abbreviating period in month part
      //Case-insensitive comparison
      v = v.replace(".","").toLowerCase();
      months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
     }
     v = dojo.indexOf(months, v);
     if(v == -1){
//      console.log("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
      return false;
     }
    }else{
     v--;
    }
    result[1] = v;
    break;
   case 'E':
   case 'e':
    var days = bundle['days-format-' + widthList[l-3]].concat();
    if(!options.strict){
     //Case-insensitive comparison
     v = v.toLowerCase();
     days = dojo.map(days, function(d){return d.toLowerCase();});
    }
    v = dojo.indexOf(days, v);
    if(v == -1){
//     console.log("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
     return false;
    }
    //TODO: not sure what to actually do with this input,
    //in terms of setting something on the Date obj...?
    //without more context, can't affect the actual date
    //TODO: just validate?
    break;
   case 'D':
    result[1] = 0;
    // fallthrough...
   case 'd':
    result[2] = v;
    break;
   case 'a': //am/pm
    var am = options.am || bundle['dayPeriods-format-wide-am'],
     pm = options.pm || bundle['dayPeriods-format-wide-pm'];
    if(!options.strict){
     var period = /\./g;
     v = v.replace(period,'').toLowerCase();
     am = am.replace(period,'').toLowerCase();
     pm = pm.replace(period,'').toLowerCase();
    }
    if(options.strict && v != am && v != pm){
//     console.log("dojo.date.locale.parse: Could not parse am/pm part.");
     return false;
    }
    // we might not have seen the hours field yet, so store the state and apply hour change later
    amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
    break;
   case 'K': //hour (1-24)
    if(v == 24){ v = 0; }
    // fallthrough...
   case 'h': //hour (1-12)
   case 'H': //hour (0-23)
   case 'k': //hour (0-11)
    //TODO: strict bounds checking, padding
    if(v > 23){
//     console.log("dojo.date.locale.parse: Illegal hours value");
     return false;
    }
    //in the 12-hour case, adjusting for am/pm requires the 'a' part
    //which could come before or after the hour, so we will adjust later
    result[3] = v;
    break;
   case 'm': //minutes
    result[4] = v;
    break;
   case 's': //seconds
    result[5] = v;
    break;
   case 'S': //milliseconds
    result[6] = v;
//    break;
//   case 'w':
//TODO    var firstDay = 0;
//   default:
//TODO: throw?
//    console.log("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
  }
  return true;
 });
 var hours = +result[3];
 if(amPm === 'p' && hours < 12){
  result[3] = hours + 12; //e.g., 3pm -> 15
 }else if(amPm === 'a' && hours == 12){
  result[3] = 0; //12am -> 0
 }
 //TODO: implement a getWeekday() method in order to test
 //validity of input strings containing 'EEE' or 'EEEE'...
 var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
 if(options.strict){
  dateObject.setFullYear(result[0]);
 }
 // Check for overflow.  The Date() constructor normalizes things like April 32nd...
 //TODO: why isn't this done for times as well?
 var allTokens = tokens.join(""),
  dateToken = allTokens.indexOf('d') != -1,
  monthToken = allTokens.indexOf('M') != -1;
 if(!valid ||
  (monthToken && dateObject.getMonth() > result[1]) ||
  (dateToken && dateObject.getDate() > result[2])){
  return null;
 }
 // Check for underflow, due to DST shifts.  See #9366
 // This assumes a 1 hour dst shift correction at midnight
 // We could compare the timezone offset after the shift and add the difference instead.
 if((monthToken && dateObject.getMonth() < result[1]) ||
  (dateToken && dateObject.getDate() < result[2])){
  dateObject = dojo.date.add(dateObject, "hour", 1);
 }
 return dateObject; // Date