1
0
Fork 0
arangodb/js/node/node_modules/timezone/rfc822.js

206 lines
8.0 KiB
JavaScript

// A full RFC 2822 date parser for use with **Timezone**.
//
// This function will return a date array that you can pass to the **Timezone**
// function as the date argument. This is an example of extending **Timezone**
// by building upon it, through functional composition.
//
// RFC 2822 specifies how to parse an RFC 822 date. This is an implementation of
// that specification.
//
// Beware of date strings that kind of look like RFC 822 dates. The similarity
// in appearance between RFC 822 and the `%c` date format specifier in
// `strftime` has led programmers to use the `%c` format for RFC 822. This is
// incorrect.
//
// The format is subtly different from RFC 822 for US English. RFC 822 has a
// comma after the day of week name, `%c` does not. We do not forgive this,
// because it gets worse.
//
// The differences between `%c` and RFC 822 become far more apparent when you
// change locales, replacing the English day of week and month name
// abbreviations with the abbreviations in set locale.
//
// Localization is a general problem. Developers will create a format string for
// an RFC 822 date using day of week and month names that their [date formatting
// library will then localize](//
// http://rel.me/2008/07/22/date-format-rfc82285010361123asctimeiso8601unicode35tr35-6/).
//
// While `So 01 Mär 1992 00:00:00 UTC` is not uncommon, it is still not an RFC
// 822 date. RFC 822 dates are not localized. They are always English according
// to the RFC. Therefore, our RFC 2822 date parser expects English and does not
// support localization.
//
// If enough examples of accidentally localized RFC 822 dates are encountered in
// the wild, we can change that. Let me know.
/*
var __slice = [].slice
function die () {
console.log.apply(console, __slice.call(arguments, 0));
return process.exit(1);
};
function say () { return console.log.apply(console, __slice.call(arguments, 0)) }
*/
!function (definition) {
if (typeof module == "object" && module.exports) module.exports = definition();
else if (typeof define == "function") define(definition);
else this.tz = definition();
} (function () {
// Our parser is case insensitive for the sake of forgiveness, even though the
// specification is pretty clear about case.
var DAY = "sun|mon|tue|wed|thu|fri|sat".split("|")
, MONTH = "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec".split("|")
// RFC 822 specified single character military time zone abbreviations, but
// got them wrong, reversed from the actual time zones used by NATO. Thus,
// RFC 2822 says that they can't be trusted. The only abbreviations that can
// be trusted are the synonyms for UTC, and the abbreviations for the four
// time zones of the contiguous United States. These were laid out in RFC
// 822.
//
// See [RFC 1123 5.2.14 Date and Time
// Specification](http://www.ietf.org/rfc/rfc1123.txt).
//
// Otherwise, you cannot know the time zone offset from a date string found
// in the wild, only in an RFC 822 date and only if is one of the following.
, ABBREV =
{ Z: "+0000"
, UT: "+0000"
, GMT: "+0000"
, UTC: "+0000"
, EDT: "-0400"
, EST: "-0500"
, CDT: "-0500"
, CST: "-0600"
, MDT: "-0600"
, MST: "-0700"
, PDT: "-0700"
, PST: "-0800"
}
//
, strip
, $
;
// The RFC 822 function is our sole export.
return rfc822;
// In the IANA Timezone Database, Arthur David Olson has this to say about
// translating time zone abbreviations to time zone offsets.
// > The UNIX Version 7 "timezone" function is not present in this package; it's
// > impossible to reliably map timezone's arguments (a "minutes west of GMT"
// > value and a "daylight saving time in effect" flag) to a time zone
// > abbreviation, and we refuse to guess.
// So, please, no pull requests with more time zone abbreviation mappings.
// What might be right for you, may not be right for some.
//
// RFC 2822 says that you can interpret military zones if you have out-of-band
// information confirming whether they are NATO or RFC 822. The second
// parameter to our parsing function is this optional out-of-band data.
//
// var eq = require("assert").equal
// , tz = require("timezone")
// , moonwalk = tz("1969-07-21 02:56")
// , military = { nato: {}, rfc822: {} }
// , index
// ;
//
// index = 0;
// "ABCDEFGHIKLM".replace(/./g, function (ch) {
// var offset = ("0" + (++index) + "00").slice(-4);
// military.nato[ch] = "+" + offset;
// military.rfc822[ch] = "-" + offset;
// });
//
// index = 0;
// "NOPQRSTUVWXY".replace(/./g, function (ch) {
// var offset = ("0" + (++index) + "00").slice(-4);
// military.nato[ch] = "-" + offset;
// military.rfc822[ch] = "+" + offset;
// });
//
// eq( tz(rfc822("Sun, 20 Jul 1969 10:56:00 E", military.rfc822)), moonwalk );
// eq( tz(rfc822("Sun, 20 Jul 1969 10:56:00 R", military.nato)), moonwalk );
//
// If you have any additional out-of-band information on time zone
// abbreviations for your application, provide that information in the map of
// additional abbreviations.
//
function rfc822(date, abbrev) {
var offset;
abbrev = abbrev || {};
// The full RFC 2822 specification includes support for features of legacy
// RFC 822 dates that I've never see in the wild. Specifically the ability
// to place comments anywhere in a date.
//
// > `Sun, 1 Mar (Spring is coming soon!) 92 00:00:00 GMT`
//
// Comments are enclosed in parenthesis. Comments can contain nested
// comments making it impossible to match them with a regular expression, as
// any discussion of a regular expression to match an RFC 822 email address
// will tell you.
//
// We strip the comments. Our approach to RFC 822 comments is informed by a
// long peek at the workings of
// [Mail::RFC822::Address](http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html).
do { strip = date } while ((date = strip.replace(/\((?:[^()\\]|\\.)*\)/, " ")) != strip);
// To parse the named timezones we'll simply match 1 or more consecutive
// letters, a through z. If those letters form on one of the time zone
// abbreviations we know about, we use the time zone offset associated with
// the abbreviation. Otherwise, we use `"-0000"`.
//
$ = /^\s*(?:(\w{3}),\s+)?(\d{1,2})\s+(\w{3})\s+(\d{2,4})\s+(\d{2}):(\d{2})(?::(\d{2}))?\s*(?:([-+]?\d{4})|([a-z]{1,5}))?\s*$/i.exec(date)
if (!$) throw new Error("invalid rfc822 date");
$[3] = MONTH.indexOf($[3].toLowerCase());
if (!~$[3]) throw new Error("invalid month");
$[0] = +($[4]);
$[4] = $[2];
$[2] = $[0];
// RFC 2822 specifies how to interpret a 2 digit year or 3 digit year exactly.
if ($[2] < 50) $[2] += 2000;
else if ($[2] < 1000) $[2] += 1900;
date = $.slice(2, 8);
// If it comes to bite you that someone has specified the wrong day of week
// for a particular date, you probably cannot trust those dates, but if you
// must parse them, strip the day of the week from the RFC 822 date string
// before calling this function to skip this assertion.
//
if ($[1]) {
$[1] = DAY.indexOf($[1].toLowerCase());
if (!~$[1])
throw new Error("invalid day of week");
if (new Date(Date.UTC.apply(Date.UTC, date)).getUTCDay() != $[1])
throw new Error("incorrect day of week for date");
}
date.push(0);
// If we cannot be certain of a time zone abbreviation, we default to
// "-0000" which indicates an uncertain UTC, according to RFC 2822. Of
// course, that bit of knowledge never leaves the function, but we'll just
// do as RFC 2822 says.
//
offset = $[8] || ABBREV[$[9].toUpperCase()] || abbrev[$[9].toUpperCase()] || "-0000"
if (offset == "GMT") date.push(1);
else if (offset) date.push(offset < 0 ? -1 : 1, Math.abs(Math.floor(offset / 100)), offset % 100);
++date[1];
return date;
}
});