A clean way to edit a time?

Hi all

I have a method, created_at=, which takes a time or a string, and
changes a Time field, created_at, accordingly. If we get a time, then
it’s easy: just set created_at to be the given time.

However, if we get a string, it’s going to be only a date, like
“11/07/09”, and in this case i want to update only the day, month and
year of created_at, and leave the rest alone. In some cases, like when
a new record is made, then there will be no value in created_at, so the
method should cope with the current value of created_at being nil (i do
this by setting the time to Time.now before updating it with the new
values).

Currently i have this which feels like a bit of a hacky mess. Can
anyone show me a cleaner solution?

def created_at=(time)
  if time.kind_of?(Time)
    self[:created_at] = time
  elsif time.kind_of?(String)
    begin
      parsed_time = DateTime.strptime(time, "%d/%m/%y")
      old_time = self.created_at || Time.now
      return self[:created_at] = Time.local(parsed_time.year,

parsed_time.month, parsed_time.day, old_time.hour, old_time.min,
old_time.sec)
rescue
return false
end
end
end

Also, i can’t get it to return the new time value, and it doesn’t return
false if it raises the exception. In either case it just returns the
string that i passed to it. It’s not vital that it return the new
created_at value (or false) but it would be nice.

I’m doing this in a framework based on Ramaze so maybe the last point is
due to something inherited from that.

thanks!
max

Hi –

On Sat, 11 Jul 2009, Max W. wrote:

method should cope with the current value of created_at being nil (i do
elsif time.kind_of?(String)
end

Also, i can’t get it to return the new time value, and it doesn’t return
false if it raises the exception. In either case it just returns the
string that i passed to it. It’s not vital that it return the new
created_at value (or false) but it would be nice.

To start with the last point: the setter methods are engineered to
look like and behave like assignments. So when you do this:

obj.x = y

it always returns y. It’s emulating assignment semantics:

x = y # always returns y

So using ‘return’ with an argument in those methods doesn’t work.

As for your main question: I’m not sure whether this does exactly what
you want (though I’ve got some tests that suggest perhaps it does),
but here’s a version that might at least give you some ideas:

require ‘time’

class Thing
attr_reader :created_at

def created_at=(new_time)
if new_time == new_time.to_s
now = Time.now
str = “#{new_time} #{now.strftime(’%H:%M:%S’)}”
@created_at = Time.parse(str)
else
@created_at = new_time
end
end
end

The idea, as you can see, is to piece together a string that contains
the year, month, and day from the string that was passed in, plus the
hour, minute, and second from Time.now. I think it does what you want
with correct input, though some further checking for correctness might
be in order.

David

On Sun, Jul 12, 2009 at 9:14 AM, David A. Black[email protected]
wrote:

To start with the last point: the setter methods are engineered to
look like and behave like assignments. So when you do this:

obj.x = y

it always returns y. It’s emulating assignment semantics:

x = y # always returns y

So using ‘return’ with an argument in those methods doesn’t work.

I think to be brutally correct, it’s not that a return in a setter
method doesn’t work it’s that

obj.x = y

will call the setter method with y as the argument, and discard any
return value. The value of the assignment expression will always be
the value of y, not the value returned by the setter method.

whereas obj.send(“x=”, y) will evaluate to the return value of the
setter.

class Foo
attr_reader :x_squared
def x=(value)
@x_squared = value * value
end
end

obj = Foo.new

obj.x = 4 # => 4
obj.x_squared # => 16

obj.send(“x=”, 3) # => 9
obj.x_squared # => 9


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale