strftime/strftime.js
2016-10-03 10:12:53 -07:00

861 lines
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// strftime
// github.com/samsonjs/strftime
// @_sjs
//
// Copyright 2010 - 2015 Sami Samhuri <sami@samhuri.net>
//
// MIT License
// http://sjs.mit-license.org
//
;(function() {
var locales = {
de_DE: {
days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
'Freitag', 'Samstag'],
shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
'August', 'September', 'Oktober', 'November', 'Dezember'],
shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d.%m.%Y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
en_US: {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday' ],
shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
months: ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
'Sep', 'Oct', 'Nov', 'Dec'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%m/%d/%y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%r',
x: '%D'
}
},
es_MX: {
days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
'sábado'],
shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio',
'julio', 'agosto', 'septiembre', 'octubre', 'noviembre',' diciembre'],
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago',
'sep', 'oct', 'nov', 'dic'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d/%m/%Y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
fr_FR: {
days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
'samedi'],
shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
shortMonths: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin',
'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d/%m/%Y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
it_IT: {
days: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì',
'venerdì', 'sabato'],
shortDays: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
months: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
shortMonths: ['pr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov',
'dic'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d/%m/%Y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
nl_NL: {
days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag',
'vrijdag', 'zaterdag'],
shortDays: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni',
'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
shortMonths: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug',
'sep', 'okt', 'nov', 'dec'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d-%m-%y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
pt_BR: {
days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta',
'sábado'],
shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho',
'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
shortMonths: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago',
'Set', 'Out', 'Nov', 'Dez'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d-%m-%Y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
ru_RU: {
days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг',
'Пятница', 'Суббота'],
shortDays: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл',
'авг', 'сен', 'окт', 'ноя', 'дек'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
c: '%a %d %b %Y %X',
D: '%d.%m.%y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
},
tr_TR: {
days: ['Pazar', 'Pazartesi', 'Salı','Çarşamba', 'Perşembe', 'Cuma',
'Cumartesi'],
shortDays: ['Paz', 'Pzt', 'Sal', 'Çrş', 'Prş', 'Cum', 'Cts'],
months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
shortMonths: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem',
'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
AM: 'ÖÖ',
PM: 'ÖS',
am: 'ÖÖ',
pm: 'ÖS',
formats: {
c: '%a %d %b %Y %X %Z',
D: '%d-%m-%Y',
F: '%Y-%m-%d',
R: '%H:%M',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
X: '%T',
x: '%D'
}
}
};
var DefaultLocale = {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
D: '%m/%d/%y',
F: '%Y-%m-%d',
R: '%H:%M',
T: '%H:%M:%S',
X: '%T',
c: '%a %b %d %X %Y',
r: '%I:%M:%S %p',
v: '%e-%b-%Y',
x: '%D'
}
},
defaultStrftime = new Strftime(DefaultLocale, 0, false),
isCommonJS = typeof module !== 'undefined',
namespace;
// CommonJS / Node module
if (isCommonJS) {
namespace = module.exports = adaptedStrftime;
namespace.strftime = deprecatedStrftime;
}
// Browsers and other environments
else {
// Get the global object. Works in ES3, ES5, and ES5 strict mode.
namespace = (function() { return this || (1,eval)('this'); }());
namespace.strftime = adaptedStrftime;
}
// Deprecated API, to be removed in v1.0
var _require = isCommonJS ? "require('strftime')" : "strftime";
var _deprecationWarnings = {};
function deprecationWarning(name, instead) {
if (!_deprecationWarnings[name]) {
if (typeof console !== 'undefined' && typeof console.warn == 'function') {
console.warn("[WARNING] " + name + " is deprecated and will be removed in version 1.0. Instead, use `" + instead + "`.");
}
_deprecationWarnings[name] = true;
}
}
namespace.strftimeTZ = deprecatedStrftimeTZ;
namespace.strftimeUTC = deprecatedStrftimeUTC;
namespace.localizedStrftime = deprecatedStrftimeLocalized;
// Adapt the old API while preserving the new API.
function adaptForwards(fn) {
fn.localize = defaultStrftime.localize.bind(defaultStrftime);
fn.localizeByIdentifier = defaultStrftime.localizeByIdentifier.bind(defaultStrftime);
fn.timezone = defaultStrftime.timezone.bind(defaultStrftime);
fn.utc = defaultStrftime.utc.bind(defaultStrftime);
}
adaptForwards(adaptedStrftime);
function adaptedStrftime(fmt, d, locale) {
// d and locale are optional, check if this is (format, locale)
if (d && d.days) {
locale = d;
d = undefined;
}
if (locale) {
deprecationWarning("`" + _require + "(format, [date], [locale])`", "var s = " + _require + ".localize(locale); s(format, [date])");
}
var strftime = locale ? defaultStrftime.localize(locale) : defaultStrftime;
return strftime(fmt, d);
}
adaptForwards(deprecatedStrftime);
function deprecatedStrftime(fmt, d, locale) {
if (locale) {
deprecationWarning("`" + _require + ".strftime(format, [date], [locale])`", "var s = " + _require + ".localize(locale); s(format, [date])");
}
else {
deprecationWarning("`" + _require + ".strftime(format, [date])`", _require + "(format, [date])");
}
var strftime = locale ? defaultStrftime.localize(locale) : defaultStrftime;
return strftime(fmt, d);
}
function deprecatedStrftimeTZ(fmt, d, locale, timezone) {
// locale is optional, check if this is (format, date, timezone)
if ((typeof locale == 'number' || typeof locale == 'string') && timezone == null) {
timezone = locale;
locale = undefined;
}
if (locale) {
deprecationWarning("`" + _require + ".strftimeTZ(format, date, locale, tz)`", "var s = " + _require + ".localize(locale).timezone(tz); s(format, [date])` or `var s = " + _require + ".localize(locale); s.timezone(tz)(format, [date])");
}
else {
deprecationWarning("`" + _require + ".strftimeTZ(format, date, tz)`", "var s = " + _require + ".timezone(tz); s(format, [date])` or `" + _require + ".timezone(tz)(format, [date])");
}
var strftime = (locale ? defaultStrftime.localize(locale) : defaultStrftime).timezone(timezone);
return strftime(fmt, d);
}
var utcStrftime = defaultStrftime.utc();
function deprecatedStrftimeUTC(fmt, d, locale) {
if (locale) {
deprecationWarning("`" + _require + ".strftimeUTC(format, date, locale)`", "var s = " + _require + ".localize(locale).utc(); s(format, [date])");
}
else {
deprecationWarning("`" + _require + ".strftimeUTC(format, [date])`", "var s = " + _require + ".utc(); s(format, [date])");
}
var strftime = locale ? utcStrftime.localize(locale) : utcStrftime;
return strftime(fmt, d);
}
function deprecatedStrftimeLocalized(locale) {
deprecationWarning("`" + _require + ".localizedStrftime(locale)`", _require + ".localize(locale)");
return defaultStrftime.localize(locale);
}
// End of deprecated API
// Polyfill Date.now for old browsers.
if (typeof Date.now !== 'function') {
Date.now = function() {
return +new Date();
};
}
function Strftime(locale, customTimezoneOffset, useUtcTimezone) {
var _locale = locale || DefaultLocale,
_customTimezoneOffset = customTimezoneOffset || 0,
_useUtcBasedDate = useUtcTimezone || false,
// we store unix timestamp value here to not create new Date() each iteration (each millisecond)
// Date.now() is 2 times faster than new Date()
// while millisecond precise is enough here
// this could be very helpful when strftime triggered a lot of times one by one
_cachedDateTimestamp = 0,
_cachedDate;
function _strftime(format, date) {
var timestamp;
if (!date) {
var currentTimestamp = Date.now();
if (currentTimestamp > _cachedDateTimestamp) {
_cachedDateTimestamp = currentTimestamp;
_cachedDate = new Date(_cachedDateTimestamp);
timestamp = _cachedDateTimestamp;
if (_useUtcBasedDate) {
// how to avoid duplication of date instantiation for utc here?
// we tied to getTimezoneOffset of the current date
_cachedDate = new Date(_cachedDateTimestamp + getTimestampToUtcOffsetFor(_cachedDate) + _customTimezoneOffset);
}
}
else {
timestamp = _cachedDateTimestamp;
}
date = _cachedDate;
}
else {
timestamp = date.getTime();
if (_useUtcBasedDate) {
date = new Date(date.getTime() + getTimestampToUtcOffsetFor(date) + _customTimezoneOffset);
}
}
return _processFormat(format, date, _locale, timestamp);
}
function _processFormat(format, date, locale, timestamp) {
var resultString = '',
padding = null,
isInScope = false,
length = format.length,
extendedTZ = false;
for (var i = 0; i < length; i++) {
var currentCharCode = format.charCodeAt(i);
if (isInScope === true) {
// '-'
if (currentCharCode === 45) {
padding = '';
continue;
}
// '_'
else if (currentCharCode === 95) {
padding = ' ';
continue;
}
// '0'
else if (currentCharCode === 48) {
padding = '0';
continue;
}
// ':'
else if (currentCharCode === 58) {
if (extendedTZ) {
if (typeof console !== 'undefined' && typeof console.warn == 'function') {
console.warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime");
}
}
extendedTZ = true;
continue;
}
switch (currentCharCode) {
// Examples for new Date(0) in GMT
// 'Thursday'
// case 'A':
case 65:
resultString += locale.days[date.getDay()];
break;
// 'January'
// case 'B':
case 66:
resultString += locale.months[date.getMonth()];
break;
// '19'
// case 'C':
case 67:
resultString += padTill2(Math.floor(date.getFullYear() / 100), padding);
break;
// '01/01/70'
// case 'D':
case 68:
resultString += _processFormat(locale.formats.D, date, locale, timestamp);
break;
// '1970-01-01'
// case 'F':
case 70:
resultString += _processFormat(locale.formats.F, date, locale, timestamp);
break;
// '00'
// case 'H':
case 72:
resultString += padTill2(date.getHours(), padding);
break;
// '12'
// case 'I':
case 73:
resultString += padTill2(hours12(date.getHours()), padding);
break;
// '000'
// case 'L':
case 76:
resultString += padTill3(Math.floor(timestamp % 1000));
break;
// '00'
// case 'M':
case 77:
resultString += padTill2(date.getMinutes(), padding);
break;
// 'am'
// case 'P':
case 80:
resultString += date.getHours() < 12 ? locale.am : locale.pm;
break;
// '00:00'
// case 'R':
case 82:
resultString += _processFormat(locale.formats.R, date, locale, timestamp);
break;
// '00'
// case 'S':
case 83:
resultString += padTill2(date.getSeconds(), padding);
break;
// '00:00:00'
// case 'T':
case 84:
resultString += _processFormat(locale.formats.T, date, locale, timestamp);
break;
// '00'
// case 'U':
case 85:
resultString += padTill2(weekNumber(date, 'sunday'), padding);
break;
// '00'
// case 'W':
case 87:
resultString += padTill2(weekNumber(date, 'monday'), padding);
break;
// '16:00:00'
// case 'X':
case 88:
resultString += _processFormat(locale.formats.X, date, locale, timestamp);
break;
// '1970'
// case 'Y':
case 89:
resultString += date.getFullYear();
break;
// 'GMT'
// case 'Z':
case 90:
if (_useUtcBasedDate && _customTimezoneOffset === 0) {
resultString += "GMT";
}
else {
// fixme optimize
var tzString = date.toString().match(/\(([\w\s]+)\)/);
resultString += tzString && tzString[1] || '';
}
break;
// 'Thu'
// case 'a':
case 97:
resultString += locale.shortDays[date.getDay()];
break;
// 'Jan'
// case 'b':
case 98:
resultString += locale.shortMonths[date.getMonth()];
break;
// ''
// case 'c':
case 99:
resultString += _processFormat(locale.formats.c, date, locale, timestamp);
break;
// '01'
// case 'd':
case 100:
resultString += padTill2(date.getDate(), padding);
break;
// ' 1'
// case 'e':
case 101:
resultString += padTill2(date.getDate(), padding == null ? ' ' : padding);
break;
// 'Jan'
// case 'h':
case 104:
resultString += locale.shortMonths[date.getMonth()];
break;
// '000'
// case 'j':
case 106:
var y = new Date(date.getFullYear(), 0, 1);
var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24));
resultString += padTill3(day);
break;
// ' 0'
// case 'k':
case 107:
resultString += padTill2(date.getHours(), padding == null ? ' ' : padding);
break;
// '12'
// case 'l':
case 108:
resultString += padTill2(hours12(date.getHours()), padding == null ? ' ' : padding);
break;
// '01'
// case 'm':
case 109:
resultString += padTill2(date.getMonth() + 1, padding);
break;
// '\n'
// case 'n':
case 110:
resultString += '\n';
break;
// '1st'
// case 'o':
case 111:
resultString += String(date.getDate()) + ordinal(date.getDate());
break;
// 'AM'
// case 'p':
case 112:
resultString += date.getHours() < 12 ? locale.AM : locale.PM;
break;
// '12:00:00 AM'
// case 'r':
case 114:
resultString += _processFormat(locale.formats.r, date, locale, timestamp);
break;
// '0'
// case 's':
case 115:
resultString += Math.floor(timestamp / 1000);
break;
// '\t'
// case 't':
case 116:
resultString += '\t';
break;
// '4'
// case 'u':
case 117:
var day = date.getDay();
resultString += day === 0 ? 7 : day;
break; // 1 - 7, Monday is first day of the week
// ' 1-Jan-1970'
// case 'v':
case 118:
resultString += _processFormat(locale.formats.v, date, locale, timestamp);
break;
// '4'
// case 'w':
case 119:
resultString += date.getDay();
break; // 0 - 6, Sunday is first day of the week
// '12/31/69'
// case 'x':
case 120:
resultString += _processFormat(locale.formats.x, date, locale, timestamp);
break;
// '70'
// case 'y':
case 121:
resultString += ('' + date.getFullYear()).slice(2);
break;
// '+0000'
// case 'z':
case 122:
if (_useUtcBasedDate && _customTimezoneOffset === 0) {
resultString += extendedTZ ? "+00:00" : "+0000";
}
else {
var off;
if (_customTimezoneOffset !== 0) {
off = _customTimezoneOffset / (60 * 1000);
}
else {
off = -date.getTimezoneOffset();
}
var sign = off < 0 ? '-' : '+';
var sep = extendedTZ ? ':' : '';
var hours = Math.floor(Math.abs(off / 60));
var mins = Math.abs(off % 60);
resultString += sign + padTill2(hours) + sep + padTill2(mins);
}
break;
default:
resultString += format[i];
break;
}
padding = null;
isInScope = false;
continue;
}
// '%'
if (currentCharCode === 37) {
isInScope = true;
continue;
}
resultString += format[i];
}
return resultString;
}
var strftime = _strftime;
strftime.localize = function(locale) {
return new Strftime(locale || _locale, _customTimezoneOffset, _useUtcBasedDate);
};
strftime.localizeByIdentifier = function(localeIdentifier) {
return strftime.localize(locales[localeIdentifier]);
};
strftime.timezone = function(timezone) {
var customTimezoneOffset = _customTimezoneOffset;
var useUtcBasedDate = _useUtcBasedDate;
var timezoneType = typeof timezone;
if (timezoneType === 'number' || timezoneType === 'string') {
useUtcBasedDate = true;
// ISO 8601 format timezone string, [-+]HHMM
if (timezoneType === 'string') {
var sign = timezone[0] === '-' ? -1 : 1,
hours = parseInt(timezone.slice(1, 3), 10),
minutes = parseInt(timezone.slice(3, 5), 10);
customTimezoneOffset = sign * ((60 * hours) + minutes) * 60 * 1000;
// in minutes: 420
}
else if (timezoneType === 'number') {
customTimezoneOffset = timezone * 60 * 1000;
}
}
return new Strftime(_locale, customTimezoneOffset, useUtcBasedDate);
};
strftime.utc = function() {
return new Strftime(_locale, _customTimezoneOffset, true);
};
return strftime;
}
function padTill2(numberToPad, paddingChar) {
if (paddingChar === '' || numberToPad > 9) {
return numberToPad;
}
if (paddingChar == null) {
paddingChar = '0';
}
return paddingChar + numberToPad;
}
function padTill3(numberToPad) {
if (numberToPad > 99) {
return numberToPad;
}
if (numberToPad > 9) {
return '0' + numberToPad;
}
return '00' + numberToPad;
}
function hours12(hour) {
if (hour === 0) {
return 12;
}
else if (hour > 12) {
return hour - 12;
}
return hour;
}
// firstWeekday: 'sunday' or 'monday', default is 'sunday'
//
// Pilfered & ported from Ruby's strftime implementation.
function weekNumber(date, firstWeekday) {
firstWeekday = firstWeekday || 'sunday';
// This works by shifting the weekday back by one day if we
// are treating Monday as the first day of the week.
var weekday = date.getDay();
if (firstWeekday === 'monday') {
if (weekday === 0) // Sunday
weekday = 6;
else
weekday--;
}
var firstDayOfYearUtc = Date.UTC(date.getFullYear(), 0, 1),
dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
yday = Math.floor((dateUtc - firstDayOfYearUtc) / 86400000),
weekNum = (yday + 7 - weekday) / 7;
return Math.floor(weekNum);
}
// Get the ordinal suffix for a number: st, nd, rd, or th
function ordinal(number) {
var i = number % 10;
var ii = number % 100;
if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) {
return 'th';
}
switch (i) {
case 1: return 'st';
case 2: return 'nd';
case 3: return 'rd';
}
}
function getTimestampToUtcOffsetFor(date) {
return (date.getTimezoneOffset() || 0) * 60000;
}
}());