Calculate next reporting date

It’s been a while since my last post. I’ve been hacking along though. Here’s an interesting puzzle I faced recently.

In the app, I have an model called Corporation. Each corporation has a reporting frequency, a start date and the timezone it’s in. The task is to calculate the next (and the last) reporting date for each corporation. It seems pretty straight forward, but the devil is in the details …

The trimmed down version of my implementation is below

class Corporation < ActiveRecord::Base

  REPORTING_FREQUENCY = %w(monthly bi-monthly quarterly half-yearly yearly)
  REPORTING_FREQUENCY_INCREMENTS = { 'monthly'     => 1.month,
                                     'bi-monthly'  => 2.months,
                                     'quarterly'   => 3.months,
                                     'half-yearly' => 6.months,
                                     'yearly'      => 12.months }

  def next_reporting_date
    now = Time.now.in_time_zone(time_zone).to_date

    return start_date if start_date > now

    reporting_date(now, start_date.day, start_date)
  end

  private

  def reporting_date(now, original_start_day, start)
    if original_start_day > start.day && start.end_of_month.day >= original_start_day
      start = start + (original_start_day - start.day).days
    end

    return start if start >= now

    reporting_date(now, original_start_day, start + reporting_frequency_increment_value)
  end

  def reporting_frequency_increment_value
    REPORTING_FREQUENCY_INCREMENTS.fetch(reporting_frequency)
  end

end

The only tricky bit is the first if block inside the recursive reporting_date call. It makes sure we’re not losing days when we cycle through months that have less days than the start_date.day value.

Hope I’m making sense here … and I’m sure there are existing algorithms out there doing what I want more elegantly.

Published: 2013-03-28
blog comments powered by Disqus