PageRenderTime 146ms CodeModel.GetById 60ms app.highlight 30ms RepoModel.GetById 50ms app.codeStats 1ms

/ajax/scripts/date-time.js

http://showslow.googlecode.com/
JavaScript | 452 lines | 290 code | 45 blank | 117 comment | 33 complexity | 6763f09e5767174cbf58d95071004a03 MD5 | raw file
  1/**
  2 * @fileOverview A collection of date/time utility functions
  3 * @name SimileAjax.DateTime
  4 */
  5
  6SimileAjax.DateTime = new Object();
  7
  8SimileAjax.DateTime.MILLISECOND    = 0;
  9SimileAjax.DateTime.SECOND         = 1;
 10SimileAjax.DateTime.MINUTE         = 2;
 11SimileAjax.DateTime.HOUR           = 3;
 12SimileAjax.DateTime.DAY            = 4;
 13SimileAjax.DateTime.WEEK           = 5;
 14SimileAjax.DateTime.MONTH          = 6;
 15SimileAjax.DateTime.YEAR           = 7;
 16SimileAjax.DateTime.DECADE         = 8;
 17SimileAjax.DateTime.CENTURY        = 9;
 18SimileAjax.DateTime.MILLENNIUM     = 10;
 19
 20SimileAjax.DateTime.EPOCH          = -1;
 21SimileAjax.DateTime.ERA            = -2;
 22
 23/**
 24 * An array of unit lengths, expressed in milliseconds, of various lengths of
 25 * time.  The array indices are predefined and stored as properties of the
 26 * SimileAjax.DateTime object, e.g. SimileAjax.DateTime.YEAR.
 27 * @type Array
 28 */
 29SimileAjax.DateTime.gregorianUnitLengths = [];
 30    (function() {
 31        var d = SimileAjax.DateTime;
 32        var a = d.gregorianUnitLengths;
 33        
 34        a[d.MILLISECOND] = 1;
 35        a[d.SECOND]      = 1000;
 36        a[d.MINUTE]      = a[d.SECOND] * 60;
 37        a[d.HOUR]        = a[d.MINUTE] * 60;
 38        a[d.DAY]         = a[d.HOUR] * 24;
 39        a[d.WEEK]        = a[d.DAY] * 7;
 40        a[d.MONTH]       = a[d.DAY] * 31;
 41        a[d.YEAR]        = a[d.DAY] * 365;
 42        a[d.DECADE]      = a[d.YEAR] * 10;
 43        a[d.CENTURY]     = a[d.YEAR] * 100;
 44        a[d.MILLENNIUM]  = a[d.YEAR] * 1000;
 45    })();
 46    
 47SimileAjax.DateTime._dateRegexp = new RegExp(
 48    "^(-?)([0-9]{4})(" + [
 49        "(-?([0-9]{2})(-?([0-9]{2}))?)", // -month-dayOfMonth
 50        "(-?([0-9]{3}))",                // -dayOfYear
 51        "(-?W([0-9]{2})(-?([1-7]))?)"    // -Wweek-dayOfWeek
 52    ].join("|") + ")?$"
 53);
 54SimileAjax.DateTime._timezoneRegexp = new RegExp(
 55    "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"
 56);
 57SimileAjax.DateTime._timeRegexp = new RegExp(
 58    "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"
 59);
 60
 61/**
 62 * Takes a date object and a string containing an ISO 8601 date and sets the
 63 * the date using information parsed from the string.  Note that this method
 64 * does not parse any time information.
 65 *
 66 * @param {Date} dateObject the date object to modify
 67 * @param {String} string an ISO 8601 string to parse
 68 * @return {Date} the modified date object
 69 */
 70SimileAjax.DateTime.setIso8601Date = function(dateObject, string) {
 71    /*
 72     *  This function has been adapted from dojo.date, v.0.3.0
 73     *  http://dojotoolkit.org/.
 74     */
 75     
 76    var d = string.match(SimileAjax.DateTime._dateRegexp);
 77    if(!d) {
 78        throw new Error("Invalid date string: " + string);
 79    }
 80    
 81    var sign = (d[1] == "-") ? -1 : 1; // BC or AD
 82    var year = sign * d[2];
 83    var month = d[5];
 84    var date = d[7];
 85    var dayofyear = d[9];
 86    var week = d[11];
 87    var dayofweek = (d[13]) ? d[13] : 1;
 88
 89    dateObject.setUTCFullYear(year);
 90    if (dayofyear) { 
 91        dateObject.setUTCMonth(0);
 92        dateObject.setUTCDate(Number(dayofyear));
 93    } else if (week) {
 94        dateObject.setUTCMonth(0);
 95        dateObject.setUTCDate(1);
 96        var gd = dateObject.getUTCDay();
 97        var day =  (gd) ? gd : 7;
 98        var offset = Number(dayofweek) + (7 * Number(week));
 99        
100        if (day <= 4) { 
101            dateObject.setUTCDate(offset + 1 - day); 
102        } else { 
103            dateObject.setUTCDate(offset + 8 - day); 
104        }
105    } else {
106        if (month) { 
107            dateObject.setUTCDate(1);
108            dateObject.setUTCMonth(month - 1); 
109        }
110        if (date) { 
111            dateObject.setUTCDate(date); 
112        }
113    }
114    
115    return dateObject;
116};
117
118/**
119 * Takes a date object and a string containing an ISO 8601 time and sets the
120 * the time using information parsed from the string.  Note that this method
121 * does not parse any date information.
122 *
123 * @param {Date} dateObject the date object to modify
124 * @param {String} string an ISO 8601 string to parse
125 * @return {Date} the modified date object
126 */
127SimileAjax.DateTime.setIso8601Time = function (dateObject, string) {
128    /*
129     *  This function has been adapted from dojo.date, v.0.3.0
130     *  http://dojotoolkit.org/.
131     */
132    
133    var d = string.match(SimileAjax.DateTime._timeRegexp);
134    if(!d) {
135        SimileAjax.Debug.warn("Invalid time string: " + string);
136        return false;
137    }
138    var hours = d[1];
139    var mins = Number((d[3]) ? d[3] : 0);
140    var secs = (d[5]) ? d[5] : 0;
141    var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0;
142
143    dateObject.setUTCHours(hours);
144    dateObject.setUTCMinutes(mins);
145    dateObject.setUTCSeconds(secs);
146    dateObject.setUTCMilliseconds(ms);
147    
148    return dateObject;
149};
150
151/**
152 * The timezone offset in minutes in the user's browser.
153 * @type Number
154 */
155SimileAjax.DateTime.timezoneOffset = new Date().getTimezoneOffset();
156
157/**
158 * Takes a date object and a string containing an ISO 8601 date and time and 
159 * sets the date object using information parsed from the string.
160 *
161 * @param {Date} dateObject the date object to modify
162 * @param {String} string an ISO 8601 string to parse
163 * @return {Date} the modified date object
164 */
165SimileAjax.DateTime.setIso8601 = function (dateObject, string){
166    /*
167     *  This function has been adapted from dojo.date, v.0.3.0
168     *  http://dojotoolkit.org/.
169     */
170     
171    var offset = null;
172    var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T");
173    
174    SimileAjax.DateTime.setIso8601Date(dateObject, comps[0]);
175    if (comps.length == 2) { 
176        // first strip timezone info from the end
177        var d = comps[1].match(SimileAjax.DateTime._timezoneRegexp);
178        if (d) {
179            if (d[0] == 'Z') {
180                offset = 0;
181            } else {
182                offset = (Number(d[3]) * 60) + Number(d[5]);
183                offset *= ((d[2] == '-') ? 1 : -1);
184            }
185            comps[1] = comps[1].substr(0, comps[1].length - d[0].length);
186        }
187
188        SimileAjax.DateTime.setIso8601Time(dateObject, comps[1]); 
189    }
190    if (offset == null) {
191        offset = dateObject.getTimezoneOffset(); // local time zone if no tz info
192    }
193    dateObject.setTime(dateObject.getTime() + offset * 60000);
194    
195    return dateObject;
196};
197
198/**
199 * Takes a string containing an ISO 8601 date and returns a newly instantiated
200 * date object with the parsed date and time information from the string.
201 *
202 * @param {String} string an ISO 8601 string to parse
203 * @return {Date} a new date object created from the string
204 */
205SimileAjax.DateTime.parseIso8601DateTime = function (string) {
206    try {
207        return SimileAjax.DateTime.setIso8601(new Date(0), string);
208    } catch (e) {
209        return null;
210    }
211};
212
213/**
214 * Takes a string containing a Gregorian date and time and returns a newly
215 * instantiated date object with the parsed date and time information from the
216 * string.  If the param is actually an instance of Date instead of a string, 
217 * simply returns the given date instead.
218 *
219 * @param {Object} o an object, to either return or parse as a string
220 * @return {Date} the date object
221 */
222SimileAjax.DateTime.parseGregorianDateTime = function(o) {
223    if (o == null) {
224        return null;
225    } else if (o instanceof Date) {
226        return o;
227    }
228    
229    var s = o.toString();
230    if (s.length > 0 && s.length < 8) {
231        var space = s.indexOf(" ");
232        if (space > 0) {
233            var year = parseInt(s.substr(0, space));
234            var suffix = s.substr(space + 1);
235            if (suffix.toLowerCase() == "bc") {
236                year = 1 - year;
237            }
238        } else {
239            var year = parseInt(s);
240        }
241            
242        var d = new Date(0);
243        d.setUTCFullYear(year);
244        
245        return d;
246    }
247    
248    try {
249        return new Date(Date.parse(s));
250    } catch (e) {
251        return null;
252    }
253};
254
255/**
256 * Rounds date objects down to the nearest interval or multiple of an interval.
257 * This method modifies the given date object, converting it to the given
258 * timezone if specified.
259 * 
260 * @param {Date} date the date object to round
261 * @param {Number} intervalUnit a constant, integer index specifying an 
262 *   interval, e.g. SimileAjax.DateTime.HOUR
263 * @param {Number} timeZone a timezone shift, given in hours
264 * @param {Number} multiple a multiple of the interval to round by
265 * @param {Number} firstDayOfWeek an integer specifying the first day of the
266 *   week, 0 corresponds to Sunday, 1 to Monday, etc.
267 */
268SimileAjax.DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
269    var timeShift = timeZone * 
270        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
271        
272    var date2 = new Date(date.getTime() + timeShift);
273    var clearInDay = function(d) {
274        d.setUTCMilliseconds(0);
275        d.setUTCSeconds(0);
276        d.setUTCMinutes(0);
277        d.setUTCHours(0);
278    };
279    var clearInYear = function(d) {
280        clearInDay(d);
281        d.setUTCDate(1);
282        d.setUTCMonth(0);
283    };
284    
285    switch(intervalUnit) {
286    case SimileAjax.DateTime.MILLISECOND:
287        var x = date2.getUTCMilliseconds();
288        date2.setUTCMilliseconds(x - (x % multiple));
289        break;
290    case SimileAjax.DateTime.SECOND:
291        date2.setUTCMilliseconds(0);
292        
293        var x = date2.getUTCSeconds();
294        date2.setUTCSeconds(x - (x % multiple));
295        break;
296    case SimileAjax.DateTime.MINUTE:
297        date2.setUTCMilliseconds(0);
298        date2.setUTCSeconds(0);
299        
300        var x = date2.getUTCMinutes();
301        date2.setTime(date2.getTime() - 
302            (x % multiple) * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
303        break;
304    case SimileAjax.DateTime.HOUR:
305        date2.setUTCMilliseconds(0);
306        date2.setUTCSeconds(0);
307        date2.setUTCMinutes(0);
308        
309        var x = date2.getUTCHours();
310        date2.setUTCHours(x - (x % multiple));
311        break;
312    case SimileAjax.DateTime.DAY:
313        clearInDay(date2);
314        break;
315    case SimileAjax.DateTime.WEEK:
316        clearInDay(date2);
317        var d = (date2.getUTCDay() + 7 - firstDayOfWeek) % 7;
318        date2.setTime(date2.getTime() - 
319            d * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY]);
320        break;
321    case SimileAjax.DateTime.MONTH:
322        clearInDay(date2);
323        date2.setUTCDate(1);
324        
325        var x = date2.getUTCMonth();
326        date2.setUTCMonth(x - (x % multiple));
327        break;
328    case SimileAjax.DateTime.YEAR:
329        clearInYear(date2);
330        
331        var x = date2.getUTCFullYear();
332        date2.setUTCFullYear(x - (x % multiple));
333        break;
334    case SimileAjax.DateTime.DECADE:
335        clearInYear(date2);
336        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
337        break;
338    case SimileAjax.DateTime.CENTURY:
339        clearInYear(date2);
340        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
341        break;
342    case SimileAjax.DateTime.MILLENNIUM:
343        clearInYear(date2);
344        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
345        break;
346    }
347    
348    date.setTime(date2.getTime() - timeShift);
349};
350
351/**
352 * Rounds date objects up to the nearest interval or multiple of an interval.
353 * This method modifies the given date object, converting it to the given
354 * timezone if specified.
355 * 
356 * @param {Date} date the date object to round
357 * @param {Number} intervalUnit a constant, integer index specifying an 
358 *   interval, e.g. SimileAjax.DateTime.HOUR
359 * @param {Number} timeZone a timezone shift, given in hours
360 * @param {Number} multiple a multiple of the interval to round by
361 * @param {Number} firstDayOfWeek an integer specifying the first day of the
362 *   week, 0 corresponds to Sunday, 1 to Monday, etc.
363 * @see SimileAjax.DateTime.roundDownToInterval
364 */
365SimileAjax.DateTime.roundUpToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
366    var originalTime = date.getTime();
367    SimileAjax.DateTime.roundDownToInterval(date, intervalUnit, timeZone, multiple, firstDayOfWeek);
368    if (date.getTime() < originalTime) {
369        date.setTime(date.getTime() + 
370            SimileAjax.DateTime.gregorianUnitLengths[intervalUnit] * multiple);
371    }
372};
373
374/**
375 * Increments a date object by a specified interval, taking into
376 * consideration the timezone.
377 *
378 * @param {Date} date the date object to increment
379 * @param {Number} intervalUnit a constant, integer index specifying an 
380 *   interval, e.g. SimileAjax.DateTime.HOUR
381 * @param {Number} timeZone the timezone offset in hours
382 */
383SimileAjax.DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
384    timeZone = (typeof timeZone == 'undefined') ? 0 : timeZone;
385
386    var timeShift = timeZone * 
387        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
388        
389    var date2 = new Date(date.getTime() + timeShift);
390
391    switch(intervalUnit) {
392    case SimileAjax.DateTime.MILLISECOND:
393        date2.setTime(date2.getTime() + 1)
394        break;
395    case SimileAjax.DateTime.SECOND:
396        date2.setTime(date2.getTime() + 1000);
397        break;
398    case SimileAjax.DateTime.MINUTE:
399        date2.setTime(date2.getTime() + 
400            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
401        break;
402    case SimileAjax.DateTime.HOUR:
403        date2.setTime(date2.getTime() + 
404            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
405        break;
406    case SimileAjax.DateTime.DAY:
407        date2.setUTCDate(date2.getUTCDate() + 1);
408        break;
409    case SimileAjax.DateTime.WEEK:
410        date2.setUTCDate(date2.getUTCDate() + 7);
411        break;
412    case SimileAjax.DateTime.MONTH:
413        date2.setUTCMonth(date2.getUTCMonth() + 1);
414        break;
415    case SimileAjax.DateTime.YEAR:
416        date2.setUTCFullYear(date2.getUTCFullYear() + 1);
417        break;
418    case SimileAjax.DateTime.DECADE:
419        date2.setUTCFullYear(date2.getUTCFullYear() + 10);
420        break;
421    case SimileAjax.DateTime.CENTURY:
422        date2.setUTCFullYear(date2.getUTCFullYear() + 100);
423        break;
424    case SimileAjax.DateTime.MILLENNIUM:
425        date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
426        break;
427    }
428
429    date.setTime(date2.getTime() - timeShift);
430};
431
432/**
433 * Returns a new date object with the given time offset removed.
434 *
435 * @param {Date} date the starting date
436 * @param {Number} timeZone a timezone specified in an hour offset to remove
437 * @return {Date} a new date object with the offset removed
438 */
439SimileAjax.DateTime.removeTimeZoneOffset = function(date, timeZone) {
440    return new Date(date.getTime() + 
441        timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
442};
443
444/**
445 * Returns the timezone of the user's browser.
446 *
447 * @return {Number} the timezone in the user's locale in hours
448 */
449SimileAjax.DateTime.getTimezone = function() {
450    var d = new Date().getTimezoneOffset();
451    return d / -60;
452};