A clean way to edit a time?

M

Max Williams

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
 
D

David A. Black

Hi --

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.

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

--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Now available: The Well-Grounded Rubyist (http://manning.com/black2)
Training! Intro to Ruby, with Black & Kastner, September 14-17
(More info: http://rubyurl.com/vmzN)
 
R

Rick DeNatale

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

=A0obj.x =3D y

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

=A0x =3D y =A0 # 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 =3D 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=3D", y) will evaluate to the return value of the setter=
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top