Commit ca050b26 authored by Mark Côté's avatar Mark Côté

Merge pull request #47 from markrcote/master

Better date/time formatting
parents fb24a990 33714017
......@@ -501,6 +501,7 @@ through the following axis options:
minTickSize: array
timeformat: null or format string
monthNames: null or array of size 12 of strings
dayNames: null or array of size 7 of strings
twelveHourClock: boolean
Here "timeformat" is a format string to use. You might use it like
......@@ -508,41 +509,47 @@ this:
xaxis: {
mode: "time"
timeformat: "%y/%m/%d"
timeformat: "%Y/%m/%d"
}
This will result in tick labels like "2000/12/24". The following
specifiers are supported
%h: hours
%H: hours (left-padded with a zero)
%M: minutes (left-padded with a zero)
%S: seconds (left-padded with a zero)
%d: day of month (1-31), use %0d for zero-padding
%m: month (1-12), use %0m for zero-padding
%y: year (four digits)
This will result in tick labels like "2000/12/24". A subset of the
standard strftime specifiers are supported:
%a: weekday name (customizable)
%b: month name (customizable)
%p: am/pm, additionally switches %h/%H to 12 hour instead of 24
%d: day of month, zero-padded (01-31)
%e: day of month, space-padded ( 1-31)
%H: hours, 24-hour time, zero-padded (00-23)
%I: hours, 12-hour time, zero-padded (01-12)
%m: month, zero-padded (01-12)
%M: minutes, zero-padded (00-59)
%S: seconds, zero-padded (00-59)
%y: year (two digits)
%Y: year (four digits)
%p: am/pm
%P: AM/PM (uppercase version of %p)
Inserting a zero like %0m or %0d means that the specifier will be
left-padded with a zero if it's only single-digit. So %y-%0m-%0d
results in unambigious ISO timestamps like 2007-05-10 (for May 10th).
%w: weekday as number (0-6, 0 being Sunday)
You can customize the month names with the "monthNames" option. For
instance, for Danish you might specify:
monthNames: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"]
Similarly you can customize the weekday names with the "dayNames"
option. An example in French:
dayNames: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"]
If you set "twelveHourClock" to true, the autogenerated timestamps
will use 12 hour AM/PM timestamps instead of 24 hour.
The format string and month names are used by a very simple built-in
format function that takes a date object, a format string (and
optionally an array of month names) and returns the formatted string.
If needed, you can access it as $.plot.formatDate(date, formatstring,
monthNames) or even replace it with another more advanced function
from a date library if you're feeling adventurous.
will use 12 hour AM/PM timestamps instead of 24 hour. This only
applies if you have not set "timeformat". Use the "%I" and "%p" or
"%P" options if you want to build your own format string with 12-hour
times.
If the Date object has a strftime property (and it is a function), it
will be used instead of the built-in formatter. Thus you can include
a strftime library such as http://hacks.bluesmoon.info/strftime/ for
more powerful date/time formatting.
If everything else fails, you can control the formatting by specifying
a custom tick formatter function as usual. Here's a simple example
......
......@@ -15,6 +15,11 @@ control how the dates are displayed. If null, the dates are displayed
as UTC. If "browser", the dates are displayed in the time zone of the
user's browser.
Date/time formatting has changed and now follows a proper subset
of the standard strftime specifiers. Additionally, if a strftime
function is found in the Date object's prototype, it will be used
instead of the built-in formatter.
Axis labels are now drawn with canvas text with some parsing to
support newlines. This solves various issues but also means that they
no longer support HTML markup, can be accessed as DOM elements or
......@@ -29,6 +34,10 @@ and "flot-overlay" to prevent accidental clashes (issue 540).
Changes:
- Date/time formatting follows proper subset of strftime specifiers,
and support added for Date.prototype.strftime, if found (patch by
Mark Cote, issue 558).
- Fixed display of year ticks (patch by Mark Cote, issue 195).
- Support for time series moved to plugin (patch by Mark Cote).
......
......@@ -23,7 +23,9 @@
<p>Zoom to: <button id="whole">Whole period</button>
<button id="nineties">1990-2000</button>
<button id="latenineties">1996-2000</button>
<button id="ninetynine">1999</button></p>
<button id="ninetynine">1999</button>
<button id="lastweekninetynine">end of 1999</button>
<button id="lastdayninetynine">very end of 1999</button></p>
<p>The timestamps must be specified as Javascript timestamps, as
milliseconds since January 1, 1970 00:00. This is like Unix
......@@ -31,10 +33,10 @@
multiply with 1000!).</p>
<p>As an extra caveat, the timestamps are interpreted according to
UTC to avoid having the graph shift with each visitor's local
time zone. So you might have to add your local time zone offset
to the timestamps or simply pretend that the data was produced
in UTC instead of your local time zone.</p>
UTC and, by default, displayed as such. You can set the axis
"timezone" option to "browser" to display the timestamps in the
user's timezone, or, if you use timezoneJS, you can specify a
time zone.</p>
<script id="source">
$(function () {
......@@ -50,8 +52,8 @@ $(function () {
$.plot($("#placeholder"), [d], {
xaxis: {
mode: "time",
min: (new Date(1990, 1, 1)).getTime(),
max: (new Date(2000, 1, 1)).getTime()
min: (new Date(1990, 0, 1)).getTime(),
max: (new Date(2000, 0, 1)).getTime()
}
});
});
......@@ -61,8 +63,8 @@ $(function () {
xaxis: {
mode: "time",
minTickSize: [1, "year"],
min: (new Date(1996, 1, 1)).getTime(),
max: (new Date(2000, 1, 1)).getTime()
min: (new Date(1996, 0, 1)).getTime(),
max: (new Date(2000, 0, 1)).getTime()
}
});
});
......@@ -72,8 +74,32 @@ $(function () {
xaxis: {
mode: "time",
minTickSize: [1, "month"],
min: (new Date(1999, 1, 1)).getTime(),
max: (new Date(2000, 1, 1)).getTime()
min: (new Date(1999, 0, 1)).getTime(),
max: (new Date(2000, 0, 1)).getTime()
}
});
});
$("#lastweekninetynine").click(function () {
$.plot($("#placeholder"), [d], {
xaxis: {
mode: "time",
minTickSize: [1, "day"],
min: (new Date(1999, 11, 25)).getTime(),
max: (new Date(2000, 0, 1)).getTime(),
timeformat: "%a"
}
});
});
$("#lastdayninetynine").click(function () {
$.plot($("#placeholder"), [d], {
xaxis: {
mode: "time",
minTickSize: [1, "hour"],
min: (new Date(1999, 11, 31)).getTime(),
max: (new Date(2000, 0, 1)).getTime(),
twelveHourClock: true
}
});
});
......
......@@ -12,51 +12,59 @@ for details.
return base * Math.floor(n / base);
}
// returns a string with the date d formatted according to fmt
function formatDate(d, fmt, monthNames) {
var leftPad = function(n) {
// Returns a string with the date d formatted according to fmt.
// A subset of the Open Group's strftime format is supported.
function formatDate(d, fmt, monthNames, dayNames) {
if (typeof d.strftime == "function") {
return d.strftime(fmt);
}
var leftPad = function(n, pad) {
n = "" + n;
return n.length == 1 ? "0" + n : n;
pad = "" + (pad == null ? "0" : pad);
return n.length == 1 ? pad + n : n;
};
var r = [];
var escape = false, padNext = false;
var escape = false;
var hours = d.getHours();
var isAM = hours < 12;
if (monthNames == null)
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
if (dayNames == null)
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
if (fmt.search(/%p|%P/) != -1) {
if (hours > 12) {
hours = hours - 12;
} else if (hours == 0) {
hours = 12;
}
var hours12;
if (hours > 12) {
hours12 = hours - 12;
} else if (hours == 0) {
hours12 = 12;
} else {
hours12 = hours;
}
for (var i = 0; i < fmt.length; ++i) {
var c = fmt.charAt(i);
if (escape) {
switch (c) {
case 'h': c = "" + hours; break;
case 'a': c = "" + dayNames[d.getDay()]; break;
case 'b': c = "" + monthNames[d.getMonth()]; break;
case 'd': c = leftPad(d.getDate()); break;
case 'e': c = leftPad(d.getDate(), " "); break;
case 'H': c = leftPad(hours); break;
case 'I': c = leftPad(hours12); break;
case 'l': c = leftPad(hours12, " "); break;
case 'm': c = leftPad(d.getMonth() + 1); break;
case 'M': c = leftPad(d.getMinutes()); break;
case 'S': c = leftPad(d.getSeconds()); break;
case 'd': c = "" + d.getDate(); break;
case 'm': c = "" + (d.getMonth() + 1); break;
case 'y': c = "" + d.getFullYear(); break;
case 'b': c = "" + monthNames[d.getMonth()]; break;
case 'y': c = leftPad(d.getFullYear() % 100); break;
case 'Y': c = "" + d.getFullYear(); break;
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
case '0': c = ""; padNext = true; break;
}
if (c && padNext) {
c = leftPad(c);
padNext = false;
case 'w': c = "" + d.getDay(); break;
}
r.push(c);
if (!padNext)
escape = false;
escape = false;
}
else {
if (c == "%")
......@@ -77,11 +85,14 @@ for details.
targetMethod) {
sourceObj[sourceMethod] = function() {
return targetObj[targetMethod].apply(targetObj, arguments);
}
};
};
var utc = {
date: d
};
// support strftime, if found
if (d.strftime != undefined)
addProxyMethod(utc, "strftime", d, "strftime");
addProxyMethod(utc, "getTime", d, "getTime");
addProxyMethod(utc, "setTime", d, "setTime");
var props = [ "Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds" ];
......@@ -254,19 +265,20 @@ for details.
// first check global format
if (opts.timeformat != null)
return formatDate(d, opts.timeformat, opts.monthNames);
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
var span = axis.max - axis.min;
var suffix = (opts.twelveHourClock) ? " %p" : "";
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
if (t < timeUnitSize.minute)
fmt = "%h:%M:%S" + suffix;
fmt = hourCode + ":%M:%S" + suffix;
else if (t < timeUnitSize.day) {
if (span < 2 * timeUnitSize.day)
fmt = "%h:%M" + suffix;
fmt = hourCode + ":%M" + suffix;
else
fmt = "%b %d %h:%M" + suffix;
fmt = "%b %d " + hourCode + ":%M" + suffix;
}
else if (t < timeUnitSize.month)
fmt = "%b %d";
......@@ -274,12 +286,12 @@ for details.
if (span < timeUnitSize.year)
fmt = "%b";
else
fmt = "%b %y";
fmt = "%b %Y";
}
else
fmt = "%y";
fmt = "%Y";
var rt = formatDate(d, fmt, opts.monthNames);
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
return rt;
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment