How Accurate Is Your Clock?
I bumped my head against another one. While Ruby is not equal in all environments, I at least want to control the difference wherever possible. When you do a `Time.now`, you'll get something like {% highlight text %} Tue May 20 17:08:21 -400 2008 {% endhighlight %} Lets say that you're formatting that time for a log message though... {% highlight ruby %} Time.now.strftime "%m/%d/%y %H:%M:%S" {% endhighlight %} which gives {% highlight text %} 05/20/08 17:08:21 {% endhighlight %} Great, except that you'd like *sub-second* precision. {% highlight ruby %} (time = Time.now).strftime "%m/%d/%y %H:%M:%S." << ("%06d" % time.usec) {% endhighlight %} Well, ruby on my iMac gives me six digits of microseconds, {% highlight text %} 05/20/08 17:08:21.943286 {% endhighlight %} but alas, my PC only three, {% highlight text %} 05/20/08 17:08:21.943000 {% endhighlight %} While you may say, "So what?" I must reply, "Yuck." I just don't want those empty zeros hanging out there. The purist in me wants to lop them off. What I want is {% highlight ruby %} (time = Time.now).strftime "%m/%d/%y %H:%M:%S." << ("%06d" % time.usec)[0,@usecs] {% endhighlight %} where `@usecs` is the sub-second precision of the clock. Now, I could just use the Config to get the platform I'm on and assign the correct value, but in this instance I'd like to be more proactive. I can figure out the right value empirically. I start by getting an Array of sample microseconds that are slightly spread out in time. {% highlight ruby %} t = (1..5).collect { sleep 0.001001; "#{Time.now.usec}" } {% endhighlight %} Then I figure out what digit contains the last non-zero digit {% highlight ruby %} nz = t.collect { |s| s.length - (/[1-9]/ =~ s.reverse) } {% endhighlight %} And finally, I just take the max {% highlight ruby %} @usecs = nz.max {% endhighlight %} Why the multiple samples? Because there's still a one in ten chance that a zero will occur naturally in the real non-zero digit position. Or one in one hundred for two zeros, or one in a thousand for three. By running multiple samples and taking the max, we won't be likely fooled, statistically speaking. Why five samples? I just figure that those odds are pretty darned good. Of course, we can collapse this all nicely, {% highlight ruby %} class Time @@subsecond_precision = nil def self.subsecond_precision @@subsecond_precision ||= (1..5).collect { sleep 0.001001 s = "#{Time.now.usec}" s.length - (/[1-9]/ =~ s.reverse) }.max end end {% endhighlight %} Now I can just use `Time.subsecond_precision` in place of `@usecs` above. I don't have to worry about using system-dependent assignments. I can just do it once when I need it and move forward.