function dateFilter($locale) {


  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
                     // 1        2       3         4          5          6          7          8  9     10      11
  function jsonStringToDate(string) {
    var match;
    if (match = string.match(R_ISO8601_STR)) {
      var date = new Date(0),
          tzHour = 0,
          tzMin  = 0,
          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
          timeSetter = match[8] ? date.setUTCHours : date.setHours;

      if (match[9]) {
        tzHour = toInt(match[9] + match[10]);
        tzMin = toInt(match[9] + match[11]);
      }
      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
      var h = toInt(match[4] || 0) - tzHour;
      var m = toInt(match[5] || 0) - tzMin;
      var s = toInt(match[6] || 0);
      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
      timeSetter.call(date, h, m, s, ms);
      return date;
    }
    return string;
  }


  return function(date, format, timezone) {
    var text = '',
        parts = [],
        fn, match;

    format = format || 'mediumDate';
    format = $locale.DATETIME_FORMATS[format] || format;
    if (isString(date)) {
      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
    }

    if (isNumber(date)) {
      date = new Date(date);
    }

    if (!isDate(date) || !isFinite(date.getTime())) {
      return date;
    }

    while (format) {
      match = DATE_FORMATS_SPLIT.exec(format);
      if (match) {
        parts = concat(parts, match, 1);
        format = parts.pop();
      } else {
        parts.push(format);
        format = null;
      }
    }

    var dateTimezoneOffset = date.getTimezoneOffset();
    if (timezone) {
      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
      date = convertTimezoneToLocal(date, timezone, true);
    }
    forEach(parts, function(value) {
      fn = DATE_FORMATS[value];
      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
    });

    return text;
  };
}
@ngdoc filter
@name date
@kind function

@description
  Formats `date` to a string based on the requested `format`.

  `format` string can be composed of the following elements:

  * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
  * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
  * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
  * `'MMMM'`: Month in year (January-December)
  * `'MMM'`: Month in year (Jan-Dec)
  * `'MM'`: Month in year, padded (01-12)
  * `'M'`: Month in year (1-12)
  * `'dd'`: Day in month, padded (01-31)
  * `'d'`: Day in month (1-31)
  * `'EEEE'`: Day in Week,(Sunday-Saturday)
  * `'EEE'`: Day in Week, (Sun-Sat)
  * `'HH'`: Hour in day, padded (00-23)
  * `'H'`: Hour in day (0-23)
  * `'hh'`: Hour in AM/PM, padded (01-12)
  * `'h'`: Hour in AM/PM, (1-12)
  * `'mm'`: Minute in hour, padded (00-59)
  * `'m'`: Minute in hour (0-59)
  * `'ss'`: Second in minute, padded (00-59)
  * `'s'`: Second in minute (0-59)
  * `'sss'`: Millisecond in second, padded (000-999)
  * `'a'`: AM/PM marker
  * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
  * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
  * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
  * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
  * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')

  `format` string can also be one of the following predefined
  {@link guide/i18n localizable formats}:

  * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
    (e.g. Sep 3, 2010 12:05:08 PM)
  * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
  * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
    (e.g. Friday, September 3, 2010)
  * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
  * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
  * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
  * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
  * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)

  `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
  `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
  (e.g. `"h 'o''clock'"`).

@param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
   number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
   shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
   specified in the string input, the time is considered to be in the local timezone.
@param {string=} format Formatting rules (see Description). If not specified,
   `mediumDate` is used.
@param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
   continental US time zone abbreviations, but for general use, use a time zone offset, for
   example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
   If not specified, the timezone of the browser will be used.
@returns {string} Formatted string or the input if input is not recognized as date/millis.

@example
   <example>
     <file name="index.html">
       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
           <span>{{1288323623006 | date:'medium'}}</span><br>
       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
     </file>
     <file name="protractor.js" type="protractor">
       it('should format date', function() {
         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
       });
     </file>
   </example>