From 0163faefef02207ad0ea3330af688103633293ff Mon Sep 17 00:00:00 2001 From: Pim van den Berg Date: Fri, 10 May 2013 21:36:54 +0200 Subject: import js directory from jsrrdgraph for client side graph rendering Source: https://github.com/manuelluis/jsrrdgraph@276b880 --- js/RrdTime.js | 621 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) create mode 100644 js/RrdTime.js (limited to 'js/RrdTime.js') diff --git a/js/RrdTime.js b/js/RrdTime.js new file mode 100644 index 0000000..95ea0ae --- /dev/null +++ b/js/RrdTime.js @@ -0,0 +1,621 @@ +/** + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + * RRDtool 1.4.5 Copyright by Tobi Oetiker, 1997-2010 + * + * Convert to javascript: Manuel Sanmartin + **/ + +"use strict"; + +/** + * RrdTimeError + * @constructor + */ +var RrdTimeError = function (message) +{ + this.prototype = Error.prototype; + this.name = "RrdTimeError"; + this.message = (message) ? message : "Error"; +}; + +/** + * RrdTime + * @constructor + */ +var RrdTime = function(tspec) /* parser */ +{ + var date = new Date(); + var hr = 0; + + this.tspec = tspec; + + this.tokens = (tspec+'').match(/[0-9]+|[A-Za-z]+|[:.+-\/]/g); + this.toklen = this.tokens.length; + this.tokidx = 0; + + this.token = null; + this.tokid = 0; + + this.specials = RrdTime.VARIOUSWORDS; + + /* establish the default time reference */ + this.type = RrdTime.ABSOLUTE_TIME; + this.offset = 0; + this.tm_sec = date.getSeconds(); + this.tm_min = date.getMinutes(); + this.tm_hour = date.getHours(); + this.tm_mday = date.getDate(); + this.tm_mon = date.getMonth(); + this.tm_year = date.getFullYear()-1900; + this.tm_wday = date.getDay(); + + this.gettok(); + switch (this.tokid) { + case RrdTime.PLUS: + case RrdTime.MINUS: + break; /* jump to OFFSET-SPEC part */ + case RrdTime.EPOCH: + this.type = RrdTime.RELATIVE_TO_EPOCH; + case RrdTime.START: + case RrdTime.END: + if (this.tokid === RrdTime.EPOCH) + this.type = RrdTime.RELATIVE_TO_START_TIME; + else + this.type = RrdTime.RELATIVE_TO_END_TIME; + this.tm_sec = 0; + this.tm_min = 0; + this.tm_hour = 0; + this.tm_mday = 0; + this.tm_mon = 0; + this.tm_year = 0; + case RrdTime.NOW: + var time_reference = this.tokid; + this.gettok(); + if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) + break; + if (time_reference != RrdTime.NOW) { + throw new RrdTimeError("'start' or 'end' MUST be followed by +|- offset"); + } else if (this.tokid != RrdTime.EOF) { + throw new RrdTimeError("if 'now' is followed by a token it must be +|- offset"); + } + break; + case RrdTime.NUMBER: /* Only absolute time specifications below */ + var hour_sv = this.tm_hour; + var year_sv = this.tm_year; + this.tm_hour = 30; + this.tm_year = 30000; + this.tod(); + this.day(); + if (this.tm_hour == 30 && this.tm_year != 30000) + this.tod(); + if (this.tm_hour == 30) + this.tm_hour = hour_sv; + if (this.tm_year == 30000) + this.tm_year = year_sv; + break; + case RrdTime.JAN: + case RrdTime.FEB: + case RrdTime.MAR: + case RrdTime.APR: + case RrdTime.MAY: + case RrdTime.JUN: + case RrdTime.JUL: + case RrdTime.AUG: + case RrdTime.SEP: + case RrdTime.OCT: + case RrdTime.NOV: + case RrdTime.DEC: + this.day(); + if (this.tokid != RrdTime.NUMBER) + break; + this.tod(); + break; + case RrdTime.TEATIME: + hr += 4; + case RrdTime.NOON: + hr += 12; + case RrdTime.MIDNIGHT: + this.tm_hour = hr; + this.tm_min = 0; + this.tm_sec = 0; + this.gettok(); + this.day(); + break; + default: + throw new RrdTimeError("unparsable time: "+this.token+" "+this.sct); + break; + } /* ugly case statement */ + + /* + * the OFFSET-SPEC part + * (NOTE, the sc_tokid was prefetched for us by the previous code) + */ + if (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS) { + this.specials = RrdTime.TIMEMULTIPLIERS; /* switch special words context */ + while (this.tokid == RrdTime.PLUS || this.tokid == RrdTime.MINUS || this.tokid == RrdTime.NUMBER) { + if (this.tokid == RrdTime.NUMBER) { + this.plus_minus(-1); + } else { + this.plus_minus(this.tokid); + } + this.gettok(); /* We will get EOF eventually but that's OK, since token() will return us as many EOFs as needed */ + } + } + + /* now we should be at EOF */ + if (this.tokid != RrdTime.EOF) + throw new RrdTimeError("unparsable trailing text: '..."+this.token+"'"); +// if (this.type == RrdTime.ABSOLUTE_TIME) +// if (mktime(&ptv->tm) == -1) // FIXME ?? +// panic(e("the specified time is incorrect (out of range?)")); +}; + +RrdTime.EOF = -1; +RrdTime.MIDNIGHT = 0; +RrdTime.NOON = 1; +RrdTime.TEATIME = 2; +RrdTime.PM = 3; +RrdTime.AM = 4; +RrdTime.YESTERDAY = 5; +RrdTime.TODAY = 6; +RrdTime.TOMORROW = 7; +RrdTime.NOW = 8; +RrdTime.START = 9; +RrdTime.END = 10; +RrdTime.EPOCH = 11; +RrdTime.SECONDS = 12; +RrdTime.MINUTES = 13; +RrdTime.HOURS = 14; +RrdTime.DAYS = 15; +RrdTime.WEEKS = 16; +RrdTime.MONTHS = 17; +RrdTime.YEARS = 18; +RrdTime.MONTHS_MINUTES = 19; +RrdTime.NUMBER = 20; +RrdTime.PLUS = 21; +RrdTime.MINUS = 22; +RrdTime.DOT = 23; +RrdTime.COLON = 24; +RrdTime.SLASH = 25; +RrdTime.ID = 26; +RrdTime.JUNK = 27; +RrdTime.JAN = 28; +RrdTime.FEB = 29; +RrdTime.MAR = 30; +RrdTime.APR = 31; +RrdTime.MAY = 32; +RrdTime.JUN = 33; +RrdTime.JUL = 34; +RrdTime.AUG = 35; +RrdTime.SEP = 36; +RrdTime.OCT = 37; +RrdTime.NOV = 38; +RrdTime.DEC = 39; +RrdTime.SUN = 40; +RrdTime.MON = 41; +RrdTime.TUE = 42; +RrdTime.WED = 43; +RrdTime.THU = 44; +RrdTime.FRI = 45; +RrdTime.SAT = 46; + +RrdTime.VARIOUSWORDS = { + "midnight": RrdTime.MIDNIGHT, /* 00:00:00 of today or tomorrow */ + "noon": RrdTime.NOON, /* 12:00:00 of today or tomorrow */ + "teatime": RrdTime.TEATIME, /* 16:00:00 of today or tomorrow */ + "am": RrdTime.AM, /* morning times for 0-12 clock */ + "pm": RrdTime.PM, /* evening times for 0-12 clock */ + "tomorrow": RrdTime.TOMORROW, + "yesterday": RrdTime.YESTERDAY, + "today": RrdTime.TODAY, + "now": RrdTime.NOW, + "n": RrdTime.NOW, + "start": RrdTime.START, + "s": RrdTime.START, + "end": RrdTime.END, + "e": RrdTime.END, + "epoch": RrdTime.EPOCH, + "jan": RrdTime.JAN, + "feb": RrdTime.FEB, + "mar": RrdTime.MAR, + "apr": RrdTime.APR, + "may": RrdTime.MAY, + "jun": RrdTime.JUN, + "jul": RrdTime.JUL, + "aug": RrdTime.AUG, + "sep": RrdTime.SEP, + "oct": RrdTime.OCT, + "nov": RrdTime.NOV, + "dec": RrdTime.DEC, + "january": RrdTime.JAN, + "february": RrdTime.FEB, + "march": RrdTime.MAR, + "april": RrdTime.APR, +// "may": RrdTime.MAY, + "june": RrdTime.JUN, + "july": RrdTime.JUL, + "august": RrdTime.AUG, + "september": RrdTime.SEP, + "october": RrdTime.OCT, + "november": RrdTime.NOV, + "december": RrdTime.DEC, + "sunday": RrdTime.SUN, + "sun": RrdTime.SUN, + "monday": RrdTime.MON, + "mon": RrdTime.MON, + "tuesday": RrdTime.TUE, + "tue": RrdTime.TUE, + "wednesday": RrdTime.WED, + "wed": RrdTime.WED, + "thursday": RrdTime.THU, + "thu": RrdTime.THU, + "friday": RrdTime.FRI, + "fri": RrdTime.FRI, + "saturday": RrdTime.SAT, + "sat": RrdTime.SAT +}; + +RrdTime.TIMEMULTIPLIERS = { + "second": RrdTime.SECONDS, /* seconds multiplier */ + "seconds": RrdTime.SECONDS, /* (pluralized) */ + "sec": RrdTime.SECONDS, /* (generic) */ + "s": RrdTime.SECONDS, /* (short generic) */ + "minute": RrdTime.MINUTES, /* minutes multiplier */ + "minutes": RrdTime.MINUTES, /* (pluralized) */ + "min": RrdTime.MINUTES, /* (generic) */ + "m": RrdTime.MONTHS_MINUTES, /* (short generic) */ + "hour": RrdTime.HOURS, /* hours ... */ + "hours": RrdTime.HOURS, /* (pluralized) */ + "hr": RrdTime.HOURS, /* (generic) */ + "h": RrdTime.HOURS, /* (short generic) */ + "day": RrdTime.DAYS, /* days ... */ + "days": RrdTime.DAYS, /* (pluralized) */ + "d": RrdTime.DAYS, /* (short generic) */ + "week": RrdTime.WEEKS, /* week ... */ + "weeks": RrdTime.WEEKS, /* (pluralized) */ + "wk": RrdTime.WEEKS, /* (generic) */ + "w": RrdTime.WEEKS, /* (short generic) */ + "month": RrdTime.MONTHS, /* week ... */ + "months": RrdTime.MONTHS, /* (pluralized) */ + "mon": RrdTime.MONTHS, /* (generic) */ + "year": RrdTime.YEARS, /* year ... */ + "years": RrdTime.YEARS, /* (pluralized) */ + "yr": RrdTime.YEARS, /* (generic) */ + "y": RrdTime.YEARS /* (short generic) */ +}; + +RrdTime.ABSOLUTE_TIME = 0; +RrdTime.RELATIVE_TO_START_TIME = 1; +RrdTime.RELATIVE_TO_END_TIME = 2; +RrdTime.RELATIVE_TO_EPOCH = 3; + +RrdTime.prototype.gettok = function () +{ + if (this.tokidx >= this.toklen) { + this.tokid = RrdTime.EOF; + } else { + this.token = this.tokens[this.tokidx]; + this.tokidx++; + if (!isNaN(this.token)) { + this.tokid = RrdTime.NUMBER; + this.token = parseInt(this.token, 10); + } else if (this.token === ':') { + this.tokid = RrdTime.COLON; + } else if (this.token === '.') { + this.tokid = RrdTime.DOT; + } else if (this.token === '+') { + this.tokid = RrdTime.PLUS; + } else if (this.token === '/') { + this.tokid = RrdTime.SLASH; + } else if (this.token === '-') { + this.tokid = RrdTime.MINUS; + } else { + this.tokid = RrdTime.ID; + if (this.token in this.specials) + this.tokid = this.specials[this.token]; + } + } + return this.tokid; +}; + +RrdTime.prototype.plus_minus = function (doop) +{ + var op = RrdTime.PLUS; + var prev_multiplier = -1; + var delta; + + if (doop >= 0) { + op = doop; + if (this.gettok() != RrdTime.NUMBER) + throw new RrdTimeError("There should be number after '"+(op == RrdTime.PLUS ? '+' : '-')+"'"); + prev_multiplier = -1; /* reset months-minutes guessing mechanics */ + } + /* if doop is < 0 then we repeat the previous op with the prefetched number */ + delta = this.token; + if (this.gettok() == RrdTime.MONTHS_MINUTES) { + /* hard job to guess what does that -5m means: -5mon or -5min? */ + switch (prev_multiplier) { + case RrdTime.DAYS: + case RrdTime.WEEKS: + case RrdTime.MONTHS: + case RrdTime.YEARS: + this.tokid = RrdTime.MONTHS; + break; + case RrdTime.SECONDS: + case RrdTime.MINUTES: + case RrdTime.HOURS: + this.tokid = RrdTime.MINUTES; + break; + default: + if (delta < 6) /* it may be some other value but in the context of RRD who needs less than 6 min deltas? */ + this.tokid = RrdTime.MONTHS; + else + this.tokid = RrdTime.MINUTES; + } + } + prev_multiplier = this.tokid; + switch (this.tokid) { + case RrdTime.YEARS: + this.tm_year += ( op == RrdTime.PLUS) ? delta : -delta; + return; + case RrdTime.MONTHS: + this.tm_mon += ( op == RrdTime.PLUS) ? delta : -delta; + return; + case RrdTime.WEEKS: + delta *= 7; + case RrdTime.DAYS: + this.tm_mday += ( op == RrdTime.PLUS) ? delta : -delta; + return; + case RrdTime.HOURS: + this.offset += (op == RrdTime.PLUS) ? delta * 60 * 60 : -delta * 60 * 60; + return; + case RrdTime.MINUTES: + this.offset += (op == RrdTime.PLUS) ? delta * 60 : -delta * 60; + return; + case RrdTime.SECONDS: + this.offset += (op == RrdTime.PLUS) ? delta : -delta; + return; + default: /*default unit is seconds */ + this.offset += (op == RrdTime.PLUS) ? delta : -delta; + return; + } + throw new RrdTimeError("well-known time unit expected after "+delta); +}; + +RrdTime.prototype.tod = function() /* tod() computes the time of day (TIME-OF-DAY-SPEC) */ +{ + var hour, minute = 0; + var tlen; + /* save token status in case we must abort */ + var tokid_sv = this.tokid; + + tlen = (this.token+"").length; + /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */ + if (tlen > 2) + return; + hour = this.token; + this.gettok(); + if (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT) { + /* guess we are looking at a date */ + this.tokid = tokid_sv; + this.token = hour; + return; + } + if (this.tokid == RrdTime.COLON) { + if (this.gettok() != RrdTime.NUMBER) + throw new RrdTimeError("Parsing HH:MM syntax, expecting MM as number, got none"); + minute = this.token; + if (minute > 59) + throw new RrdTimeError("parsing HH:MM syntax, got MM = "+minute+" (>59!)"); + this.gettok(); + } + /* check if an AM or PM specifier was given */ + if (this.tokid == RrdTime.AM || this.tokid == RrdTime.PM) { + if (hour > 12) { + throw new RrdTimeError("there cannot be more than 12 AM or PM hours"); + } + if (this.tokid == RrdTime.PM) { + if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */ + hour += 12; + } else { + if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */ + hour = 0; + } + this.gettok(); + } else if (hour > 23) { + /* guess it was not a time then ... */ + this.tokid = tokid_sv; + this.token = hour; + return; + } + this.tm_hour = hour; + this.tm_min = minute; + this.tm_sec = 0; + if (this.tm_hour == 24) { + this.tm_hour = 0; + this.tm_mday++; + } +}; + +RrdTime.prototype.assign_date = function(mday, mon, year) +{ + if (year > 138) { + if (year > 1970) { + year -= 1900; + } else { + throw new RrdTimeError("invalid year "+year+" (should be either 00-99 or >1900)"); + } + } else if (year >= 0 && year < 38) { + year += 100; /* Allow year 2000-2037 to be specified as */ + } + /* 00-37 until the problem of 2038 year will */ + /* arise for unices with 32-bit time_t :) */ + if (year < 70) + throw new RrdTimeError("won't handle dates before epoch (01/01/1970), sorry"); + + this.tm_mday = mday; + this.tm_mon = mon; + this.tm_year = year; +}; + +RrdTime.prototype.day = function () +{ + var mday = 0, wday, mon, year = this.tm_year; + var tlen; + + switch (this.tokid) { + case RrdTime.YESTERDAY: + this.tm_mday--; + case RrdTime.TODAY: + this.gettok(); + break; + case RrdTime.TOMORROW: + this.tm_mday++; + this.gettok(); + break; + case RrdTime.JAN: + case RrdTime.FEB: + case RrdTime.MAR: + case RrdTime.APR: + case RrdTime.MAY: + case RrdTime.JUN: + case RrdTime.JUL: + case RrdTime.AUG: + case RrdTime.SEP: + case RrdTime.OCT: + case RrdTime.NOV: + case RrdTime.DEC: + mon = (this.tokid - RrdTime.JAN); + if (this.gettok() != RrdTime.NUMBER) + throw new RrdTimeError("the day of the month should follow month name"); + mday = this.token; + if (this.gettok() == RrdTime.NUMBER) { + year = this.token; + this.gettok(); + } else { + year = this.tm_year; + } + this.assign_date(mday, mon, year); + break; + case RrdTime.SUN: + case RrdTime.MON: + case RrdTime.TUE: + case RrdTime.WED: + case RrdTime.THU: + case RrdTime.FRI: + case RrdTime.SAT: + wday = (this.tokid - RrdTime.SUN); + this.tm_mday += (wday - this.tm_wday); + this.gettok(); + break; + case RrdTime.NUMBER: + mon = this.token; + if (mon > 10 * 365 * 24 * 60 * 60) { + this.localtime(mon); + this.gettok(); + break; + } + if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */ + var str = this.token + ''; + year = parseInt(str.substr(0,4),10); + mon = parseInt(str.substr(4,2),10); + mday = parseInt(str.substr(6,2),10); + this.gettok(); + } else { + this.gettok(); + if (mon <= 31 && (this.tokid == RrdTime.SLASH || this.tokid == RrdTime.DOT)) { + var sep = this.tokid; + if (this.gettok() != RrdTime.NUMBER) + throw new RrdTimeError("there should be "+(RrdTime.DOT ? "month" : "day")+" number after '"+(RrdTime.DOT ? '.' : '/')+"'"); + mday = this.token; + if (this.gettok() == sep) { + if (this.gettok() != RrdTime.NUMBER) + throw new RrdTimeError("there should be year number after '"+(sep == RrdTime.DOT ? '.' : '/')+"'"); + year = this.token; + this.gettok(); + } + if (sep == RrdTime.DOT) { + var x = mday; + mday = mon; + mon = x; + } + } + } + mon--; + if (mon < 0 || mon > 11) + throw new RrdTimeError("did you really mean month "+(mon+1)+"?"); + if (mday < 1 || mday > 31) + throw new RrdTimeError("I'm afraid that "+mday+" is not a valid day of the month"); + this.assign_date(mday, mon, year); + break; + } +}; + +RrdTime.prototype.localtime = function (tm) +{ + var date = new Date(tm*1000); + this.tm_sec = date.getSeconds(); + this.tm_min = date.getMinutes(); + this.tm_hour = date.getHours(); + this.tm_mday = date.getDate(); + this.tm_mon = date.getMonth(); + this.tm_year = date.getFullYear()-1900; + this.tm_wday = date.getDay(); +}; + +RrdTime.prototype.mktime = function() +{ + var date = new Date(this.tm_year+1900, this.tm_mon, this.tm_mday, this.tm_hour, this.tm_min, this.tm_sec); + return Math.round(date.getTime()/1000.0); +}; + +RrdTime.proc_start_end = function(start_t, end_t) +{ + var start, end; + + if (start_t.type == RrdTime.RELATIVE_TO_END_TIME && end_t.type == RrdTime.RELATIVE_TO_START_TIME) + throw new RrdTimeError("the start and end times cannot be specified relative to each other"); + if (start_t.type == RrdTime.RELATIVE_TO_START_TIME) + throw new RrdTimeError("the start time cannot be specified relative to itself"); + if (end_t.type == RrdTime.RELATIVE_TO_END_TIME) + throw new RrdTimeError("the end time cannot be specified relative to itself"); + + if (start_t.type == RrdTime.RELATIVE_TO_END_TIME) { + end = end_t.mktime() + end_t.offset; + var tmtmp = new Date(end*1000); + tmtmp.setDate(tmtmp.getDate()+start_t.tm_mday); + tmtmp.setMonth(tmtmp.getMonth()+start_t.tm_mon); + tmtmp.setFullYear(tmtmp.getFullYear()+start_t.tm_year); + start = Math.round(tmtmp.getTime()/1000.0) + start_t.offset; + } else { + start = start_t.mktime() + start_t.offset; + } + + if (end_t.type == RrdTime.RELATIVE_TO_START_TIME) { + start = start_t.mktime() + start_t.offset; + var tmtmp = new Date(start*1000); + tmtmp.setDate(tmtmp.getDate()+end_t.tm_mday); + tmtmp.setMonth(tmtmp.getMonth()+end_t.tm_mon); + tmtmp.setFullYear(tmtmp.getFullYear()+end_t.tm_year); + end = Math.round(tmtmp.getTime()/1000.0) + end_t.offset; + } else { + end = end_t.mktime() + end_t.offset; + } + + return [start, end]; +}; + -- cgit v1.1