diff --git a/src/Calendar.js b/src/Calendar.js index d32595b..6f3536a 100644 --- a/src/Calendar.js +++ b/src/Calendar.js @@ -22,8 +22,8 @@ function Calendar(element, options, eventSources) { t.today = today; t.gotoDate = gotoDate; t.incrementDate = incrementDate; - t.formatDate = function(format, date) { return formatDate(format, date, options) }; - t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) }; + t.formatDate = function(date, format) { return date.toString(format, options) }; + t.formatDates = function(date1, date2, format) { return date1.toString(date2, format, options) }; t.getDate = getDate; t.getView = getView; t.option = option; @@ -49,7 +49,7 @@ function Calendar(element, options, eventSources) { var absoluteViewElement; var resizeUID = 0; var ignoreWindowResize = 0; - var date = new Date(); + var date = new MightyDate(); var events = []; var _dragElement; @@ -59,7 +59,13 @@ function Calendar(element, options, eventSources) { -----------------------------------------------------------------------------*/ - setYMD(date, options.year, options.month, options.date); + if (options.year) { + date = new MightyDate( + options.year, + options.month || 0, + options.date || 1 + ); + } function render(inc) { @@ -198,7 +204,9 @@ function Calendar(element, options, eventSources) { } var forceEventRender = false; - if (!currentView.start || inc || date < currentView.start || date >= currentView.end) { + if ( + !currentView.start || inc || date.before(currentView.start) || !date.before(currentView.end) + ) { // view must render an entire new date range (and refetch/render events) currentView.render(date, inc || 0); // responsible for clearing events setSize(true); @@ -221,8 +229,8 @@ function Calendar(element, options, eventSources) { elementOuterWidth = element.outerWidth(); header.updateTitle(currentView.title); - var today = new Date(); - if (today >= currentView.start && today < currentView.end) { + var today = new MightyDate(); + if (!today.before(currentView.start) && today.before(currentView.end)) { // within range header.disableButton('today'); }else{ header.enableButton('today'); @@ -391,28 +399,32 @@ function Calendar(element, options, eventSources) { function prevYear() { - addYears(date, -1); + date.addYears(-1, true); renderView(); } function nextYear() { - addYears(date, 1); + date.addYears(1, true); renderView(); } function today() { - date = new Date(); + date = new MightyDate(); renderView(); } function gotoDate(year, month, dateOfMonth) { - if (year instanceof Date) { - date = cloneDate(year); // provided 1 argument, a Date + if (typeof year == 'object') { + date = new MightyDate(year); // provided 1 argument, a Date or MightyDate }else{ - setYMD(date, year, month, dateOfMonth); + date = new MightyDate( + year, + month || 0, + dateOfMonth || 1 + ); } renderView(); } @@ -420,20 +432,20 @@ function Calendar(element, options, eventSources) { function incrementDate(years, months, days) { if (years !== undefined) { - addYears(date, years); + date.addYears(years, true); } if (months !== undefined) { - addMonths(date, months); + date.addMonths(months, true); } if (days !== undefined) { - addDays(date, days); + date.addDays(days); } renderView(); } function getDate() { - return cloneDate(date); + return date.clone(); } diff --git a/src/EventManager.js b/src/EventManager.js index 4d159ab..95bc43c 100644 --- a/src/EventManager.js +++ b/src/EventManager.js @@ -53,7 +53,7 @@ function EventManager(options, _sources) { function isFetchNeeded(start, end) { - return !rangeStart || start < rangeStart || end > rangeEnd; + return !rangeStart || start.before(rangeStart) || end.after(rangeEnd); } @@ -94,7 +94,7 @@ function EventManager(options, _sources) { var fetchers = fc.sourceFetchers; var res; for (i=0; i"; - d = zeroDate(); - maxd = addMinutes(cloneDate(d), maxMinute); - addMinutes(d, minMinute); + d = t.visStart.clone(); + maxd = d.clone().addMinutes(maxMinute); + d.addMinutes(minMinute); slotCnt = 0; - for (i=0; d < maxd; i++) { + for (i=0; d.before(maxd); i++) { minutes = d.getMinutes(); s += "" + @@ -288,7 +288,7 @@ function AgendaView(element, calendar, viewName) { "
 
" + "" + ""; - addMinutes(d, opt('slotMinutes')); + d.addMinutes(opt('slotMinutes')); slotCnt++; } s += @@ -309,13 +309,13 @@ function AgendaView(element, calendar, viewName) { var headCell; var bodyCell; var date; - var today = clearTime(new Date()); + var today = new MightyDate().clearTime(); for (i=0; i= 0) { - addMinutes(d, minMinute + slotIndex * opt('slotMinutes')); + d.addMinutes(minMinute + slotIndex * opt('slotMinutes')); } return d; } function colDate(col) { // returns dates with 00:00:00 - return addDays(cloneDate(t.visStart), col*dis+dit); + return t.visStart.clone().addDays(col*dis+dit); } @@ -605,11 +606,11 @@ function AgendaView(element, calendar, viewName) { // get the Y coordinate of the given time on the given day (both Date objects) function timePosition(day, time) { // both date objects. day holds 00:00 of current day - day = cloneDate(day, true); - if (time < addMinutes(cloneDate(day), minMinute)) { + day = day.clone().clearTime(); + if (time.before(day.clone().addMinutes(minMinute))) { return 0; } - if (time >= addMinutes(cloneDate(day), maxMinute)) { + if (!time.before(day.clone().addMinutes(maxMinute))) { return slotTable.height(); } var slotMinutes = opt('slotMinutes'), @@ -639,11 +640,11 @@ function AgendaView(element, calendar, viewName) { function defaultEventEnd(event) { - var start = cloneDate(event.start); + var start = event.start.clone(); if (event.allDay) { return start; } - return addMinutes(start, opt('defaultEventMinutes')); + return start.addMinutes(opt('defaultEventMinutes')); } @@ -654,16 +655,20 @@ function AgendaView(element, calendar, viewName) { function defaultSelectionEnd(startDate, allDay) { if (allDay) { - return cloneDate(startDate); + return startDate.clone(); } - return addMinutes(cloneDate(startDate), opt('slotMinutes')); + return startDate.clone().addMinutes(opt('slotMinutes')); } function renderSelection(startDate, endDate, allDay) { // only for all-day if (allDay) { if (opt('allDaySlot')) { - renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); + renderDayOverlay( + startDate.clone().clearTime(), + endDate.clone().addDays(1).clearTime(), // TODO: use util + true + ); } }else{ renderSlotSelection(startDate, endDate); @@ -675,7 +680,7 @@ function AgendaView(element, calendar, viewName) { var helperOption = opt('selectHelper'); coordinateGrid.build(); if (helperOption) { - var col = dayDiff(startDate, t.visStart) * dis + dit; + var col = t.visStart.diffDays(startDate) * dis + dit; if (col >= 0 && col < colCnt) { // only works when times are on same day var rect = coordinateGrid.rect(0, col, 0, col, slotContent); // only for horizontal coords var top = timePosition(startDate, startDate); @@ -743,9 +748,9 @@ function AgendaView(element, calendar, viewName) { var d2 = cellDate(cell); dates = [ d1, - addMinutes(cloneDate(d1), opt('slotMinutes')), + d1.clone().addMinutes(opt('slotMinutes')), d2, - addMinutes(cloneDate(d2), opt('slotMinutes')) + d2.clone().addMinutes(opt('slotMinutes')) ].sort(cmp); renderSlotSelection(dates[0], dates[3]); }else{ @@ -755,7 +760,7 @@ function AgendaView(element, calendar, viewName) { $(document).one('mouseup', function(ev) { hoverListener.stop(); if (dates) { - if (+dates[0] == +dates[1]) { + if (dates[0].equals(dates[1])) { reportDayClick(dates[0], false, ev); } reportSelection(dates[0], dates[3], false, ev); @@ -766,7 +771,7 @@ function AgendaView(element, calendar, viewName) { function reportDayClick(date, allDay, ev) { - trigger('dayClick', dayBodyCells[dayOfWeekCol(date.getDay())], date, allDay, ev); + trigger('dayClick', dayBodyCells[dayOfWeekCol(date.getDay())], date.clone(), allDay, ev); } @@ -783,7 +788,7 @@ function AgendaView(element, calendar, viewName) { renderCellOverlay(cell.row, cell.col, cell.row, cell.col); }else{ var d1 = cellDate(cell); - var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes')); + var d2 = d1.clone().addMinutes(opt('defaultEventMinutes')); renderSlotOverlay(d1, d2); } } diff --git a/src/agenda/AgendaWeekView.js b/src/agenda/AgendaWeekView.js index 03d0f7e..fba01b6 100644 --- a/src/agenda/AgendaWeekView.js +++ b/src/agenda/AgendaWeekView.js @@ -19,12 +19,12 @@ function AgendaWeekView(element, calendar) { function render(date, delta) { if (delta) { - addDays(date, delta * 7); + date.addWeeks(delta); } - var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7)); - var end = addDays(cloneDate(start), 7); - var visStart = cloneDate(start); - var visEnd = cloneDate(end); + var start = date.clone().clearTime().addDays(-((date.getDay() - opt('firstDay') + 7) % 7)); + var end = start.clone().addWeeks(1); + var visStart = start.clone(); + var visEnd = end.clone(); var weekends = opt('weekends'); if (!weekends) { skipWeekend(visStart); @@ -32,7 +32,7 @@ function AgendaWeekView(element, calendar) { } t.title = formatDates( visStart, - addDays(cloneDate(visEnd), -1), + visEnd.clone().addDays(-1), opt('titleFormat') ); t.start = start; diff --git a/src/basic/BasicDayView.js b/src/basic/BasicDayView.js index ac9ffca..c9261f6 100644 --- a/src/basic/BasicDayView.js +++ b/src/basic/BasicDayView.js @@ -22,14 +22,14 @@ function BasicDayView(element, calendar) { function render(date, delta) { if (delta) { - addDays(date, delta); + date.addDays(delta); if (!opt('weekends')) { skipWeekend(date, delta < 0 ? -1 : 1); } } t.title = formatDate(date, opt('titleFormat')); - t.start = t.visStart = cloneDate(date, true); - t.end = t.visEnd = addDays(cloneDate(t.start), 1); + t.start = t.visStart = date.clone().clearTime(); + t.end = t.visEnd = t.start.clone().addDays(1); renderBasic(1, 1, 1, false); } diff --git a/src/basic/BasicEventRenderer.js b/src/basic/BasicEventRenderer.js index 3c1acc6..d59f604 100644 --- a/src/basic/BasicEventRenderer.js +++ b/src/basic/BasicEventRenderer.js @@ -53,8 +53,8 @@ function BasicEventRenderer() { function compileSegs(events) { var rowCnt = getRowCnt(), colCnt = getColCnt(), - d1 = cloneDate(t.visStart), - d2 = addDays(cloneDate(d1), colCnt), + d1 = t.visStart.clone(), + d2 = d1.clone().addDays(colCnt), visEventsEnds = $.map(events, exclEndDay), i, row, j, level, @@ -71,8 +71,8 @@ function BasicEventRenderer() { segs.push(seg); } } - addDays(d1, 7); - addDays(d2, 7); + d1.addWeeks(1); + d2.addWeeks(1); } return segs; } @@ -113,8 +113,8 @@ function BasicEventRenderer() { //setOverflowHidden(true); dayDelta = rowDelta*7 + colDelta * (opt('isRTL') ? -1 : 1); renderDayOverlay( - addDays(cloneDate(event.start), dayDelta), - addDays(exclEndDay(event), dayDelta) + event.start.clone().addDays(dayDelta).clearTime(), + exclEndDay(event).addDays(dayDelta).clearTime() ); }else{ //setOverflowHidden(false); diff --git a/src/basic/BasicView.js b/src/basic/BasicView.js index 9e08f6d..de12078 100644 --- a/src/basic/BasicView.js +++ b/src/basic/BasicView.js @@ -184,7 +184,7 @@ function BasicView(element, calendar, viewName) { function updateCells(firstTime) { var dowDirty = firstTime || rowCnt == 1; // could the cells' day-of-weeks need updating? var month = t.start.getMonth(); - var today = clearTime(new Date()); + var today = new MightyDate().clearTime(); var cell; var date; var row; @@ -206,7 +206,7 @@ function BasicView(element, calendar, viewName) { }else{ cell.addClass('fc-other-month'); } - if (+date == +today) { + if (date.equals(today)) { cell.addClass(tm + '-state-highlight fc-today'); }else{ cell.removeClass(tm + '-state-highlight fc-today'); @@ -296,29 +296,30 @@ function BasicView(element, calendar, viewName) { function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive + // TODO: get straight whether caller needs to do clearTime or not if (refreshCoordinateGrid) { coordinateGrid.build(); } - var rowStart = cloneDate(t.visStart); - var rowEnd = addDays(cloneDate(rowStart), colCnt); + var rowStart = t.visStart.clone(); + var rowEnd = rowStart.clone().addDays(colCnt); for (var i=0; i)), return null instead - return null; -} - - -function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false - // derived from http://delete.me.uk/2005/03/iso8601.html - // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html - var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/); - if (!m) { - return null; - } - var date = new Date(m[1], 0, 1); - if (ignoreTimezone || !m[14]) { - var check = new Date(m[1], 0, 1, 9, 0); - if (m[3]) { - date.setMonth(m[3] - 1); - check.setMonth(m[3] - 1); - } - if (m[5]) { - date.setDate(m[5]); - check.setDate(m[5]); - } - fixDate(date, check); - if (m[7]) { - date.setHours(m[7]); - } - if (m[8]) { - date.setMinutes(m[8]); - } - if (m[10]) { - date.setSeconds(m[10]); - } - if (m[12]) { - date.setMilliseconds(Number("0." + m[12]) * 1000); - } - fixDate(date, check); - }else{ - date.setUTCFullYear( - m[1], - m[3] ? m[3] - 1 : 0, - m[5] || 1 - ); - date.setUTCHours( - m[7] || 0, - m[8] || 0, - m[10] || 0, - m[12] ? Number("0." + m[12]) * 1000 : 0 - ); - var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0); - offset *= m[15] == '-' ? 1 : -1; - date = new Date(+date + (offset * 60 * 1000)); - } - return date; -} - - -function parseTime(s) { // returns minutes since start of day - if (typeof s == 'number') { // an hour - return s * 60; - } - if (typeof s == 'object') { // a Date object - return s.getHours() * 60 + s.getMinutes(); - } - var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/); - if (m) { - var h = parseInt(m[1], 10); - if (m[3]) { - h %= 12; - if (m[3].toLowerCase().charAt(0) == 'p') { - h += 12; - } - } - return h * 60 + (m[2] ? parseInt(m[2], 10) : 0); - } -} - - - -/* Date Formatting ------------------------------------------------------------------------------*/ -// TODO: use same function formatDate(date, [date2], format, [options]) - - -function formatDate(date, format, options) { - return formatDates(date, null, format, options); -} - - -function formatDates(date1, date2, format, options) { - options = options || defaults; - var date = date1, - otherDate = date2, - i, len = format.length, c, - i2, formatter, - res = ''; - for (i=0; ii; i2--) { - if (formatter = dateFormatters[format.substring(i, i2)]) { - if (date) { - res += formatter(date, options); - } - i = i2 - 1; - break; - } - } - if (i2 == i) { - if (date) { - res += c; - } - } - } - } - return res; -}; - - -var dateFormatters = { - s : function(d) { return d.getSeconds() }, - ss : function(d) { return zeroPad(d.getSeconds()) }, - m : function(d) { return d.getMinutes() }, - mm : function(d) { return zeroPad(d.getMinutes()) }, - h : function(d) { return d.getHours() % 12 || 12 }, - hh : function(d) { return zeroPad(d.getHours() % 12 || 12) }, - H : function(d) { return d.getHours() }, - HH : function(d) { return zeroPad(d.getHours()) }, - d : function(d) { return d.getDate() }, - dd : function(d) { return zeroPad(d.getDate()) }, - ddd : function(d,o) { return o.dayNamesShort[d.getDay()] }, - dddd: function(d,o) { return o.dayNames[d.getDay()] }, - M : function(d) { return d.getMonth() + 1 }, - MM : function(d) { return zeroPad(d.getMonth() + 1) }, - MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] }, - MMMM: function(d,o) { return o.monthNames[d.getMonth()] }, - yy : function(d) { return (d.getFullYear()+'').substring(2) }, - yyyy: function(d) { return d.getFullYear() }, - t : function(d) { return d.getHours() < 12 ? 'a' : 'p' }, - tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' }, - T : function(d) { return d.getHours() < 12 ? 'A' : 'P' }, - TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' }, - u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") }, - S : function(d) { - var date = d.getDate(); - if (date > 10 && date < 20) { - return 'th'; - } - return ['st', 'nd', 'rd'][date%10-1] || 'th'; - } -}; - - diff --git a/src/defaults.js b/src/defaults.js index 9cb9982..354d609 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -42,10 +42,6 @@ var defaults = { // locale isRTL: false, firstDay: 0, - monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], - monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], - dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], - dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], buttonText: { prev: ' ◄ ', next: ' ► ', diff --git a/src/gcal/gcal.js b/src/gcal/gcal.js index 55b5b10..d1c86dd 100644 --- a/src/gcal/gcal.js +++ b/src/gcal/gcal.js @@ -13,9 +13,7 @@ var fc = $.fullCalendar; -var formatDate = fc.formatDate; -var parseISO8601 = fc.parseISO8601; -var addDays = fc.addDays; +var parseDate = fc.parseDate; var applyAll = fc.applyAll; @@ -42,8 +40,8 @@ function transformOptions(sourceOptions, start, end) { var success = sourceOptions.success; var data = $.extend({}, sourceOptions.data || {}, { - 'start-min': formatDate(start, 'u'), - 'start-max': formatDate(end, 'u'), + 'start-min': start.toString('u'), + 'start-max': end.toString('u'), 'singleevents': true, 'max-results': 9999 }); @@ -64,8 +62,8 @@ function transformOptions(sourceOptions, start, end) { if (data.feed.entry) { $.each(data.feed.entry, function(i, entry) { var startStr = entry['gd$when'][0]['startTime']; - var start = parseISO8601(startStr, true); - var end = parseISO8601(entry['gd$when'][0]['endTime'], true); + var start = parseDate(startStr, true); // true for ignoreTimezone + var end = parseDate(entry['gd$when'][0]['endTime'], true); // true for ignoreTimezone var allDay = startStr.indexOf('T') == -1; var url; $.each(entry.link, function(i, link) { @@ -77,7 +75,7 @@ function transformOptions(sourceOptions, start, end) { } }); if (allDay) { - addDays(end, -1); // make inclusive + end.addDays(-1); // make inclusive } events.push({ id: entry['gCal$uid']['value'], diff --git a/src/mightydate.js b/src/mightydate.js new file mode 100644 index 0000000..4ef0b02 --- /dev/null +++ b/src/mightydate.js @@ -0,0 +1,795 @@ +/** + * @preserve MightyDate v0.4 + * http://github.com/arshaw/mightydate + * + * Copyright 2011, Adam Shaw + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Date: Sun May 8 00:19:07 2011 -0700 + */ + + +var MightyDate = (function(Date, undefined) { + + +/** @const */ var FULLYEAR_INDEX = 0; +/** @const */ var MONTH_INDEX = 1; +/** @const */ var DATE_INDEX = 2; +/** @const */ var HOURS_INDEX = 3; +/** @const */ var MINUTES_INDEX = 4; +/** @const */ var SECONDS_INDEX = 5; +/** @const */ var MILLISECONDS_INDEX = 6; +/** @const */ var DAY_INDEX = 7; +/** @const */ var YEAR_INDEX = 8; +/** @const */ var WEEK_INDEX = 9; + +/** @const */ var DAY_MS = 86400000; + +var methodSubjects = [ + 'FullYear', // 0 + 'Month', // 1 + 'Date', // 2 + 'Hours', // 3 + 'Minutes', // 4 + 'Seconds', // 5 + 'Milliseconds', // 6 + 'Day', // 7 + 'Year' // 8 +]; +var subjectPlurals = [ + 'Years', // 0 + 'Months', // 1 + 'Days' // 2 +]; +var otherGetters = [ + 'getTimezoneOffset', + 'getTime', + 'valueOf', + 'toDateString', + 'toTimeString', + 'toLocaleString', + 'toLocaleDateString', + 'toLocaleTimeString', + 'toJSON' +]; +var formatStringRE = new RegExp( + "(" + // 1 + "\\((" + "('.*?'|\\(.*?\\)|.)*?" + ")\\)" + "|" + // 2 3 + "\\{(" + "('.*?'|.)*?" + ")\\}" + "|" + // 4 5 + "\\[(" + "('.*?'|.)*?" + ")\\]" + "|" + // 6 7 + "'(.*?)'" + "|" + // 8 + "(.[^({[']*)" + // 9 + ")" + + "(.*)" // 10 +); +var UTC = Date.UTC; +var proto = MightyDate.prototype; +var i; +var noopSignal = {}; + + + +/* Constructor +------------------------------------------------------------------------------*/ + + +function MightyDate() { + if (!(this instanceof MightyDate)) { + // when MightyDate is called as a method. TODO: write test + return init(new MightyDate(noopSignal), arguments); + } + init(this, arguments); +} + + +function init(mightyDate, args) { + var len = args.length; + if (!len) { + // set to now + setLocalDate(mightyDate, new Date()); + } + else if (len == 1) { + var arg = args[0]; + if (isNumber(arg)) { + setLocalDate(mightyDate, new Date(arg)); + } + else if (isString(arg)) { + _setUniversalDate(mightyDate, new Date(0)); + parse(mightyDate, arg); + } + else if (arg instanceof Date) { + setLocalDate(mightyDate, new Date(+arg)); + } + else if (arg instanceof MightyDate) { + _setUniversalDate(mightyDate, new Date(+getUniversalDate(arg))); + } + else if (arg != noopSignal) { + _setUniversalDate(mightyDate, NaN); // make it an Invalid Date. TODO: write test + } + } + else { + _setUniversalDate(mightyDate, new Date(UTC.apply(Date, args))); + } + return mightyDate; +} + + + +/* Standard Methods + Adding/Diffing Methods +-----------------------------------------------------------------------------*/ + + +for (i=0; i= nextWeek1) { + week1 = nextWeek1; + } + } + return Math.floor(Math.round((d - week1) / DAY_MS) / 7) + 1; +} + + +function getWeek1(year) { + var d = new Date(UTC(year, 0, 4)); + d.setUTCDate(d.getUTCDate() - (d.getUTCDay() + 6) % 7); + return d; +} + + + +/* Non-Standard Methods +-----------------------------------------------------------------------------*/ + + +proto.equals = function(date) { + return +getUniversalDate(this) == +extractUniversalDate(date); +}; + + +proto.before = function(date) { + return getUniversalDate(this) < extractUniversalDate(date); +}; + + +proto.after = function(date) { + return getUniversalDate(this) > extractUniversalDate(date); +}; + + +proto.valid = function() { + return !isNaN(+getUniversalDate(this)); +}; + + +proto.clearTime = function() { + return this.setHours(0, 0, 0, 0); +}; + + +proto.clone = function() { + return new MightyDate(this); +}; + + +proto.toDate = function() { + return new Date(+this); +}; + + + +/* Parsing +-----------------------------------------------------------------------------*/ + + +MightyDate.parsers = [ + parseISO +]; + + +MightyDate.parse = function(str) { + return +new MightyDate(''+str); +}; + + +function parse(date, str) { + var parsers = MightyDate.parsers; + for (var i=0; i=0; j--) { + uniqueness.push(_getField(date, j)); + } + } + return _getField(date, i); + } + + while (formatString) { + match = formatStringRE.exec(formatString); + if (match[9] !== undefined) { + // tokens + var tokenSoup = match[9]; + var tokenSoupLength = tokenSoup.length; + var tokenStart = 0; + var tokenEnd = tokenSoupLength; + while (tokenStart < tokenSoupLength) { + tokenReplacement = undefined; + // find the longest formatter starting at tokenStart + while (tokenEnd > tokenStart) { + tokenReplacement = getTokenReplacement( + tokenSoup.substring(tokenStart, tokenEnd), // the potential token + isUTC, date, getField, getSetting, uniqueness + ); + if (tokenReplacement !== undefined) { + output += tokenReplacement; + tokenStart = tokenEnd; // continue looking after + break; + } + tokenEnd--; + } + if (tokenReplacement === undefined) { + // swallow single character and continue after + output += formatString.charAt(tokenStart); + tokenStart++; + } + tokenEnd = tokenSoupLength; + } + } + else if (match[2] !== undefined) { + // only if non-zero + subOutput = _format(isUTC, date, otherDate, match[2], getSetting); + if (Number(subOutput.replace(/\D/g, ''))) { + output += subOutput; + } + } + else if (match[4] !== undefined) { + // switch to other date + if (otherDate) { + output += _format(isUTC, otherDate, date, match[4], getSetting); + } + } + else if (match[6] !== undefined) { + // only if different + var otherSubUniqueness = []; + var otherSubOutput = _format(isUTC, otherDate, date, match[6], getSetting, otherSubUniqueness); + var subUniqueness = []; + subOutput = _format(isUTC, date, otherDate, match[6], getSetting, subUniqueness); + if (subOutput != otherSubOutput || subUniqueness.join() != otherSubUniqueness.join()) { + output += subOutput; + } + } + else if (match[8] !== undefined) { + if (match[8]) { + output += match[8]; // quoted text + }else{ + output += "'"; // escaped quote + } + } + formatString = match[10]; + } + + return output; +} + + +function getTokenReplacement(token, isUTC, date, getField, getSetting, uniqueness) { + var formatter = MightyDate.formatters[token]; + if (formatter) { + if (isString(formatter)) { + return _format(isUTC, date, null, formatter, getSetting, uniqueness); + } + return formatter(date, getSetting, isUTC); + } + switch (token) { + case 's' : return getField(SECONDS_INDEX); + case 'ss' : return zeroPad(getField(SECONDS_INDEX)); + case 'm' : return getField(MINUTES_INDEX); + case 'mm' : return zeroPad(getField(MINUTES_INDEX)); + case 'h' : return getField(HOURS_INDEX) % 12 || 12; + case 'hh' : return zeroPad(getField(HOURS_INDEX) % 12 || 12); + case 'H' : return getField(HOURS_INDEX); + case 'HH' : return zeroPad(getField(HOURS_INDEX)); + case 'd' : return getField(DATE_INDEX); + case 'dd' : return zeroPad(getField(DATE_INDEX)); + case 'ddd' : return getSetting('dayNamesShort')[getField(DAY_INDEX)]; + case 'dddd' : return getSetting('dayNames')[getField(DAY_INDEX)]; + case 'M' : return getField(MONTH_INDEX) + 1; + case 'MM' : return zeroPad(getField(MONTH_INDEX) + 1); + case 'MMM' : return getSetting('monthNamesShort')[getField(MONTH_INDEX)]; + case 'MMMM' : return getSetting('monthNames')[getField(MONTH_INDEX)]; + case 'yy' : return (getField(FULLYEAR_INDEX)+'').substring(2); + case 'yyyy' : return getField(FULLYEAR_INDEX); + case 't' : + case 'tt' : + case 'T' : + case 'TT' : + var s = getField(HOURS_INDEX) < 12 ? getSetting('amDesignator') : getSetting('pmDesignator'); + if (token.length == 1) s = s.substr(0, 1); + if (token > 'TT') s = s.toLowerCase(); // token == 't' || token == 'tt' + return s; + case 'K' : if (isUTC) return 'Z'; + case 'z' : + case 'zz' : + case 'zzz' : + var tzo = date.getTimezoneOffset(); + var sign = tzo < 0 ? '+' : '-'; + var hours = Math.floor((tzo = Math.abs(tzo)) / 60); + var minutes = tzo % 60; + return sign + + (token == 'z' ? hours : zeroPad(hours)) + + ((token == 'z' || token == 'zz') ? '' : ':' + zeroPad(minutes)); + case 'S' : + var d = getField(DATE_INDEX); + if (d > 10 && d < 20) return 'th'; + return ['st', 'nd', 'rd'][d % 10 - 1] || 'th'; + case 'W' : + return _getWeek( + getField(FULLYEAR_INDEX), + getField(MONTH_INDEX), + getField(DATE_INDEX) + ); + case 'WW' : + return zeroPad( + _getWeek( + getField(FULLYEAR_INDEX), + getField(MONTH_INDEX), + getField(DATE_INDEX) + ) + ); + } +} + + +function getLocalField(date, i) { + return date['get' + methodSubjects[i]](); +} + + +function getUTCField(date, i) { + return date['getUTC' + methodSubjects[i]](); +} + + + +/* Misc Class Methods +-----------------------------------------------------------------------------*/ + + +MightyDate.now = Date.now; + + +MightyDate.UTC = UTC; + + +MightyDate.getDaysInMonth = getDaysInMonth; + + + +/* Date Utilities +--------------------------------------------------------------------------------*/ + + +function getDaysInMonth(year, month) { + return 32 - new Date(Date.UTC(year, month, 32)).getUTCDate(); +} + + +function extractUniversalDate(date) { + if (date instanceof MightyDate) { + return getUniversalDate(date); + } + else if (date instanceof Date) { + return localToUniversal(date); + } + return getUniversalDate(new MightyDate(date)); +} + + +function localToUniversal(localDate) { + return new Date(UTC( + localDate.getFullYear(), + localDate.getMonth(), + localDate.getDate(), + localDate.getHours(), + localDate.getMinutes(), + localDate.getSeconds(), + localDate.getMilliseconds() + )); +} + + +function universalToLocal(universalDate) { + return new Date( + universalDate.getUTCFullYear(), + universalDate.getUTCMonth(), + universalDate.getUTCDate(), + universalDate.getUTCHours(), + universalDate.getUTCMinutes(), + universalDate.getUTCSeconds(), + universalDate.getUTCMilliseconds() + ); +} + + + +/* General Utilities +--------------------------------------------------------------------------------*/ + + +function zeroPad(n) { + return (n < 10 ? '0' : '') + n; +} + + +function isString(x) { + return typeof x == 'string'; +} + + +function isNumber(x) { + return typeof x == 'number'; +} + + +function isBoolean(x) { + return typeof x == 'boolean'; +} + + + +/* Internal Utilities +---------------------------------------------------------------------------------*/ + +// _ is universal date +// $ is local date + + +function getUniversalDate(mightyDate) { + return mightyDate._; +} + + +function setUniversalDate(mightyDate, universalDate) { + _setUniversalDate(mightyDate, universalDate); + clearLocalDate(mightyDate); +} + + +function _setUniversalDate(mightyDate, universalDate) { + mightyDate._ = universalDate; +} + + +function getLocalDate(mightyDate) { + if (mightyDate.$ === undefined) { + mightyDate.$ = universalToLocal(mightyDate._); + } + return mightyDate.$; +} + + +function setLocalDate(mightyDate, localDate) { + mightyDate.$ = localDate; + mightyDate._ = localToUniversal(localDate); +} + + +function clearLocalDate(mightyDate) { + delete mightyDate.$; +} + + + +return MightyDate; + +})(Date); diff --git a/src/util.js b/src/util.js index b16f064..54941ff 100644 --- a/src/util.js +++ b/src/util.js @@ -1,5 +1,85 @@ fc.applyAll = applyAll; +fc.Date = MightyDate; +fc.parseDate = parseDate; +fc.parseISO8601 = parseISO8601; +fc.formatDate = formatDate; +fc.formatDates = formatDates; + + +var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; + + +/* Date +--------------------------------------------------------------------------------*/ + + +function skipWeekend(date, inc, excl) { + inc = inc || 1; + while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) { + date.addDays(inc); + } + return date; +} + + +function parseTime(s) { // returns minutes since start of day + if (typeof s == 'number') { // an hour + return s * 60; + } + if (typeof s == 'object') { // a Date object + return s.getHours() * 60 + s.getMinutes(); + } + var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/); + if (m) { + var h = parseInt(m[1], 10); + if (m[3]) { + h %= 12; + if (m[3].toLowerCase().charAt(0) == 'p') { + h += 12; + } + } + return h * 60 + (m[2] ? parseInt(m[2], 10) : 0); + } +} + + +// TODO: change ignoreTimezone default +function parseDate(input, ignoreTimezone) { // ignoreTimezone DEFAULTS TO TRUE!!! :( + // handles more than just strings + if (ignoreTimezone === undefined) { + ignoreTimezone = true; + } + if (ignoreTimezone && typeof input == 'string') { + input = stripTimezone(input); + } + return new MightyDate(input); +} + + +// TODO: deprecate +function parseISO8601(input, ignoreTimezone) { // ignoreTimezone DEFAULTS TO FALSE + if (ignoreTimezone === undefined) { + ignoreTimezone = false; + } + return parseDate(input, ignoreTimezone); +} + + +function stripTimezone(s) { + return s.replace(/^(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?)(.*)$/, '$1'); +} + + +function formatDate(date, formatString, settings) { + return new MightyDate(date).toString(formatString, settings); +} + + +function formatDates(date1, date2, formatString, settings) { + return new MightyDate(date1).toString(date2, formatString, settings); +} + /* Event Date Math @@ -10,24 +90,27 @@ function exclEndDay(event) { if (event.end) { return _exclEndDay(event.end, event.allDay); }else{ - return addDays(cloneDate(event.start), 1); + return event.start.clone().addDays(1).clearTime(); } } function _exclEndDay(end, allDay) { - end = cloneDate(end); - return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end); + end = end.clone(); + if (allDay || end.getHours() || end.getMinutes()) { + end.addDays(1); + } + return end.clearTime(); } function segCmp(a, b) { - return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start); + return (b.msLength - a.msLength) * 100 + b.event.start.diffMilliseconds(a.event.start); } function segsCollide(seg1, seg2) { - return seg1.end > seg2.start && seg1.start < seg2.end; + return seg1.end.after(seg2.start) && seg1.start.before(seg2.end); } @@ -47,16 +130,16 @@ function sliceSegs(events, visEventEnds, start, end) { event = events[i]; eventStart = event.start; eventEnd = visEventEnds[i]; - if (eventEnd > start && eventStart < end) { - if (eventStart < start) { - segStart = cloneDate(start); + if (eventEnd.after(start) && eventStart.before(end)) { + if (eventStart.before(start)) { + segStart = start.clone(); isStart = false; }else{ segStart = eventStart; isStart = true; } - if (eventEnd > end) { - segEnd = cloneDate(end); + if (eventEnd.after(end)) { + segEnd = end.clone(); isEnd = false; }else{ segEnd = eventEnd; @@ -68,7 +151,7 @@ function sliceSegs(events, visEventEnds, start, end) { end: segEnd, isStart: isStart, isEnd: isEnd, - msLength: segEnd - segStart + msLength: segStart.diffMilliseconds(segEnd) }); } }