Friday 21 September 2007

quarter-master

Continuing my series of "why isn't this in ruby already?", we have some date functions that allow us to add and display the current financial quarter. I have even overridden "strftime" to accept the symbol '%Q' to display the quarter.

class Time
  # save the original strftime so we can call it later
  alias original_strftime :strftime
  # overload strftime to accept '%Q' to display the quarter
  def strftime(format)
    format = format.gsub('%Q', "Q#{self.quarter.to_s}") if format
    self.original_strftime format
  end

  # create a utc time using the year and financial quarter
  def self.utc_fq(year, fq = 1)
    self.utc(year, self.quarter_to_month(fq))
  end
  # create a local time using the year and financial quarter
  def self.local_fq(year, fq = 1)
    self.local(year, self.quarter_to_month(fq))
  end

  # turns a financial quarter (1-4) into the first month of that quarter
  def self.quarter_to_month(fq)
    ((fq-1)  * 3) + 1
  end
  # turns a given month (1-12) into the financial quarter
  # (1=jan-mar, 2=apr-jun, 3=jul-sep, 4=oct-dec)
  def self.month_to_quarter(month)
    ((month - 1) / 3) + 1
  end

  # returns the financial quarter of this date
  # (1=jan-mar, 2=apr-jun, 3=jul-sep, 4=oct-dec)
  def quarter
    self.class.month_to_quarter(self.month)
  end
end

Example of use:

>> order_fq = Time.utc_fq(order.created_at.year, order.created_at.quarter)
=> Sun Jul 01 00:00:00 UTC 2007
>> p "Order's financial quarter: #{order_fq.strftime("%Y-%Q")}"
"Order's financial quarter: 2007-Q3"
=> nil

Wednesday 19 September 2007

methods in_time

On the subject of "why isn't this in Ruby already?". I've written up a set of conversion methods that I felt sure were there already, except that I couldn't find them anywhere.

class Numeric
  # assumes i am expressed in seconds - converts me to minutes
  def in_minutes
    self / 60
  end
  # assumes i am expressed in seconds - converts me to hours
  def in_hours
    self / 3600
  end
  # assumes i am expressed in seconds - converts me to days
  def in_days
    self / (3600 * 24)
  end
end

So now I can do things like: 3600.in_hours, but also the more useful: (prevTime - Time.now).in_days

Monday 17 September 2007

float precision

So, I want to test that some floating-point helper function returns a certain value - but I only want it to check to a certain precision (so I don't get hit with negligible rounding errors).

Add this to config/environment.rb (and why doesn't this already exist in Ruby?)

class Float
  def precision(pre)
    mult = 10 ** pre
    (self * mult).round.to_f / mult
  end
end

Then you can do things like this:

assert_equal -5087.35, order1.profit_loss.precision(2)

[Edit: I've replaced 'truncate' with 'round' - it's the only change I've made in two years and I'm still using this code... maybe I should put it into the Rails core-extensions instead?]