Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby DateTime: Get next 5:15pm (or similar)

So, given a DateTime object, and a fixed time, I want to get the next occurrence of the fixed time after the given DateTime object.

  • For example, given the date of 14th March, 2016, 4:00pm, and the time of 5:15pm, I want to return 14th March, 2016 5:15pm.

  • However, given the date of 14th March, 2016, 6:00pm, and the time of 5:15pm, I want to return 15th March, 2016, 5:15pm, since that's the next occurrence.

So far, I've written this code:

# Given fixed_time and date_time

new_time = date_time
if fixed_time.utc.strftime("%H%M%S%N") >= date_time.utc.strftime("%H%M%S%N")
  new_time = DateTime.new(
    date_time.year,
    date_time.month,
    date_time.day,
    fixed_time.hour,
    fixed_time.min,
    fixed_time.sec
  )
else
  next_day = date_time.beginning_of_day + 1.day
  new_time = DateTime.new(
    next_day.year,
    next_day.month,
    next_day.day,
    fixed_time.hour,
    fixed_time.min,
    fixed_time.sec
  )
end

# Return new_time

It works, but is there a better way?

like image 993
shashwat Avatar asked May 14 '16 17:05

shashwat


2 Answers

I would construct the new date time just once and add 1 day if needed:

# Given fixed_time and date_time
new_date_time = DateTime.new(
  date_time.year,
  date_time.month,
  date_time.day,
  fixed_time.hour,
  fixed_time.min,
  fixed_time.sec
)

# add 1 day if new date prior to the given date
new_date_time += 1.day if new_date_time < date_time
like image 164
Matouš Borák Avatar answered Sep 21 '22 07:09

Matouš Borák


Here's a little stab at refactoring it to remove some of the redundancy:

# Given fixed_time and date_time

base_date = date_time.to_date
if fixed_time.to_time.utc.strftime("%T%N") <= date_time.to_time.utc.strftime("%T%N")
  base_date = base_date.next_day
end

new_time = DateTime.new(
  base_date.year,
  base_date.month,
  base_date.day,
  fixed_time.hour,
  fixed_time.min,
  fixed_time.sec
)

# Return new_time

The biggest changes are that the base_date is determined before the new_time is created, so that it can be used there.

I also used the next_day method on DateTime to get the next day, and used the "%T" format specifier as a shortcut for "%H:%M:%S"

Here's a little test program that to show that it works:

require "date"

def next_schedule(fixed_time, date_time)
  # Given fixed_time and date_time

  base_date = date_time.to_date
  if fixed_time.to_time.utc.strftime("%T%N") <= date_time.to_time.utc.strftime("%T%N")
    base_date = base_date.next_day
  end

  new_time = DateTime.new(
    base_date.year,
    base_date.month,
    base_date.day,
    fixed_time.hour,
    fixed_time.min,
    fixed_time.sec
  )

  # Return new_time
end

StartTime = DateTime.strptime("2016-02-14 17:15:00", "%F %T")
Dates = [
  "2016-03-14 16:00:00",
  "2016-03-14 18:00:00"
]

Dates.each do |current_date|
  scheduled = next_schedule(StartTime, DateTime.strptime(current_date, "%F %T"))
  puts "Scheduled: #{scheduled.strftime('%F %T')}"
end

The output of this is:

Scheduled: 2016-03-14 17:15:00
Scheduled: 2016-03-15 17:15:00

It's using the test cases described in the question, and it gets the expected answers.

like image 42
Michael Gaskill Avatar answered Sep 23 '22 07:09

Michael Gaskill