/* -*-c-*- * iso8610.zkl : Do some date calculations * ISO 8601: Numeric representations of date and time * YYYY-MM-DD * Monday (1) ... Sunday (7) * hh:mm:ss, 24:00 == 00:00 (00:00 is preferred) * YYYYMMDDThhmmss * UTC time is hh:mm:ssZ, offsets are +|-hh[:mm] * 12:00Z = 13:00+01:00 = 0700-0500 * * YYYY-MM-DDThh:mm:ss * where the capital letter T is used to separate the date and time * components. Thus, for a very precise date and time, look at this: * Example: 2003-04-01T13:01:02 represents one minute and two seconds * after one o'clock in the afternoon of 2003-04-01. * * http://wikipedia.org/wiki/ISO_8601 * http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm * * Documentation is in zklManual.pdf Appendix B * * Copyright (c) 2006, 2007, 2008 Craig Durland craigd@zenkinetic.com * License: The zlib/libpng License, * http://www.opensource.org/licenses/zlib-license.php */ AKA(Time.ISO8601); Attributes(static,noChildren); var [const] TheTime = TheVault.Time.Clock, // TheVault needed: "class Time" (below) TimeDate = Import("Time.Date"); const BASIC_FORMAT = 1; const EXTENDED_FORMAT = 2; const WEEK_FORMAT = 3; fcn init(dateString=Void) // 8601 to Date, Repeater, or Duration { if (not dateString) returnClass(DateTime()); if (dateString[0] == "P") returnClass(Duration(dateString)); if (dateString[0] == "R") returnClass(Repeater(dateString)); returnClass(DateTime(dateString)); } /* Formats: * hh:mm:ss == hhmmss * hh:mm == hhmm * hh * 00:00 == 24:00 here, in a date means start of next day * hh:mm:60 == leap second * Fractional time: can only apply to most precise component (ie * the last one). Use comma or dot. */ class Time { var hour= 0, // 0 ... 23 min = 0, // 0 ... 59 sec = 0, // 0 ... 61 format = text; fcn init(h=Void,m=0,s=0) // ("hh:mm:ss") { if (Void == h) now(); // get current time else if (h.isType("")) parse(h); else { hour = h; min = m; sec = s; } } fcn toString { return("%s(%s)".fmt(topdog.name,text())); } fcn now { x,x,x,hour,min,sec := TheTime.localTime; return(self); } fcn text { return("%02d:%02d:%02d".fmt(hour,min,sec)); } fcn toAMPM { hh := hour; ampm := "AM"; if (hh > 12) { hh -= 12; ampm = "PM"; } return("%02d:%02d:%02d%s".fmt(hh,min,sec,ampm)); } fcn read { now(); topdog.format(); } fcn addHMS(h,m,s) { sec += s; min += m; hour += h; min += sec / 60; hour += min / 60; overflow := hour / 24; hour = hour % 24; min = min % 60; sec = sec % 60; return(overflow); } fcn subHMS(h,m,s) { if ((sec -= s) < 0) { sec += 60; min -= 1; } if ((min -= m) < 0) { min += 60; hour -= 1; } if ((hour -= h) < 0) { hour += 24; -1 } else 0; // returns underflow } fcn add(aTime) { return(addHMS(aTime.hour,aTime.min,aTime.sec)); } fcn sub(aTime) { return(subHMS(aTime.hour,aTime.min,aTime.sec)); } fcn parse(text="") { if (not text) now(); else hour,min,sec = textToHMS(text); return(self.text()); } fcn textToHMS(text) { reg hh=0,mm=0,ss=0; if (not text) { x,x,x,hh,mm,ss := TheTime.localTime; return(hh,mm,ss); } time := text.strip() - " "; if (time.holds(":")) // h:m:s, ::s, h::s, etc { splits := time.split(":").apply(fcn(x) { (x or 0).toInt() }); try { hh,mm,ss = splits; } catch(IndexError) { hh,mm = splits; } } else { try { hh = (time[0,2] or 0).toInt(); mm = (time[2,2] or 0).toInt(); ss = (time[4,2] or 0).toInt(); // hh = 24 --> next day } catch { throw(Exception.ValueError( "Time: Invalid time: %s".fmt(text))); } } if (hh < 0 or hh > 23 or mm < 0 or mm > 59 or ss < 0 or ss > 59) throw(Exception.ValueError("Time: Invalid time: %s".fmt(text))); return(hh,mm,ss); } fcn secondsToHMS(seconds) { reg h = (seconds / 3600); if (seconds < 0) seconds = -seconds; reg m = (seconds % 3600) / 60; reg s = (seconds % 60); return(h,m,s); } fcn toSeconds { return(hour * 3600 + min * 60 + sec); } fcn toFloat { return(TimeDate.toFloat(hour,min,sec)); } } /* Time zones: *