From: "David A. Black" Date: 2009-07-12T22:14:36+09:00 Subject: Re: A clean way to edit a time? Hi -- On Sat, 11 Jul 2009, Max Williams wrote: > 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)