Kevin Behan Kevin Behan - 2 years ago 127
Ruby Question

Ruby on Rails parsing time from datetime, not as string

I'm trying to extract the time component from a DateTime object (which is represented as "at" in my example). How do I do this, I am absolutely stumped? (I don't want to parse it to a string with

as i did here):"%H:%M")

I would really like to return the hours and minutes as a Time object.

Answer Source

Is there a specific reason you want a Time object?

Just so we're clear, the Time class in Ruby isn't just "DateTime without the date." As "What's the difference between DateTime and Time in Ruby?" explains, "Time is a wrapper around POSIX-standard time_t, or seconds since January 1, 1970." Like DateTime, a Time object still has year, month, and day, so you don't really gain anything by using Time instead. There's not really a way to represent just hour and minute using either Time or DateTime.

The best you could do with Time, I think, would be this:

date_time =
seconds = date_time.hour * 60 * 60 + date_time.minute * 60

time =
# => 1970-01-01 09:58

...but then you still have to call time.hour and time.min to get at the hour and minute.

If you're just looking for a lightweight data structure to represent an hour and minute pair, though, you might as well just roll your own:

HourAndMinute =, :minute) do
  def self.from_datetime(date_time)
    new(date_time.hour, date_time.minute)

hm = HourAndMinute.from_datetime(
# => #<struct HourAndMinute hour=15, minute=58>
# => { :hour => 15, :minute => 58 }
# => [ 15, 58 ]

Edit re:

I have a variable that stores an appointment -- this variable is a DateTime object. I have two table fields that store the start and end times of a location. I need to check if the time scheduled for that appointment lies between the start and end times.

Ah, it seems you had a bit of a XY problem. This makes a lot more sense now.

Absent any more information, I'm going to assume your "fields that store the start and end times of a location" are MySQL TIME columns called start_time and end_time. Given MySQL TIME columns, Rails casts the values to Time objects with the date component set to 1/1/2000. So if your database has the values start_time = '09:00' and end_time = '17:00', Rails will give you Time objects like this:

start_time =, 1, 1, 9, 0) # => 2000-01-01 09:00:00 ...
end_time =, 1, 1, 17, 0)  # => 2000-01-01 17:00:00 ...

Now you say your appointment time is a DateTime, so let's call it appointment_datetime and suppose it's at 10:30am tomorrow:

appointment_datetime =, 11, 18, 10, 30) # => 2014-11-18 10:30:00 ...

So now to rephrase your question: How do we tell if the time part of appointment_datetime is between the time part of start_time and end_time. The answer is, we need to either change the date part of start_time and end_time to match the date part of appointment_datetime, or the other way around. Since it's easier to change one thing than two, let's do it the other way around and change appointment_datetime to match start_time and end_time (and, since those two are Time objects, we'll create a Time object):

appointment_time =, 1, 1, appointment_datetime.hour, appointment_datetime.minute)
# => 2000-01-01 10:30:00 ...

Now we can compare them directly:

if appointment_time >= start_time && appointment_time <= end_time
  puts "Appointment time is good!"

# Or, more succinctly:
if (start_time..end_time).cover?(appointment_time)
  puts "Appointment time is good!"

You would, of course, want to wrap all of this up in a method, perhaps in your Location model (which, again, I'm assuming has start_time and end_time attributes):

class Location < ActiveRecord::Base
  # ...

  def appointment_time_good?(appointment_datetime)
    appointment_time =, 1, 1,
                         appointment_datetime.hour, appointment_datetime.minute)


location = Location.find(12) # => #<Location id: 12, ...>
location.appointment_time_good?(appointment_time) # => true

I hope that's helpful!

P.S. Another way to implement this would be to ditch the date/time objects entirely and do a straight numeric comparison:

def appointment_time_good?(appointment_datetime)
  appointment_hour_min = [ appointment_datetime.hour, appointment_datetime.minute ]

  appointment_hour_min >= [ start_time.hour, start_time.min ]
    && appointment_hour_min <= [ end_time.hour, end_time.min ]
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download