Ruby: Combine Date and Time Objects into a Datetime

Create date and time from separate strings

You can combine a date and a time like this:

date = '2014-07-09'
time = '9:30am'

DateTime.parse([ date, time ].join(' '))
# => Wed, 09 Jul 2014 09:30:00 +0000

I've got my local time-zone in Rails set to UTC.

Ruby merge time and date to datetime

You can construct a new DateTime using DateTime.new and passing the single date components such as year and month.

class Product < AR::Base
MANUFACTURE_TIME = '08:00'

def manufactured_at
hour, minute = MANUFACTURE_TIME.split(':')
DateTime.new(manufacture_date.year, manufacture_date.month, manufacture_date.day, hour, minute)
end
end

You can store the time as you want, even in a array if you don't want to decompose it later using split.

Union arrays of date time object

Here's one solution:

parse = ->(r) { DateTime.parse(r[:start])..DateTime.parse(r[:end]) }
ranges = list1.map(&parse) + list2.map(&parse)

merged = ranges.sort_by(&:begin).reduce([]) do |acc, range|
if acc.any? && acc.last.cover?(range.begin)
last = acc.pop
acc.push last.begin..range.end
else
acc.push range
end
acc
end

Your solution doesn't "merge" the time ranges, because it just sorts the list of times by ascending distance between start times. This will obviously not produce any useful merging of times!

I'm using Ruby's Range construct to create a time range from each start/end pair, then I sort all the ranges by their beginning time, then reduce the list of individual ranges with the algorithm:

if the current range covers the next range's start date:
extend the current range to the next range's end date
else
add the next range into the return list

It's worth noting that I'm just merging all the ranges via any intersections; there's no need to split them into two lists, because if there is an overlapping range in one list or the other, they would union together anyhow.

You don't have to use ranges, but they map onto this concept cleanly, and you can then use them to produce intervals or whatever you're looking to do.

Edit: As Cary pointed out, if the data contains an inverted range, the results get a little funky. Here's an alternate parsing scheme which addresses that issue:

ranges = [list1, list2].flatten.map(&:values).map do |v| 
Range.new *v.map {|d| DateTime.parse d}.sort.take(2)
end


Related Topics



Leave a reply



Submit