node package manager


Hierarchical time-series axis for charts with knockouts for holidays, weekends, and lots of other conveniences.


Copyright (c) 2012, Lawrence S. Maccherone, Jr.

Time axis creation/manipulation for charts





Documentation and source code


  • Generate the values for time series chart axis
  • Allows for custom granularities like release/iteration/iteration_day
  • Knockout weekends and holidays
  • Knockout non-work hours
  • Drill up and down granularity (coneceptually supported by ChartTime, consumed by Lumenize)
  • Work with precision around timezone differences
  • Month is 1-indexed instead of 0-indexed like Javascript's Date object
  • Date/Time math (add 3 months, subtract 2 weeks, etc.)
  • Tested
  • Documented


Each ChartTime object has a granularity. This means that you never have to worry about any bits lower than your specified granularity. A day has only year, month, and day segments. You are never tempted to specify 11:59pm to specify the end of a day-long timebox.

ChartTime supports the following granularities:

  • year
    • month
      • day
        • hour
          • minute
            • second
              • millisecond
    • quarter (but not quarter_month, day, etc.)
    • week (ISO-8601 style week numbering)
      • week_day (Monday = 1, Sunday = 7)

Also, you can define your own custom hierarchical granularities, for example...

  • release
    • iteration
      • iteration_day

Timezone precision

It's very hard to do filtering and grouping of time-series data with timezone precision.

For instance, 11pm in California on December 25 (Christmas holiday) is 2am December 26 (not a holiday) in New York. This also happens to be 7am December 26 GMT. If you have an event that occurs at 2011-12-26T07:00:00.000Z, then you need to decide what timezone to use as your context before you decide if that event occured on Christmas day or not. It's not just holidays, where this can burn you. Deciding if a piece of work finished in one iteration versus another can make a difference for you iteration metrics. The iteration metrics for a distributed team should look the same regardless of whether it was generated in New York versus Los Angeles... versus Bangalore.

The javascript Date object lets you work in either the local time or Zulu (GMT/UTC) time but it doesn't let you control the timezone. Do you know the correct way to apply the timezone shift to a JavaScript Date Object? Do you know when Daylight Savings Time kicks in and New York is 4 hours shifted from GMT instead of 5? Will you remember to do it perfectly every time it's needed in your code?

If you need this precision, ChartTime helps by clearly delineating the moment when you need to do timezone manipulation... the moment you need to compare two or more dates. You can do all of your holiday/weekend knockout manipulation without regard to timezone and only consider the timezone upon comparison.

Month is 1-indexed as you would expect

Javascript's date object uses 0 for January and 11 for December. ChartTime uses 1 for January and 12 for December... which is what ISO-8601 uses and what humans expect. Everyone who works with the javascript Date Object at one point or another gets burned by this.

Week support

ChartTime follows ISO-8601 where ever it makes sense. Implications of using this ISO format (paraphrased info from wikipedia):

  • All weeks have 7 days (i.e. there are no fractional weeks).
  • Any given day falls into a single week which means that incrementing across the year boundary in week granularity is without gaps or repeats.
  • Weeks are contained within a single year. (i.e. weeks are never spit over two years).
  • The above two implications also mean that we have to warp the boundaries of the year to accomplish this. In week granularity dates may appear in a different year than you would expect and some years have 53 weeks.
  • The date directly tells the weekday.
  • All years start with a Monday and end with a Sunday.
  • Dates represented as yyyyWww-d can be sorted as strings.

In general, it just greatly simplifies the use of week granularity in a chart situation.

The only real downside to this approach is that USA folks expect the week to start on Sunday. However, the ISO-8601 spec starts each week on Monday. Following ISO-8601, ChartTime uses 1 for Monday and 7 for Sunday which aligns with the US standard for every day except Sunday. The US standard is to use 0 for Sunday.

Basic usage

{ChartTime, ChartTimeIterator, ChartTimeRange} = require('charttime')

d1 = new ChartTime({granularity: 'day', year: 2011, month: 2, day: 28})
# 2011-02-28

You can use the string short-hand rather than spell out the segments seperately. The granularity is automatically inferred from how many segments you provide.

d2 = new ChartTime('2011-03-01')
# 2011-03-01

Increment/decrement and compare ChartTimes without regard to timezone

# false
# true

Do math on them.

d3 = d1.add(5)
# 2011-03-06

Get the day of the week.

# Sunday

Subtraction is just addition with negative numbers.

# 2011-02-28

If you start on the last day of a month, adding a month takes you to the last day of the next month, even if the number of days are different.

d3.addInPlace(1, 'month')  
# 2011-03-31

Deals well with year-granularity math and leap year complexity.

d4 = new ChartTime('2004-02-29')  # leap day
d4.addInPlace(1, 'year')  # adding a year takes us to a non-leap year
# 2005-02-28

Week granularity correctly wraps and deals with 53-week years.

w1 = new ChartTime('2004W53-6')
# 2005-01-01

Convert between any of the standard granularities. Also converts custom granularities (not shown) to standard granularities if you provide a rataDieNumber() function with your custom granularities.

d5 = new ChartTime('2005-01-01')  # goes the other direction also
# 2004W53-6

q1 = new ChartTime('2011Q3')
# 2011-07-01T00:00:00.000


ChartTime does timezone sensitive conversions. You must set the path to the tz files before doing any timezone sensitive comparisons.


console.log(new ChartTime('2011-01-01').getJSDate('America/New_York'))
# Sat, 01 Jan 2011 05:00:00 GMT

Iterating over ranges skipping weekends (non-workdays), holidays and non-workhours

r = new ChartTimeRange({
  start: new ChartTime('2011-01-02'),
  pastEnd: new ChartTime('2011-01-07'),
  workDays: 'Monday, Tuesday, Thursday, Friday',  # very strange work week
  holidays: [
    {month: 1, day: 1},  # Notice the lack of a year specification
    {year: 2011, month: 1, day: 3}  # Got January 3 off also in 2011

Now let's get an iterator over this range.

i = r.getIterator('ChartTime')

while i.hasNext()
# 2011-01-04
# 2011-01-06

ChartTime utils

Copyright (c) Lawrence S. Maccherone, Jr., 2012