/* * Dateutil * - provides formatting, parsing and other utility functions for dates. * * Copyright (c) 2009 Borgar Þorsteinsson * Licensed under the terms of the MIT (LICENSE.txt) software license. * */ /*jslint laxbreak: true, undef: true *//*global module */ (function(__global__){ var SECOND_SIZE = 1000; var MINUTE_SIZE = SECOND_SIZE * 60; var HOUR_SIZE = MINUTE_SIZE * 60; var DAY_SIZE = HOUR_SIZE * 24; var WEEK_SIZE = DAY_SIZE * 7; var MONTH_SIZE = DAY_SIZE * 30.436875; // average month size var YEAR_SIZE = DAY_SIZE * 365.2425; // average year size var _gl = __global__.lang = { 'en': {} }; var _m = 'January February March April May June July August September October November December'.split(' '); var _d = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '); var method_size = { 'FullYear': 6, 'Month': 5, 'Date': 4, 'Hours': 3, 'Minutes': 2, 'Seconds': 1, 'Milliseconds': 0 }; var method_map = { 'yr': 'FullYear', 'year': 'FullYear', 'years': 'FullYear', 'mn': 'Month', 'month': 'Month', 'months': 'Month', 'day': 'Date', 'days': 'Date', 'date': 'Date', 'hr': 'Hours', 'hour': 'Hours', 'hours': 'Hours', 'min': 'Minutes', 'minute': 'Minutes', 'minutes': 'Minutes', 'sec': 'Seconds', 'second': 'Seconds', 'seconds': 'Seconds', 'ms': 'Milliseconds', 'millisecond': 'Milliseconds', 'milliseconds': 'Milliseconds' }; // ***************************************** // *** *** *** formats & parsers *** *** *** // ***************************************** var date_parsers = __global__._parsers = { // year + month + day + time // -- currently doesn't really support fractions on anything other than seconds >> FIXME // -- does not support timezones other than Zulu date_and_time: { test: /^[+\-]?\d{4,6}(?:(?:\-\d\d){1,2}|\d{4})[T ](?:\d\d)(?::?\d\d){0,2}(?:[\.,]\d+)?(?:Z|[+\-]\d\d(:?\d\d)?)?$/, size: 1, parse: function ( str ) { var b = str.split( /[T ]/ ); var date = date_parsers.date.parse( b[0] ); var m = b[1].replace( /:/g, '' ) .match( /^(\d\d)(\d\d)?(\d\d)?(?:[.,](\d+))?([+\-](?:\d\d){1,2})?/ ); // TODO: timezone (I have no need for this feature yet) // if ( m[5] ) { var zone = m[5] || '0000'; } var fs = 0, t = date.getTime() + parseInt( m[1], 10 ) * HOUR_SIZE + parseInt( m[2] || '0', 10 ) * MINUTE_SIZE + parseInt( m[3] || '0', 10 ) * SECOND_SIZE; if ( m[3] ) { fs = SECOND_SIZE; } else if ( m[2] ) { fs = MINUTE_SIZE; } else if ( m[1] ) { fs = HOUR_SIZE; } t += parseFloat( '0.' + ( m[4] || '0' ) ) * fs; date.setTime( t ); date.size = 0; return date; } }, // year + month + day date: { test: /^[+\-]?\d{4,6}(?:\-\d\d\-\d\d|-?\d\d\d\d)$/, size: DAY_SIZE, parse: function ( str ) { var s = str.replace( /\D/g, '' ), d = __global__.date( s.substring( 0, s.length -4 ), +s.substr( s.length -4, 2 ) - 1, s.substr( s.length -2 ) ); d.size = DAY_SIZE; return d; } }, // year + month year_and_month: { test: /^[+\-]?\d{4,6}[\/\-](?:0[1-9]|1[012])$/, size: MONTH_SIZE, parse: function ( str ) { var b = str.split( /[\/\-]/ ); var d = __global__.date( b[0], +b[1] - 1, 1 ); d.size = __global__.daysInMonth( d ) * DAY_SIZE; return d; } }, // year year: { test: /^[+\-]?\d{4,6}$/, size: YEAR_SIZE, parse: function ( str ) { var d = __global__.date( str, 0, 1 ); d.size = DAY_SIZE * ( __global__.isLeapYear( d ) ? 366 : 365 ); return d; } }, // year + iso week + [day] year_and_week: { test: /^[+\-]?\d{4,6}\-?[Ww]\d\d(?:\-?\d)?$/, size: WEEK_SIZE, parse: function ( str ) { var s = str.toLowerCase().replace( /[^w\d]/g, '' ).split('w'); var d = __global__.date( s[0], 0, 3 ); // Jan 3 d.setUTCDate( 3 - d.getUTCDay() + ( parseInt( s[1].substr( 0, 2 ), 10 ) - 1 ) * 7 + parseInt( s[1].substr( 2, 1 ) || '1', 10 ) ); d.size = WEEK_SIZE; return d; } }, // year + day-of-year // -- we don't allow the short form yyyyddd because of ambiguity with yyyymmdd // -- 5 letter years would clash with cal-dates: yyyyyddd ~ yyyymmdd year_and_ordinal: { test: /^[+\-]?\d{4,6}\-[0-3]\d\d$/, size: DAY_SIZE, parse: function ( str ) { var d = new Date(0); d.setUTCFullYear( parseInt( str.substr(0,str.length-4), 10 ) ); d.setDate( parseInt( str.substr(str.length-3), 10 ) ); d.size = DAY_SIZE; return d; } }, // year + quarter year_and_quarter: { test: /^[+\-]?\d{4,6}\-?[Qq][1-4]$/, size: YEAR_SIZE / 4, parse: function ( str ) { var b = str.split(/\-?[Qq]/), d = __global__.date( b[0], ( parseInt( b[1], 10 ) - 1 ) * 3 ); d.size = DAY_SIZE; return d; } } }; var date_formatters = __global__._formats = { // Lowercase Ante meridiem and Post meridiem a: function (d) { return d.getUTCHours() >= 12 ? 'pm' : 'am'; }, // Uppercase Ante meridiem and Post meridiem A: function (d) { return d.getUTCHours() >= 12 ? 'PM' : 'AM'; }, // ISO 8601 date c: function (d,l) { return __global__.format( d, 'Y-m-d\\TH:i:s.', l ) + __global__.pad( d.getUTCMilliseconds(), 3 ) + 'Z'; }, // Day of the month, 2 digits with leading zeros d: function (d) { return __global__.pad( d.getUTCDate() ); }, // A textual representation of a day, three letters D: function (d,l) { return __global__._( _d[ d.getUTCDay() ].substr( 0, 3 ), l ); }, // Time zone identifier e: function (d) { return 'UTC'; }, // A full textual representation of a month F: function (d,l) { return __global__._( _m[ d.getUTCMonth() ], l ); }, // 12-hour format of an hour without leading zeros g: function (d) { return d.getUTCHours() % 12 || 12; }, // 24-hour format of an hour without leading zeros G: function (d) { return d.getUTCHours(); }, // 12-hour format of an hour with leading zeros h: function (d) { return __global__.pad( d.getUTCHours() % 12 || 12 ); }, // 24-hour format of an hour with leading zeros H: function (d) { return __global__.pad( d.getUTCHours() ); }, // Minutes with leading zeros i: function (d) { return __global__.pad( d.getUTCMinutes() ); }, // Day of the month without leading zeros j: function (d) { return d.getUTCDate(); }, // A full textual representation of the day of the week l: function (d,l) { return __global__._( _d[ d.getUTCDay() ], l ); }, // Whether it's a leap year (0 = yes, 1 = no) L: function (d) { return __global__.isLeapYear( d ) * 1; }, // Numeric representation of a month, with leading zeros m: function (d) { return __global__.pad( d.getUTCMonth() + 1 ); }, // A short textual representation of a month, three letters M: function (d,l) { return __global__._( _m[ d.getUTCMonth() ].substr( 0, 3 ), l ); }, // Numeric representation of a month, without leading zeros n: function (d) { return d.getUTCMonth() + 1; }, // ISO-8601 numeric representation of the day of the week N: function (d) { return d.getUTCDay() || 7; }, // ISO-8601 year number o: function (d) { return __global__.pad( __global__.isocalendar(d)[0], 4 ); }, // Time zone designator O: function (d) { return '+0000'; }, // Time zone difference P: function (d) { return '+00:00'; }, // Quarter of the year q: function (d) { return ~~( d.getUTCMonth() / 3 ) + 1; }, // RFC 2822 formatted date r: function (d,l) { return __global__.format( d, 'D, d M Y H:i:s O', l ); }, // Seconds, with leading zeros s: function (d) { return __global__.pad( d.getUTCSeconds() ); }, // English ordinal suffix for the day of the month, 2 characters S: function (d) { var a = d.getUTCDate() % 10, b = d.getUTCDate() % 100; return (a === 1) && (b !== 11) && 'st' || (a === 2) && (b !== 12) && 'nd' || (a === 3) && (b !== 13) && 'rd' || 'th'; }, // Number of days in the given month t: function (d) { return __global__.daysInMonth( d ); }, // Time zone abbreviation T: function (d) { return 'UTC'; }, // Microseconds u: function (d) { return __global__.pad( d.getUTCMilliseconds(), 6 ); }, // Seconds since the Unix Epoch U: function (d) { return ~~( d / 1000 ); }, // Numeric representation of the day of the week w: function (d) { return d.getUTCDay(); }, // ISO-8601 week number of year, weeks starting on Monday W: function (d) { return __global__.pad( __global__.isocalendar(d)[1] ); }, // A short numeric representation of a year, 2 digits y: function (d) { return (d.getUTCFullYear() + '').substr(2); }, // A full numeric representation of a year, 4 digits Y: function (d) { return d.getUTCFullYear(); }, // The day of the year (starting from 0) z: function (d) { return Math.floor( ( d - (new Date(Date.UTC(d.getUTCFullYear(), 0, 1))) ) / DAY_SIZE ); } }; // ************************************** // *** *** *** module methods *** *** *** // ************************************** __global__.date = function ( y, m, d, h, n, s, ms ) { var ts = ( !arguments.length ) ? __global__.now() : Date.UTC( parseInt( y||0, 10 ), parseInt( m||0, 10 ), parseInt( d||1, 10 ), parseInt( h||0, 10 ), parseInt( n||0, 10 ), parseInt( s||0, 10 ), parseInt( ms||0, 10 ) ); return new Date( ts ); }; // zero pad a string n to l places __global__.pad = function ( n, l ) { var s = __global__.pad.z; if ( !s ) { // This mess is here because JSlint breaks on new Array(999) var a = []; a[999] = ''; s = __global__.pad.z = a.join('0'); } s += n; return s.substring( s.length -( l || 2 ) ); }; // is a given year a leap year __global__.isLeapYear = function ( y ) { if ( y instanceof Date ) { y = y.getUTCFullYear(); } return (( y % 4 === 0 ) && ( y % 100 !== 0 )) || ( y % 400 === 0 ); }; // return the number of days in a date's month __global__.daysInMonth = function ( dt ) { var m = dt.getUTCMonth(); if ( m === 1 ) { return __global__.isLeapYear( dt ) ? 29 : 28; } return [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ][ m ]; }; // return a 3-tuple, (ISO year, ISO week number, ISO weekday). __global__.isocalendar = function ( dt ) { var d = dt.getUTCDay(); var t = new Date( dt.valueOf() ); t.setDate( t.getDate() - ((d + 6) % 7) + 3 ); var iso_year = t.getUTCFullYear(); var w = Math.floor( (t.getTime() - __global__.date(iso_year, 0, 1, -6)) / 86400000 ); return [ iso_year, 1+Math.floor(w/7), d||7 ]; }; // Allow setting multiple properties at once using object notation: // `mydate.set({ hour: 8, minute: 12, second: 0 });` __global__.set = function ( dt, values ) { if ( typeof values === 'object' ) { var s = [], n, i; // step 1: collect a list of values to modify for ( var key in values ) { if ( key in method_map ) { n = method_map[ key ]; s.push( [ values[ key ], n, method_size[ n ] ] ); } } // step 2: order values by size s = s.sort(function ( a, b ) { return a[2] - b[2]; }); // step 3: little endian value zeroing for (i=0; i