/** * Flotr Date */ Flotr.Date = { set : function (date, name, mode, value) { mode = mode || 'UTC'; name = 'set' + (mode === 'UTC' ? 'UTC' : '') + name; date[name](value); }, get : function (date, name, mode) { mode = mode || 'UTC'; name = 'get' + (mode === 'UTC' ? 'UTC' : '') + name; return date[name](); }, format: function(d, format, mode) { if (!d) return; // We should maybe use an "official" date format spec, like PHP date() or ColdFusion // http://fr.php.net/manual/en/function.date.php // http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_c-d_29.html var get = this.get, tokens = { h: get(d, 'Hours', mode).toString(), H: leftPad(get(d, 'Hours', mode)), M: leftPad(get(d, 'Minutes', mode)), S: leftPad(get(d, 'Seconds', mode)), s: get(d, 'Milliseconds', mode), d: get(d, 'Date', mode).toString(), m: (get(d, 'Month', mode) + 1).toString(), y: get(d, 'FullYear', mode).toString(), b: Flotr.Date.monthNames[get(d, 'Month', mode)] }; function leftPad(n){ n += ''; return n.length == 1 ? "0" + n : n; } var r = [], c, escape = false; for (var i = 0; i < format.length; ++i) { c = format.charAt(i); if (escape) { r.push(tokens[c] || c); escape = false; } else if (c == "%") escape = true; else r.push(c); } return r.join(''); }, getFormat: function(time, span) { var tu = Flotr.Date.timeUnits; if (time < tu.second) return "%h:%M:%S.%s"; else if (time < tu.minute) return "%h:%M:%S"; else if (time < tu.day) return (span < 2 * tu.day) ? "%h:%M" : "%b %d %h:%M"; else if (time < tu.month) return "%b %d"; else if (time < tu.year) return (span < tu.year) ? "%b" : "%b %y"; else return "%y"; }, formatter: function (v, axis) { var options = axis.options, scale = Flotr.Date.timeUnits[options.timeUnit], d = new Date(v * scale); // first check global format if (axis.options.timeFormat) return Flotr.Date.format(d, options.timeFormat, options.timeMode); var span = (axis.max - axis.min) * scale, t = axis.tickSize * Flotr.Date.timeUnits[axis.tickUnit]; return Flotr.Date.format(d, Flotr.Date.getFormat(t, span), options.timeMode); }, generator: function(axis) { var set = this.set, get = this.get, timeUnits = this.timeUnits, spec = this.spec, options = axis.options, mode = options.timeMode, scale = timeUnits[options.timeUnit], min = axis.min * scale, max = axis.max * scale, delta = (max - min) / options.noTicks, ticks = [], tickSize = axis.tickSize, tickUnit, formatter, i; // Use custom formatter or time tick formatter formatter = (options.tickFormatter === Flotr.defaultTickFormatter ? this.formatter : options.tickFormatter ); for (i = 0; i < spec.length - 1; ++i) { var d = spec[i][0] * timeUnits[spec[i][1]]; if (delta < (d + spec[i+1][0] * timeUnits[spec[i+1][1]]) / 2 && d >= tickSize) break; } tickSize = spec[i][0]; tickUnit = spec[i][1]; // special-case the possibility of several years if (tickUnit == "year") { tickSize = Flotr.getTickSize(options.noTicks*timeUnits.year, min, max, 0); // Fix for 0.5 year case if (tickSize == 0.5) { tickUnit = "month"; tickSize = 6; } } axis.tickUnit = tickUnit; axis.tickSize = tickSize; var step = tickSize * timeUnits[tickUnit]; d = new Date(min); function setTick (name) { set(d, name, mode, Flotr.floorInBase( get(d, name, mode), tickSize )); } switch (tickUnit) { case "millisecond": setTick('Milliseconds'); break; case "second": setTick('Seconds'); break; case "minute": setTick('Minutes'); break; case "hour": setTick('Hours'); break; case "month": setTick('Month'); break; case "year": setTick('FullYear'); break; } // reset smaller components if (step >= timeUnits.second) set(d, 'Milliseconds', mode, 0); if (step >= timeUnits.minute) set(d, 'Seconds', mode, 0); if (step >= timeUnits.hour) set(d, 'Minutes', mode, 0); if (step >= timeUnits.day) set(d, 'Hours', mode, 0); if (step >= timeUnits.day * 4) set(d, 'Date', mode, 1); if (step >= timeUnits.year) set(d, 'Month', mode, 0); var carry = 0, v = NaN, prev; do { prev = v; v = d.getTime(); ticks.push({ v: v / scale, label: formatter(v / scale, axis) }); if (tickUnit == "month") { if (tickSize < 1) { /* a bit complicated - we'll divide the month up but we need to take care of fractions so we don't end up in the middle of a day */ set(d, 'Date', mode, 1); var start = d.getTime(); set(d, 'Month', mode, get(d, 'Month', mode) + 1); var end = d.getTime(); d.setTime(v + carry * timeUnits.hour + (end - start) * tickSize); carry = get(d, 'Hours', mode); set(d, 'Hours', mode, 0); } else set(d, 'Month', mode, get(d, 'Month', mode) + tickSize); } else if (tickUnit == "year") { set(d, 'FullYear', mode, get(d, 'FullYear', mode) + tickSize); } else d.setTime(v + step); } while (v < max && v != prev); return ticks; }, timeUnits: { millisecond: 1, second: 1000, minute: 1000 * 60, hour: 1000 * 60 * 60, day: 1000 * 60 * 60 * 24, month: 1000 * 60 * 60 * 24 * 30, year: 1000 * 60 * 60 * 24 * 365.2425 }, // the allowed tick sizes, after 1 year we use an integer algorithm spec: [ [1, "millisecond"], [20, "millisecond"], [50, "millisecond"], [100, "millisecond"], [200, "millisecond"], [500, "millisecond"], [1, "second"], [2, "second"], [5, "second"], [10, "second"], [30, "second"], [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], [30, "minute"], [1, "hour"], [2, "hour"], [4, "hour"], [8, "hour"], [12, "hour"], [1, "day"], [2, "day"], [3, "day"], [0.25, "month"], [0.5, "month"], [1, "month"], [2, "month"], [3, "month"], [6, "month"], [1, "year"] ], monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] };