# Fuzzy Time (#99)

**URL:** <https://www.ruby-forum.com/t/fuzzy-time-99/225927>\
**Category:** Ruby\
**Created:** [November 7, 2006, 10:56am UTC](https://www.ruby-forum.com/t/fuzzy-time-99/225927 "2006-11-07T10:56:36Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![phrogz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/2acd7d/32.png) [@phrogz](https://www.ruby-forum.com/u/phrogz)\
**Post date:** [November 7, 2006, 10:56am UTC](https://www.ruby-forum.com/t/fuzzy-time-99/225927/1 "2006-11-07T10:56:36Z")

</div>

The core requirements for this quiz were somewhat easy. Let’s look at  
what needed to be done, and how some people accomplished it:

1. How do you represent the internal time?

While it would be possible to manage the time yourself, everyone  
(sensibly) chose to use Ruby’s built-in Time class to represent the  
‘real’ time behind the fuzz. Everyone gets a pat on the back.

1. How to you handle the ‘overloaded’ constructor?

How do you write one initialize function that allows you to specify a  
start time or omit it? It seems almost silly to focus on it, and yet  
it’s a common enough need that it’s important to know how to do it  
‘right’.

There were a few approaches to this. The simplest, I think, is to use  
Ruby’s default values in the method definition. As Daniel L. wrote:

def initialize( time = Time.now )  
@start\_time = time  
end

Not only is this easy to write, but it also documents nicely. “If you  
don’t give me a value for the time parameter, I’m going to use Time.now  
instead.” RDoc handles this case when generating documentation.

A variation is how Tom P. handled it:

def initialize ( actual=nil )  
@actual = actual || Time.new()  
end

The above says to the user, “You may pass me an actual time or not, but  
what value I use if you don’t give it to me is a detail you probably  
don’t need to know about.” Under some circumstances, this sort of  
implementation hiding might be preferable (coupled with good  
documentation about what it means to not supply the value).

Less ideal (but functional) variations included:

def initialize(\*args)  
now = Time.new  
@internal\_time = args[0] || now  
end

def initialize(\*args)  
if args.size == 0  
@timeshift = 0  
else  
@timeshift = Time.now.to\_i - args[0].to\_i  
end

Writing code like this certainly works, but it makes the code less  
self-explanatory.

1. How do you convert to a fuzzy string?

I was surprised by the variation in this category. The major variations:

def to\_s  
@my\_time.strftime(“%H:%M”)[0…3] + “~”  
end

def to\_s  
s = @my\_time.strftime(“%H:%M”)  
s[4] = ‘~’  
s  
end

def to\_s  
fuzzy\_hour, fuzzy\_min, fuzzy\_sec = get\_time  
“#{fuzzy\_hour}:#{fuzzy\_min / 10}~”  
end

def to\_s  
sprintf(‘%02d:%d~%s’,  
@mode == 24 ? @my\_time.hour : @my\_time.hour % 12,  
@my\_time.min / 10,  
@mode != 24 ? @my\_time.hour / 12 == 1 ? ’ pm’ : ’ am’ : ‘’  
)  
end

I like the one-line simplicity of the first. It happens to create three  
strings in the process of creating the final, while the second  
(annoying-to-type, ugly) technique creates just one. I personally went  
with the second approach (create a string and then replace a character  
in-place) in the pointless pursuit of memory savings. Premature  
optimization at its finest, I think.

1. How do you keep track of the last displayed time?

Because the Time class plays well with seconds as a sort of atomic base  
unit, some people chose to keep the ‘fuzzy’ time being displayed  
internally as an offset from the real time maintained internally. Others  
(including myself) chose to maintain the last displayed time as a  
separate Time instance. I don’t see a major benefit of using one over  
the other, but wanted to point out the alternative.

1. How do you ensure no backtracking?

This was the trickiest part of the quiz (outside of the ‘extra credit’  
options). The requirement was that the time never display a time earlier  
than the last displayed time. I originally (naively) coded this roughly  
as:

@time\_to\_display = … #some Time instance  
if @time\_to\_display \< @last\_displayed\_time  
@time\_to\_display = @last\_displayed\_time  
end

# (Display the time here)

@last\_displayed\_time = @time\_to\_display

The problem with this approach is that it’s overly restrictive. For  
example, assume that the last time shown by the class was 10:49, which  
was displayed at “10:4~”. If the class then generates a random time to  
display of 10:45, the above code will say “Nope, that’s before 10:49,  
gotta stick to 10:49”. Of course, 10:45 is a perfectly reasonable time  
to use internally, since it still displays as “10:4~”.

Using any sort of approach that adds or subtracts an offset from a  
moving ‘fuzzy’ time, the above approach is like the ratchets on a roller  
coaster hill. Each step forward you take prevents you from coming  
backwards. Before you know it, your wandering time is pinned as far  
forward as it can go.

The desire, then, is to ensure that the internal time never becomes  
earlier than the bottom of the _displayed_ version of the internal fuzzy  
time.

In my test case (because I had no access to the internals of the class),  
I did this by parsing the string and backing it up manually.

y,mon,day = t.year, t.mon, t.day  
h,m = last\_value.scan(/\d+/).map{ |s| s.to\_i }  
m \*= 10  
if (m -= 10) \< 0  
m %= 60  
if (h -= 1) \< 0  
h %= 24  
end  
end  
illegal\_old\_value = Time.local( y,mon,day,h,m ).to\_fuzzy

Ugh. Time is so messy. In my class, I used the fact that rounding the  
internal seconds of a time class to a resolution of 600 gives you a time  
rounded to 10 minutes:

class Time  
def round\_to( seconds )  
seconds = seconds.round  
Time.at( self.to\_i / seconds \* seconds )  
end  
end  
…  
TEN\_MINS = 10 \* 60  
…  
if @fuzzy.round\_to( TEN\_MINS ) \< @last\_fuzzy.round\_to( TEN\_MINS )

However, Jeremy H. rounded the minutes down to 10 _before_  
displaying, and used that to create a new Time:

min = (@fuzzed.min / 10) \* 10  
@display = Time.mktime( @fuzzed.year, @fuzzed.month, @fuzzed.day,  
@fuzzed.hour, min, 0, 0)

This is nice, because the same Time instance used to display the time is  
also the lower limit for the next display. Nice work, Jeremy!

That’s most of the interesting stuff from the quiz…outside of the  
extra credit. Particularly the bit about trying to get an even  
distribution of random times. Other extra credit solutions were nice -  
check out the code for some interesting ways to round time to various  
degrees. But I wanted to mention this one just a bit.

1. How do you attempt even distribution?

I’m not a statistician. For most of this topic, I suggest that you read  
some of the very interesting discussions[1] that took place on the  
mailing list.

At first, I thought I was a statistician. I started writing this out in  
my code.

# The chance of success after n attempts of something with probability p

# per attempt is: s = 1-(1-p)\*\*n

# 

# Solving for p:

# 1-s = (1-p)\*\*n

# (1-s)\*\*(1/n) == 1-p

# p == 1 - (1-s)\*\*(1/n)

# 

# I want to have a 50% chance of moving ahead at the exact correct time,

# so if I am going to make n updates between -5 minutes and the correct

# time, I should try for a per attempt chance of p = 1 - 0.5\*\*(1.0/n).

# 

# This leaves me with a total chance of success of 75% after 2n

# attempts (+5 minutes), which (I think) means that the algorithm will

be

# weighted towards running on the not-too-fast side. That’s probably OK,

# given the one-way time valve.

Then I went to put this idea into code. I wrote some code that  
automatically tracked how frequently the advance method was being called  
(using a simple low-pass filter instead of a running average). I used  
that number to calculate how many times per minute it was being called,  
and calculated the ideal probability to use per attempt.

I wrote:

if rand \< per\_advance\_probability  
# Uhm…what do I do here?

Then I punted and went with a simple even distributation probability  
instead. (See the solution I submitted.) I just couldn’t figure out  
where to go from there.

My point, other than sharing my pain, is twofold:

1. Having a rough, good idea of how to solve a problem is not the same  
as knowing how to solve it. My advice (to myself) is not to start  
randomly writing code, HOPING that when I get to the bit I’m not sure of  
that it will all work out. Time gets wasted that way.

2. It’s amazing how even simple programming problems can require some  
interesting cross-disciplinary knowledge, like probability and  
statistics.

Finally, I wanted to mention how much of a challenge it turned out to be  
trying to write a test for random distribution. Min/maxes, means and  
standard deviations can give you an idea of how random some output  
is…but in this case a big curtain was being held up over half the  
output. If the time is 10:47 and the program displays “10:4~”, I have no  
idea if internally it’s running fast, slow, or on time. The only way I  
could think to test for randomness (other than peeking at the internals  
of the class) was to watch for the change from one displayed value to  
the next and see at that point how fast/slow the class was running.

Thanks to everyone for contributing to this quiz! Great to see some new  
contributors. Yet again the requirements that I provided turned out to  
have some holes in them, which I hope that the test cases I provided  
helped to clarify my intent. I’ve decided I won’t apologize for that,  
and instead use it as a life lesson on edge cases and getting clear  
problem specifications from your boss/client. 🙂

Which gives me an interesting idea for a new quiz…  
Hmmmm…yes…

See you next time!

[1]  
[http://groups.google.com/group/comp.lang.ruby/browse\_frm/thread/7e6b564a](http://groups.google.com/group/comp.lang.ruby/browse_frm/thread/7e6b564a)  
10d65735/dc30cb4ade9531d9#dc30cb4ade9531d9
