/* * Timeglider for Javascript / jQuery * http://timeglider.com/jquery * * Copyright 2011, Mnemograph LLC * Licensed under Timeglider Dual License * http://timeglider.com/jquery/?p=license * */ // initial declaration of timeglider object for widget // authoring app will declare a different object, so // this will defer to window.timeglider timeglider = window.timeglider || {mode:"publish", version:"0.1.0", ui:{touchtesting:false}}; /* * TG_Date * * dependencies: jQuery, Globalize * * You might be wondering why we're not extending JS Date(). * That might be a good idea some day. There are some * major issues with Date(): the "year zero" (or millisecond) * in JS and other date APIs is 1970, so timestamps are negative * prior to that; JS's Date() can't handle years prior to * -271820, so some extension needs to be created to deal with * times (on the order of billions of years) existing before that. * * This TG_Date object also has functionality which goes hand-in-hand * with the date hashing system: each event on the timeline is hashed * according to day, year, decade, century, millenia, etc * */ /* IMPORTED DATE STANDARD http://www.w3.org/TR/NOTE-datetime "a profile of" ISO 8601 date format Complete date plus hours, minutes and seconds: YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) Acceptable: YYYY YYYY-MM YYYY-MM-DD YYYY-MM-DDT13 YYYY-MM-DD 08:15 (strlen 16) YYYY-MM-DD 08:15:30 (strlen 19) (above would assume either a timeline-level timezone, or UTC) containing its own timezone, this would ignore timeline timezone YYYY-MM-DD 08:15:30-07:00 */ timeglider.TG_Date = {}; (function(tg){ var tg = timeglider, $ = jQuery; // caches speed up costly calculations var getRataDieCache = {}, getDaysInYearSpanCache = {}, getBCRataDieCache = {}, getDateFromRDCache = {}, getDateFromSecCache = {}; var VALID_DATE_PATTERN = /^(\-?\d+)?(\-\d{1,2})?(\-\d{1,2})?(?:T| )?(\d{1,2})?(?::)?(\d{1,2})?(?::)?(\d{1,2})?(\+|\-)?(\d{1,2})?(?::)?(\d{1,2})?/; // MAIN CONSTRUCTOR tg.TG_Date = function (strOrNum, date_display, offSec) { var dateStr, isoStr, gotSec, offsetSeconds = offSec || 0; if (typeof(strOrNum) == "number") { // SERIAL SECONDS dateStr = isoStr = TG_Date.getDateFromSec(strOrNum); gotSec = (strOrNum + offsetSeconds); } else if (typeof(strOrNum) === "object") { // TODO: JS Date object? // dateStr = strOrNum.ye + "-" + strOrNum.mo + "-" + strOrNum.da } else { // STRING if (strOrNum == "today") { strOrNum = TG_Date.getToday(); } dateStr = isoStr = strOrNum; } if (VALID_DATE_PATTERN.test(dateStr)) { // !TODO: translate strings like "today" and "now" // "next week", "a week from thursday", "christmas" var parsed = TG_Date.parse8601(dateStr); if (parsed.tz_ho) { // this is working ------ timezones in the string translate correctly // OK: transforms date properly to UTC since it should have been there parsed = TG_Date.toFromUTC(parsed, {hours:parsed.tz_ho, minutes:parsed.tz_mi}, "to"); } // ye, mo, da, ho, mi, se arrive in parsed (with tz_) $.extend(this,parsed); // SERIAL day from year zero this.rd = TG_Date.getRataDie(this); // SERIAL month from year 0 this.mo_num = getMoNum(this); // SERIAL second from year 0 this.sec = gotSec || getSec(this); this.date_display = (date_display) ? (date_display.toLowerCase()).substr(0,2) : "ho"; // TODO: get good str from parse8601 this.dateStr = isoStr; } else { return {error:"invalid date"}; } return this; } // end TG_Date Function var TG_Date = tg.TG_Date; /* * getTimeUnitSerial * gets the serial number of specified time unit, using a ye-mo-da date object * used in addToTicksArray() in Mediator * * @param fd {object} i.e. the focus date: {ye:1968, mo:8, da:20} * @param unit {string} scale-unit (da, mo, ye, etc) * * @return {number} a non-zero serial for the specified time unit */ TG_Date.getTimeUnitSerial = function (fd, unit) { var ret = 0; var floorCeil; if (fd.ye < 0) { floorCeil = Math.ceil; } else { floorCeil = Math.floor; } switch (unit) { case "da": ret = fd.rd; break; // set up mo_num inside TG_Date constructor case "mo": ret = fd.mo_num; break; case "ye": ret = fd.ye; break; case "de": ret = floorCeil(fd.ye / 10); break; case "ce": ret = floorCeil(fd.ye / 100); break; case "thou": ret = floorCeil(fd.ye / 1000); break; case "tenthou": ret = floorCeil(fd.ye / 10000); break; case "hundredthou": ret = floorCeil(fd.ye / 100000); break; case "mill": ret = floorCeil(fd.ye / 1000000); break; case "tenmill": ret = floorCeil(fd.ye / 10000000); break; case "hundredmill": ret = floorCeil(fd.ye / 100000000); break; case "bill": ret = floorCeil(fd.ye / 1000000000); break; } return ret; }; TG_Date.getMonthDays = function(mo,ye) { if ((TG_Date.isLeapYear(ye) == true) && (mo==2)) { return 29; } else { return TG_Date.monthsDayNums[mo]; } }; TG_Date.twentyFourToTwelve = function (e) { var dob = {}; dob.ye = e.ye; dob.mo = e.mo || 1; dob.da = e.da || 1; dob.ho = e.ho || 0; dob.mi = e.mi || 0; dob.ampm = "am"; if (e.ho >= 12) { dob.ampm = "pm"; if (e.ho > 12) { dob.ho = e.ho - 12; } else { dob.ho = 12; } } else if (e.ho == 0) { dob.ho = 12; dob.ampm = "am"; } else { dob.ho = e.ho; } if (dob.mi < 9) { dob.mi = "0" + dob.mi; } return dob; }; /* * RELATES TO TICK WIDTH: SPECIFIC TO TIMELINE VIEW */ TG_Date.getMonthAdj = function (serial, tw) { var d = TG_Date.getDateFromMonthNum(serial); var w; switch (d.mo) { // 31 days case 1: case 3: case 5: case 7: case 8: case 10: case 12: var w = Math.floor(tw + ((tw/28) * 3)); return {"width":w, "days":31}; break; // Blasted February! case 2: if (TG_Date.isLeapYear(d.ye) == true) { w = Math.floor(tw + (tw/28)); return {"width":w, "days":29}; } else { return {"width":tw, "days":28}; } break; default: // 30 days w = Math.floor(tw + ((tw/28) * 2)); return {"width":w, "days":30}; } }; /* * getDateFromMonthNum * Gets a month (1-12) and year from a serial month number * @param mn {number} serial month number * @return {object} ye, mo (numbers) */ TG_Date.getDateFromMonthNum = function(mn) { var rem = 0; var ye, mo; if (mn > 0) { rem = mn % 12; if (rem == 0) { rem = 12 }; mo = rem; ye = Math.ceil(mn / 12); } else { // BCE! rem = Math.abs(mn) % 12; mo = (12 - rem) + 1; if (mo == 13) mo = 1; // NOYEARZERO problem: here we would subtract // a year from the results to eliminate the year 0 ye = -1 * Math.ceil(Math.abs(mn) / 12); // -1 } return {ye:ye, mo:mo}; }; /* * getMonthWidth * Starting with a base-width for a 28-day month, calculate * the width for any month with the possibility that it might * be a leap-year February. * * @param mo {number} month i.e. 1 = January, 12 = December * @param ye {number} year * * RELATES TO TICK WIDTH: SPECIFIC TO TIMELINE VIEW */ TG_Date.getMonthWidth = function(mo,ye,tickWidth) { var dayWidth = tickWidth / 28; var ad; var nd = 28; switch (mo) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: ad = 3; break; case 4: case 6: case 9: case 11: ad = 2; break; // leap year case 2: if (TG_Date.isLeapYear(ye) == true) { ad = 1; } else { ad=0; }; break; } var width = Math.floor(tickWidth + (dayWidth * ad)); var days = nd + ad; return {width:width, numDays:days}; }; TG_Date.getToday = function () { var d = new Date(); return d.getUTCFullYear() + "-" + (d.getUTCMonth() + 1) + "-" + d.getUTCDate() + " " + d.getUTCHours() + ":" + d.getUTCMinutes() + ":" + d.getUTCSeconds(); } /* * Helps calculate the position of a modulo remainder in getRataDie() */ TG_Date.getMonthFromRemDays = function (dnum, yr) { var tack = 0; var rem = 0; var m = 0; if (TG_Date.isLeapYear(yr)){ tack = 1; } else { tack=0; } if (dnum <= 31) { m = 1; rem = dnum; } else if ((dnum >31) && (dnum <= 59 + tack)) { m = 2; rem = dnum - (31 + tack); } else if ((dnum > 59 + tack) && (dnum <= 90 + tack)) { m = 3; rem = dnum - (59 + tack); } else if ((dnum > 90 + tack) && (dnum <= 120 + tack)) { m = 4; rem = dnum - (90 + tack); } else if ((dnum > 120 + tack) && (dnum <= 151 + tack)) { m = 5; rem = dnum - (120 + tack); } else if ((dnum > 151 + tack) && (dnum <= 181 + tack)) { m = 6; rem = dnum - (151 + tack); } else if ((dnum > 181 + tack) && (dnum <= 212 + tack)) { m = 7; rem = dnum - (181 + tack); } else if ((dnum > 212 + tack) && (dnum <= 243 + tack)) { m = 8; rem = dnum - (212 + tack); } else if ((dnum > 243 + tack) && (dnum <= 273 + tack)) { m = 9; rem = dnum - (243 + tack); } else if ((dnum > 273 + tack) && (dnum <= 304 + tack)) { m = 10; rem = dnum - (273 + tack); } else if ((dnum > 304 + tack) && (dnum <= 334 + tack)) { m = 11; rem = dnum - (304 + tack); } else { m = 12; rem = dnum - (334 + tack); } return {mo:m, da:rem}; }; /* GET YYYY.MM.DD FROM (serial) rata die @param snum is the rata die or day serial number */ TG_Date.getDateFromRD = function (snum) { if (getDateFromRDCache[snum]) { return getDateFromRDCache[snum] } // in case it arrives as an RD-decimal var snumAb = Math.floor(snum); var bigP = 146097; // constant days in big cal cycle var chunk1 = Math.floor(snumAb / bigP); var chunk1days = chunk1 * bigP; var chunk1yrs = Math.floor(snumAb / bigP) * 400; var chunk2days = snumAb - chunk1days; var dechunker = chunk2days; var ct = 1; var ia = chunk1yrs + 1; var iz = ia + 400; for (var i = ia; i <= iz; i++) { if (dechunker > 365) { dechunker -= 365; if (TG_Date.isLeapYear(i)) { dechunker -= 1; } ct++; } else { i = iz; } } var yt = chunk1yrs + ct; if (dechunker == 0) dechunker = 1; var inf = TG_Date.getMonthFromRemDays(dechunker,yt); // in case... var miLong = (snum - snumAb) * 1440; var mi = Math.floor(miLong % 60); var ho = Math.floor(miLong / 60); if ((TG_Date.isLeapYear(yt)) && (inf['mo'] == 2)) { inf['da'] += 1; } var ret = yt + "-" + inf['mo'] + "-" + inf['da'] + " " + ho + ":" + mi + ":00"; getDateFromRDCache[snum] = ret; return ret; }, // end getDateFromRD TG_Date.getDateFromSec = function (sec) { // FIRST GET Rata die if (getDateFromSecCache[sec]) { return getDateFromSecCache[sec] } // the sec/86400 represents a "rd-decimal form" // that will allow extraction of hour, minute, second var ret = TG_Date.getDateFromRD(sec / 86400); getDateFromSecCache[sec] = ret; return ret; }; TG_Date.isLeapYear = function(y) { if (y % 400 == 0) { return true; } else if (y % 100 == 0){ return false; } else if (y % 4 == 0) { return true; } else { return false; } }; /* * getRataDie * Core "normalizing" function for dates, the serial number day for * any date, starting with year 1 (well, zero...), wraps a getBCRataDie() * for getting negative year serial days * * @param dat {object} date object with {ye, mo, da} * @return {number} the serial day */ TG_Date.getRataDie = function (dat) { var ye = dat.ye; var mo = dat.mo; var da = dat.da; var ret = 0; if (getRataDieCache[ye + "-" + mo + "-" + da]) { return getRataDieCache[ye + "-" + mo + "-" + da]; } if (ye >= 0) { // THERE IS NO YEAR ZERO!!! if (ye == 0) ye = 1; var fat = (Math.floor(ye / 400)) * 146097, remStart = (ye - (ye % 400)), moreDays = parseInt(getDaysInYearSpan(remStart, ye)), daysSoFar = parseInt(getDaysSoFar(mo,ye)); ret = (fat + moreDays + daysSoFar + da) - 366; } else if (ye < 0) { ret = TG_Date.getBCRataDie({ye:ye, mo:mo, da:da}); } getRataDieCache[ye + "-" + mo + "-" + da] = ret; return ret; ////// internal RataDie functions /* * getDaysInYearSpan * helps calculate chunks of whole years * @param a {number} initial year in span * @param z {number} last year in span * * @return {number} days in span of arg. years */ function getDaysInYearSpan (a, z) { if (getDaysInYearSpanCache[a + "-" + z]) { return getDaysInYearSpanCache[a + "-" + z]; } var t = 0; for (var i = a; i < z; i++){ if (TG_Date.isLeapYear(i)) { t += 366; } else { t += 365; } } getDaysInYearSpanCache[a + "-" + z] = t; return t; }; function getDaysSoFar (mo,ye) { var d; switch (mo) { case 1: d=0; break; // 31 case 2: d=31; break; // 29 case 3: d=59; break; // 31 case 4: d=90; break; // 30 case 5: d=120; break; // 31 case 6: d=151; break; // 30 case 7: d=181; break; // 31 case 8: d=212; break; // 31 case 9: d=243; break; // 30 case 10: d=273;break; // 31 case 11: d=304;break; // 30 case 12: d=334;break; // 31 } if (mo > 2) { if (TG_Date.isLeapYear(ye)) { d += 1; } } return d; }; }; TG_Date.monthNamesLet = ["","J","F","M","A","M","J","J","A","S","O","N","D"]; TG_Date.monthsDayNums = [0,31,28,31,30,31,30,31,31,30,31,30,31,29]; // NON-CULTURE TG_Date.units = ["da", "mo", "ye", "de", "ce", "thou", "tenthou", "hundredthou", "mill", "tenmill", "hundredmill", "bill"]; /* Counts serial days starting with -1 in year -1. Visualize a number counting from "right to left" on top of the other calendrical pieces chunking away from "left to right". But since there's no origin farther back before 0 we have no choice. @param dat object with .ye, .mo, .da */ TG_Date.getBCRataDie = function (dat) { var ye = dat.ye, mo = dat.mo, da = dat.da; if (getBCRataDieCache[ye + "-" + mo + "-" + da]) { return getBCRataDieCache[ye + "-" + mo + "-" + da]; } if (mo == 0) mo = 1; if (da == 0) da = 1; var absYe = Math.abs(ye); var chunks = [0,335,306,275,245,214,184,153,122,92,61,31,0]; var mdays = TG_Date.monthsDayNums[mo]; var rawYeDays = (absYe - 1) * 366; var rawMoDays = chunks[mo]; var rawDaDays = (mdays - da) + 1; var ret = -1 * (rawYeDays + rawMoDays + rawDaDays); getBCRataDieCache[ye + "-" + mo + "-" + da] = ret; return ret; }; TG_Date.setCulture = function(culture_str) { var cult = tg.culture = Globalize.culture(culture_str || "default"); // ["","January", "February", "March", etc]; TG_Date.monthNames = $.merge([""], cult.calendar.months.names); // ["","Jan", "Feb", "Mar", etc]; TG_Date.monthNamesAbbr = $.merge([""], cult.calendar.months.namesAbbr); // ["Sunday", "Monday", "Tuesday", etc]; TG_Date.dayNames = cult.calendar.days.names; // ["Sun", "Mon", "Tue", etc]; TG_Date.dayNamesAbbr = cult.calendar.days.namesAbbr; TG_Date.dayNamesShort = cult.calendar.days.namesShort; TG_Date.patterns = cult.calendar.patterns; }; /* * INSTANCE METHODS * */ TG_Date.prototype = { format : function (sig, useLimit, tz_off) { var offset = tz_off || {"hours":0, "minutes":0}; var jsDate, ddlim = "da", fromUTC; // jsDate var tgFormatDate = function(fromUTC, display) { var tgd = timeglider.TG_Date, f = "", d = fromUTC, ampm, hopack = {}, disp = display || "da", bceYe = function(ye) { var y = parseInt(ye,10); if (y < 0) { return Math.abs(y) + " bce"; } else { return y; } }; switch (disp) { case "no": return ""; break; case "ye": f = bceYe(d.ye); break; case "mo": f = tgd.monthNamesAbbr[d.mo] + " " + bceYe(d.ye); break; case "da": f =bceYe(d.ye) + "-"+ tgd.monthNamesAbbr[d.mo] + "-" + d.da ; break; case "ho": ampm = "AM"; hoPack = tgd.twentyFourToTwelve(d); var hour=hoPack.ho; if(hoPack.ampm=="pm") hour=hour+12; f =d.ye+"-"+d.mo+"-"+d.da+ " " +hour+":"+hoPack.mi; //f = tgd.monthNamesAbbr[d.mo] //+ " " + d.da + ", " //+ bceYe(d.ye) + " " //+ hoPack.ho + ":" + hoPack.mi + " " + hoPack.ampm; break; } // end switch return f; }; if (useLimit == true) { // reduce to 2 chars for consistency ddlim = this.date_display.substr(0,2); switch (ddlim) { case "no": return ""; break; case "ye": sig = "yyyy"; break; case "mo": sig = "MMM yyyy"; break; case "da": sig = "MMM d, yyyy"; break; case "ho": sig = "MMM d, yyyy h:mm tt"; break; default: sig = "f"; } } var cloner = _.clone(this), fromUTC = TG_Date.toFromUTC(cloner, offset, "from"); if (timeglider.i18n) { // make use of possible other culture via i18n return timeglider.i18n.formatDate(fromUTC, ddlim); } else { // dates before roughly this time do not work in JS if (fromUTC.ye < -270000){ return this.ye; } else { // jsDate = new Date(fromUTC.ye, (fromUTC.mo-1), fromUTC.da, fromUTC.ho, fromUTC.mi, fromUTC.se, 0); // return Globalize.format(jsDate, sig); return tgFormatDate(fromUTC, ddlim); } } } } // end .prototype TG_Date.getTimeOffset = function(offsetString) { // remove all but numbers, minus, colon var oss = offsetString.replace(/[^-\d:]/gi, ""), osA = oss.split(":"), ho = parseInt(osA[0], 10), mi = parseInt(osA[1], 10), // minutes negative if hours are sw = (ho < 0) ? -1 : 1, miDec = sw * ( mi / 60 ), dec = (ho + miDec), se = dec * 3600; var ob = {"decimal":dec, "hours":ho, "minutes":mi, "seconds":se, "string":oss}; return ob; }; TG_Date.tzOffsetStr = function (datestr, offsetStr) { if (datestr) { if (datestr.length == 19) { datestr += offsetStr; } else if (datestr.length == 16) { datestr += ":00" + offsetStr; } return datestr; } }; /* * TG_parse8601 * transforms string into TG Date object */ TG_Date.parse8601 = function(str){ /* len str 4 YyYyYyY 7 YyYyYyY-MM 10 YyYyYyY-MM-DD 13 YyYyYyY-MM-DDTHH (T is optional between day and hour) 16 YyYyYyY-MM-DD HH:MM 19 YyYyYyY-MM-DDTHH:MM:SS 25 YyYyYyY-MM-DD HH:MM:SS-ZH:ZM */ var ye, mo, da, ho, mi, se, bce, bce_ye, tz_pm, tz_ho, tz_mi, mo_default = 1, da_default = 1, ho_default = 0, mi_default = 0, se_default = 0, dedash = function (n){ if (n) { return parseInt(n.replace("-", ""), 10); } else { return 0; } }, // YyYyYyY MM DD reg = VALID_DATE_PATTERN; var rx = str.match(reg); // picks up positive OR negative (bce) ye = parseInt(rx[1]); if (!ye) return {"error":"invalid date; no year provided"}; mo = dedash(rx[2]) || mo_default; da = dedash(rx[3]) || da_default; // rx[4] is the "T" or " " ho = dedash(rx[4]) || ho_default; // rx[6] is ":" mi = dedash(rx[5]) || mi_default; // rx[8] is ":" se = dedash(rx[6]) || se_default; // if year is < 1 or > 9999, override // tz offset, set it to 0/UTC no matter what // If the offset is negative, we want to make // sure that minutes are considered negative along // with the hours"-07:00" > {tz_ho:-7; tz_mi:-30} tz_pm = rx[7] || "+"; tz_ho = parseInt(rx[8], 10) || 0; if (tz_pm == "-") {tz_ho = tz_ho * -1;} tz_mi = parseInt(rx[9], 10) || 0; if (tz_pm == "-") {tz_mi = tz_mi * -1;} return {"ye":ye, "mo":mo, "da":da, "ho":ho, "mi":mi, "se":se, "tz_ho":tz_ho, "tz_mi":tz_mi}; }; // parse8601 TG_Date.getLastDayOfMonth = function(ye, mo) { var lastDays = [0,31,28,31,30,31,30,31,31,30,31,30,31], da = 0; if (mo == 2 && TG_Date.isLeapYear(ye) == true) { da = 29; } else { da = lastDays[mo]; } return da; }; /* * getDateTimeStrings * * @param str {String} ISO8601 date string * @return {Object} date, time as strings with am or pm */ TG_Date.getDateTimeStrings = function (str) { var obj = TG_Date.parse8601(str); if (str == "today" || str == "now") { return {"date": str, "time":""} } else { var date_val = obj.ye + "-" + unboil(obj.mo) + "-" + unboil(obj.da); } var ampm = "pm"; if (obj.ho >= 12) { if (obj.ho > 12) obj.ho -= 12; ampm = "pm"; } else { if (obj.ho == 0) { obj.ho = "12"; } ampm = "am"; } var time_val = boil(obj.ho) + ":" + unboil(obj.mi) + " " + ampm; return {"date": date_val, "time":time_val} }; // This is for a separate date input field --- YYYY-MM-DD (DATE ONLY) // field needs to be restricted by the $.alphanumeric plugin TG_Date.transValidateDateString = function (date_str) { if (date_str == "today" || date_str == "now"){ return date_str; } if (!date_str) return false; // date needs some value var reg = /^(\-?\d+|today|now) ?(bce?)?-?(\d{1,2})?-?(\d{1,2})?/, valid = "", match = date_str.match(reg), zb = TG_Date.zeroButt; if (match) { // now: 9999-09-09 // today: get today // translate var ye = match[1], bc = match[2] || "", mo = match[3] || "07", da = match[4] || "1"; if (parseInt(ye, 10) < 0 || bc.substr(0,1) == "b") { ye = -1 * (Math.abs(ye)); } if (TG_Date.validateDate(ye, mo, da)) { return ye + "-" + zb(mo) + "-" + zb(da); } else { return false; } } else { return false; } }; // This is for a separate TIME input field: 12:30 pm // field needs to be restricted by the $.alphanumeric plugin TG_Date.transValidateTimeString = function (time_str) { if (!time_str) return "12:00:00"; var reg = /^(\d{1,2}|noon):?(\d{1,2})?:?(\d{1,2})? ?(am|pm)?/i, match = time_str.toLowerCase().match(reg), valid = "", zb = TG_Date.zeroButt; if (match[1]) { // translate if (match[0] == "noon") { valid = "12:00:00" } else { // HH MM var ho = parseInt(match[1], 10) || 12; var mi = parseInt(match[2], 10) || 0; var se = parseInt(match[3], 10) || 0; var ampm = match[4] || "am"; if (TG_Date.validateTime(ho, mi, se) == false) return false; if (ampm == "pm" && ho < 12) { ho += 12; } else if (ampm == "am" && ho ==12){ ho = 0; } valid = zb(ho) + ":" + zb(mi) + ":" + zb(se); } } else { valid = false; } return valid; }; // make sure hours and minutes are valid numbers TG_Date.validateTime = function (ho, mi, se) { if ((ho < 0 || ho > 23) || (mi < 0 || mi > 59) || (se < 0 || se > 59)) { return false; } return true; }; /* * validateDate * Rejects dates like "2001-13-32" and such * */ TG_Date.validateDate = function (ye, mo, da) { // this takes care of leap year var ld = TG_Date.getMonthDays(mo, ye); if ((da > ld) || (da <= 0)) { return false; } // invalid month numbers if ((mo > 12) || (mo < 0)) { return false; } // there's no year "0" if (ye == 0) { return false; } return true; }; // make sure hours and minutes are valid numbers TG_Date.zeroButt = function (n) { var num = parseInt(n, 10); if (num > 9) { return String(num); } else { return "0" + num; } } /* * toFromUTC * transforms TG_Date object to be either in UTC (GMT!) or in non-UTC * * @param ob: {Object} date object including ye, mo, da, etc * @param offset: {Object} eg: hours, minutes {Number} x 2 * @param toFrom: either "to" UTC or "from" * * with offsets made clear. Used for formatting dates at all times * since all event dates are stored in UTC * * @ return {Object} returns SIMPLE DATE OBJECT: not a full TG_Date instance * since we don't want the overhead of calculating .rd etc. */ TG_Date.toFromUTC = function (ob, offset, toFrom) { var nh_dec = 0, lastDays = [0,31,28,31,30,31,30,31,31,30,31,30,31,29], deltaFloatToHM = function (flt){ var fl = Math.abs(flt), h = Math.floor(fl), dec = fl - h, m = Math.round(dec * 60); return {"ho":h, "mi":m, "se":0}; }, delta = {}; // Offset is the "timezone setting" on the timeline, // or the timezone to which to translate from UTC if (toFrom == "from") { delta.ho = -1 * offset.hours; delta.mi = -1 * offset.minutes; } else if (toFrom == "to"){ delta.ho = offset.hours; delta.mi = offset.minutes; } else { delta.ho = -1 * ob.tz_ho; delta.mi = -1 * ob.tz_mi; } // no change, man! if (delta.ho == 0 && delta.mi ==0) { return ob; } // decimal overage or underage after adding offset var ho_delta = (ob.ho + (ob.mi / 60)) + ((-1 * delta.ho) + ((delta.mi * -1) / 60)); // FWD OR BACK ? if (ho_delta < 0) { // go back a day nh_dec = 24 + ho_delta; if (ob.da > 1) { ob.da = ob.da - 1; } else { // day is 1.... if (ob.mo == 1) { // & month is JAN, go back to DEC ob.ye = ob.ye - 1; ob.mo = 12; ob.da = 31; } else { ob.mo = ob.mo-1; // now that we know month, what is the last day number? ob.da = TG_Date.getLastDayOfMonth(ob.ye, ob.mo) } } } else if (ho_delta >= 24) { // going fwd a day nh_dec = ho_delta - 24; if (TG_Date.isLeapYear(ob.ye) && ob.mo == 2 && ob.da==28){ ob.da = 29; } else if (ob.da == lastDays[ob.mo]) { if (ob.mo == 12) { ob.ye = ob.ye + 1; ob.mo = 1; } else { ob.mo = ob.mo + 1; } ob.da = 1; } else { ob.da = ob.da + 1; } } else { nh_dec = ho_delta; } // delta did not take us from one day to another // only adjust the hour and minute var hm = deltaFloatToHM(nh_dec); ob.ho = hm.ho; ob.mi = hm.mi; if (!offset) { ob.tz_ho = 0; ob.tz_mi = 0; } else { ob.tz_ho = offset.tz_ho; ob.tz_mi = offset.tz_mi; } ////// // return ob; var retob = {ye:ob.ye, mo:ob.mo, da:ob.da, ho:ob.ho, mi:ob.mi, se:ob.se}; return retob; }; // toFromUTC /* * TGSecToUnixSec * translates Timeglider seconds to unix-usable * SECONDS. Multiply by 1000 to get unix milliseconds * for JS dates, etc. * * @return {Number} SECONDS (not milliseconds) * */ TG_Date.TGSecToUnixSec = function(tg_sec) { // 62135686740 return tg_sec - (62135686740 - 24867); }; TG_Date.JSDateToISODateString = function (d){ var pad = function(n){return n<10 ? '0'+n : n} return d.getUTCFullYear()+'-' + pad(d.getUTCMonth()+1)+'-' + pad(d.getUTCDate())+' ' + pad(d.getUTCHours())+':' + pad(d.getUTCMinutes())+':' + pad(d.getUTCSeconds()); }; TG_Date.timezones = [ {"offset": "-12:00", "name": "Int'l Date Line West"}, {"offset": "-11:00", "name": "Bering & Nome"}, {"offset": "-10:00", "name": "Alaska-Hawaii Standard Time"}, {"offset": "-10:00", "name": "U.S. Hawaiian Standard Time"}, {"offset": "-10:00", "name": "U.S. Central Alaska Time"}, {"offset": "-09:00", "name": "U.S. Yukon Standard Time"}, {"offset": "-08:00", "name": "U.S. Pacific Standard Time"}, {"offset": "-07:00", "name": "U.S. Mountain Standard Time"}, {"offset": "-07:00", "name": "U.S. Pacific Daylight Time"}, {"offset": "-06:00", "name": "U.S. Central Standard Time"}, {"offset": "-06:00", "name": "U.S. Mountain Daylight Time"}, {"offset": "-05:00", "name": "U.S. Eastern Standard Time"}, {"offset": "-05:00", "name": "U.S. Central Daylight Time"}, {"offset": "-04:00", "name": "U.S. Atlantic Standard Time"}, {"offset": "-04:00", "name": "U.S. Eastern Daylight Time"}, {"offset": "-03:30", "name": "Newfoundland Standard Time"}, {"offset": "-03:00", "name": "Brazil Standard Time"}, {"offset": "-03:00", "name": "Atlantic Daylight Time"}, {"offset": "-03:00", "name": "Greenland Standard Time"}, {"offset": "-02:00", "name": "Azores Time"}, {"offset": "-01:00", "name": "West Africa Time"}, {"offset": "00:00", "name": "Greenwich Mean Time/UTC"}, {"offset": "00:00", "name": "Western European Time"}, {"offset": "01:00", "name": "Central European Time"}, {"offset": "01:00", "name": "Middle European Time"}, {"offset": "01:00", "name": "British Summer Time"}, {"offset": "01:00", "name": "Middle European Winter Time"}, {"offset": "01:00", "name": "Swedish Winter Time"}, {"offset": "01:00", "name": "French Winter Time"}, {"offset": "02:00", "name": "Eastean EU"}, {"offset": "02:00", "name": "USSR-zone1"}, {"offset": "02:00", "name": "Middle European Summer Time"}, {"offset": "02:00", "name": "French Summer Time"}, {"offset": "03:00", "name": "Baghdad Time"}, {"offset": "03:00", "name": "USSR-zone2"}, {"offset": "03:30", "name": "Iran"}, {"offset": "04:00", "name": "USSR-zone3"}, {"offset": "05:00", "name": "USSR-zone4"}, {"offset": "05:30", "name": "Indian Standard Time"}, {"offset": "06:00", "name": "USSR-zone5"}, {"offset": "06:30", "name": "North Sumatra Time"}, {"offset": "07:00", "name": "USSR-zone6"}, {"offset": "07:00", "name": "West Australian Standard Time"}, {"offset": "07:30", "name": "Java"}, {"offset": "08:00", "name": "China & Hong Kong"}, {"offset": "08:00", "name": "USSR-zone7"}, {"offset": "08:00", "name": "West Australian Daylight Time"}, {"offset": "09:00", "name": "Japan"}, {"offset": "09:00", "name": "Korea"}, {"offset": "09:00", "name": "USSR-zone8"}, {"offset": "09:30", "name": "South Australian Standard Time"}, {"offset": "09:30", "name": "Central Australian Standard Time"}, {"offset": "10:00", "name": "Guam Standard Time"}, {"offset": "10:00", "name": "USSR-zone9"}, {"offset": "10:00", "name": "East Australian Standard Time"}, {"offset": "10:30", "name": "Central Australian Daylight Time"}, {"offset": "10:30", "name": "South Australian Daylight Time"}, {"offset": "11:00", "name": "USSR-zone10"}, {"offset": "11:00", "name": "East Australian Daylight Time"}, {"offset": "12:00", "name": "New Zealand Standard Time"}, {"offset": "12:00", "name": "Int'l Date Line East"}, {"offset": "13:00", "name": "New Zealand Daylight Time"} ]; /* * boil * basic wrapper for parseInt to clean leading zeros, * as in dates */ function boil (n) { return parseInt(n, 10); }; TG_Date.boil = boil; function unboil (n) { var no = parseInt(n, 10); if (no > 9 || no < 0) { return String(n); } else { return "0" + no; } }; TG_Date.unboil = unboil; function getSec (fd) { var daSec = Math.abs(fd.rd) * 86400; var hoSec = (fd.ho) * 3600; var miSec = (fd.mi - 1) * 60; var bc = (fd.rd > 0) ? 1 : -1; var ret = bc * (daSec + hoSec + miSec); return ret; }; /* getMoNum * * @param mo {Number} month from 1 to 12 * @param ye {Number} straight year * */ function getMoNum (ob) { if (ob.ye > 0) { return ((ob.ye -1) * 12) + ob.mo; } else { return getMoNumBC(ob.mo, ob.ye); } }; /* * getMoNumBC * In BC time, serial numbers for months are going backward * starting with December of 1 bce. So, a month that is actually * "month 12 of year -1" is actually just -1, and November of * year 1 bce is -2. Capiche!? * * @param {object} ob ---> .ye (year) .mo (month) * @return {number} serial month number (negative in this case) */ function getMoNumBC (mo, ye) { var absYe = Math.abs(ye); var n = ((absYe - 1) * 12) + (12-(mo -1)); return -1 * n; }; function show(ob){ return ob.ye + "-" + ob.mo + "-" + ob.da + " " + ob.ho + ":" + ob.mi; } })(timeglider);