   for m in matches
     [_full, timestamp, target, dyno, info] = m
 
+    timestamp = new Date(timestamp)
+
     # known things to ignore
     if startsWith info, 'info'
     else if startsWith info, 'warn'

    else if info.indexOf('noflo-runtime-msgflo:error') != -1

    # events we care about
    # FIXME: parse timestamps
    else if startsWith info, 'Scale to'
      events.push { type: 'scale-to', time: timestamp, msg: info }

    else if startsWith info, 'State changed from up to down'
      events.push { type: 'up->down', time: timestamp, dyno: dyno, msg: info }


    else if startsWith info, 'Starting process with command'
      events.push { type: 'process-starting', time: timestamp, dyno: dyno, msg: info }
    else if startsWith info, 'noflo-runtime-msgflo started'
      events.push { type: 'process-started', time: timestamp, dyno: dyno, msg: info }

    else if startsWith info, 'Process exited with status'
      events.push { type: 'process-exited', time: timestamp, dyno: dyno, msg: info }
    else if startsWith info, 'Stopping all processes with SIGTERM'
      events.push { type: 'process-stopping', time: timestamp, dyno: dyno, msg: info }

    else
      #console.log info
  return events

# Basically a finite state machine, one per dyno
applyEvent = (state, event) ->
  # DynoState:    requested  |  starting   |  up  |   stopping  | (exited)
  state.lasttransition = {} if not state.lasttransition # 'dyno.N' -> lastTransition: Event }
  state.dynostate = {} if not state.dynostate # 'dyno.N' -> DynoState
  state.startups = [] if not state.startups


  # TODO: reject invalid transitions.\
  # Note, they can happen initially because we don't generally know initial state
  if event.dyno
    # Dyno-specific events
    #console.log event.dyno, event.type
    switch event.type
      when 'process-starting'
        #console.log 'starting', event.dyno
        state.dynostate[event.dyno] = 'starting'
        state.lasttransition[event.dyno] = event
      when 'process-started'
        #console.log 'started', event.dyno
        if state.lasttransition[event.dyno] and state.dynostate[event.dyno] == 'starting'
          s =
            dyno: event.dyno
            start: state.lasttransition[event.dyno]
            end: event
          s.duration = s.end.time.getTime() - s.start.time.getTime()
          state.startups.push s

          state.dynostate[event.dyno] = 'started'
          state.lasttransition[event.dyno] = event
        else
          console.log 'invalid transition', event.type, state.dynostate[event.dyno]
      when 'starting->up' then null

      when 'up->down' then null
      when 'process-stopping' then null
      when 'process-exited' then null
  else
    # FIXME: handle. Maybe outside/before. Particularly scale-to?


analyzeStartups = (filename, callback) ->
  fs = require 'fs'

  state = {}
  fs.readFile filename, {encoding: 'utf-8'}, (err, contents) ->
    return callback err if err
    events = eventsFromLog contents
    #results = events.map (e) -> "#{e.dyno or ''} #{e.type}"
    for e in events
      applyEvent state, e
    results = state.startups.map (s) -> s.duration
    return callback null, results

exports.startuptime_main = () ->
