JavaScript DateTime
A library of datetime manipulations (ripped from the python datetime module) for manipulating date times, especially for plotting.
Browse Code in SVN Repository
Date, Time, Datetime, Duration (absolute ms), Span/Interval (start&end)
- These must be supported seperately. Just having a time means you need a default date assumption. UNIX and the standard C library assumes January 1, 1970. Microsoft COM system assumes December 30, 1899 C.E. while the .NET Framework assumes January, 1, 0001 C.E.
- DateTime represents an instant in time whereas a TimeSpan represents a time interval.
- Additions to a datetime object: isLeapYear, isDaylightSavings
- Daylight savings time makes some specifications of a local datetime illegal (spring forward) and others ambiguous (fall back)
- Julian Day number. This is the number of days since noon U.T.C. on January 1, 4713 B.C. (Julian calendar). This time scale was originally proposed by John Herschel, and is often used in astronomical calculations.
- You need 50 bits to represent the number of seconds since the big bang 13.6 billion years ago. You need 80 to represent the number of nanoseconds.
- Excel uses a different system for its dates than most Unix programs. Excel uses ``number of days since 31 Dec 1899''. Naturally, Microsoft messed this up because they happened to believe that 1900 was a leap year.
- US Geological Survey (USGS) likes midnight to be 24:00:00 of the previous day, not 00:00:00 of the day people expect.
- Incomplete date/times like birthdays where you often only have a month and day. Of course, the math you can do with these is limited because of things like leapyears. (These are different from abstract timeswhich can be affected by timezone.)
- ISO 8601 has a concept of repatition period.
- R/P3M means every three months (not absolute ms) R/P123D means every 123 days and PT12.345S means 12.345 seconds (both of these are an absolute number of ms, but they are defined not to add to a dateTime as milliseconds because of leap seconds).
- Only if you specify PT12.345S do you get absolute seconds/ms. Thus the addition of either PT1M or PT60S to any dateTime will always produce the same result. This is a special definition of addition which is designed to match common practice witout regard to leap seconds, and -- most importantly -- be stable over time.
- If you really want to add an absolute duration in miliseconds, convert to and from an epoc. This is the difference between UTC, which adjusts itself with leap seconds and International Atomic Time (TAI) which always marches forward.
- You can't specify repeated events of the form "2nd Tuesday of each month/year" or "Evey Mon, Wed, Fri"
- UNIX time defined by POSIX strictly chugs along seconds since Jan 1 1970, like TAI. However, NTP alters this behavior. NTP makes the UNIX clock jump forward or backward to keep in line with UTC.
- Many of the issues of plotting date & stuff are the same as scheduling repeating events, so perhaps some of the iCalendar standard can be used/implimented.
- jsDateTime is somebody's shareware. DateTime.js is MochiKit.
- Everybody cares about Universal Coordinated Time UTC and timezone offsets from it (this is the modern version of GMT.) Scientists and engineers care about International Atomic Time TAI because it chugs along with no leap seconds so absolute differences are easy to calculate. Astronomers care about Siderial Time, Universal Time, Julian Days, etc. Historians care about time in other calendars, and times before this was all worked out in the 1970s.
- Leap seonds: If you add P2S (two seconds) to 1998-12-31 23:59:59.00, what should you get? The next second is a leap second, so one second later is technically 1998-12-31 23:59:60.00 and two seconds later is 1999-01-01 00:00:00.00. If you didn't know about leap seconds, you would get 1999-01-01 00:00:01.00. 99.99% of the time, this is what people mean and this is how ISO 8601 defines it.
- If you really want to go out of your way to find the real difference between two UTC timestamps or to add a real difference, you need to have a table of the leapseconds. One way to do this properly would be to convert both to TAI, do your additions or subtractions, and then convert back.
Refererences
- Python datetime module (includes date, time, datetime, timedelta, and tzinfo) - timedeltas are treated as days, seconds, and miliseconds exactly. This makes it hard to go to the same day on the next year or the next month. This is what python-dateutil is for.
- Perl DateTime, datetime.perl.org - timedeltas are not converted to absolute miliseconds. You can represent "one year" and add it. This causes some ambiguity.
- Dojo JavaScript library has date stuff.
- iCalendar (RFC 2445) for scheduling interoperability Wikipedia ICalendar
- ISO 8601 datetime format (supported in MochiKit) Wikipedia ISO 8601
- W3C Date and Time Formats (subset of ISO 8601), XMLSchema with a nice algorithm for addition
- ISO C strftime (used by python and PHP) strptime python parser
- Table of leap seconds
Plotting Dates & Times
- These can either be a category type scale or an number type scale. For a histogram of web hits, it's a category. How many hits came all of Monday, how many came all of Tuesday. For a stock price line-graph, it's a number type scale. You have data for the stock price at exactly 2:34:00pm.
- People see years, months, weeks, and days as being intervals: on March 11 implies anywhere from 00:00:00.00 to 11:59:59.99 on that day. Times are viewed as points: at 3:00. This makes auto-labeling challenging. This means that even if you're ploting things like stock prices, years, months, and days should be treated like categories. What about when you label every 5th day (Dec 20, Dec 25, Jan 1, Jan 5, Jan 10, Jan 15, Jan 20) or every 3rd Month (Jan'06, Apr'06, Jul'06, Oct'06, Jan'07)? Here you have to put the label on the tick. Google Finance has an interesting thing of offsetting the times a little to the right so they are against the tick, but under the hour they span.
- Also, "every five days" doesn't really cut it when making ticks and labels for a month or two. Dates don't start at zero, so you'd like to include 1 as the reference start of the next month, but 1, 6, 11, 16, 21, 26, 31 does't look good, nor will there always be a 31. On the other hand 1, 5, 10, 15, 20, 25, 30 is not evenly spaced and again there isn't always a 30, but a bigger problem is that 30 and 1 will end up too close to label. Beginning and middle 1, 15, 1, 15 is pretty good, though not even. Maybe better than 1, 15, 30, 1, 15, 30 when February is included.
- Idea of putting higher level information only at beginning and end: (Jan 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 1, 2, 3, 4, 5, 6, 7, 8, 9, Feb 10)
- For Months, multiples of 1, 4 (Mar, Jun, Sep, Dec) or (Jan, Apr, Jul, Oct), 6 (Jan, July or Jun, Dec)
- For dates, multiples of three and four are not good. Only 1, 2, 5, 10
- For hours, 1, 2, 3, 4, 6, 8, 12
- For minutes an seconds, 1, 2, 5, 10, 20, 30
- For each possibility see if actual text would overlap and if so, go to the next biggest? (This takes a long time.)
How lables on absolute datetime ranges are formatted
Few 1-4 (most info -- only what changes and one constant) |
Medium 5-10 (a shortened version of Few) | Lots >10 (top of two lines - the largest interval that changes more than 10 or more times in the range) | |
---|---|---|---|
Many Years | 1980, 1990, 2000, 2010 | '80, '85, '90, '95, '00, '05, '10, '15 | 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 00, 02, 04, 06, 08, 10 |
Few Years | 1998, 1999, 2000, 2001 | ||
One Or Two Years | January 1999, June 1999, January 2000, June 2000 | Oct99, Nov99, Dec99, Jan00, Feb00, Mar00, | JFMAMJJASONDJFMAMJJASONDJFM |
12 Months | Mar 99, Jun 99, Sep 99, Dec 99, Mar 00, | ||
Few Months | December 1999, January 2000, February 2000, March 2000 | Dec 15, Jan 1, Jan 15, Feb 1, Feb 15, Mar 1 or 1-Jan, 15-Jan, 1-Feb, 15-Feb, 1-Mar |
12/15, 12/20, 12/25, 1/1, 1/5, 1/10, 1/15, 1/20, 1/25, 2/1, 2/5, 2/10, 2/15, 2/20, 2/25, 3/1, 3/5 or 15-D, 20-D, 25-D, 1-J, 5-J, 10-J, 15-J, 20-J, 25-J, 1-F, 5-F, 10-F, 15-F, 20-F, 25-F, 1-M, 5-M |
30 Days | Dec 20 1999, Jan 1 2000, Jan 10 2000, Jan 20 2000 | Dec 20, Dec 25, Jan 1, Jan 5, Jan 10, Jan 15, Jan 20 | dd |
15 Days | Dec | dd | |
Few Days | Dec 31 12:00pm, Dec 31 6:00pm, Jan 1 12:00am, Jan 1 6:00am (every day) | ||
Few Hours | |||
10-60 Minutes | mm-dd h:mm:ss | h:mm:ss | mm |
1-10 Minutes | mm-dd h:mm:ss | mm:ss | ss |
1-60 Seconds | h:mm:ss | mm:ss | ss |
Less than a second | h:mm:ss.u | ss.u | .uu |
Calculating Days of Week & Leap Years
char *day_of_week(int day, int month, int year) { int cent; char *dayofweek[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; /* adjust months so February is the last one */ month -= 2; if (month < 1) { month += 12; --year; } /* split by century */ cent = year / 100; year %= 100; return (dayofweek[((26 * month - 2) / 10 + day + year + year / 4 + cent / 4 + 5 * cent) % 7]); } int leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }