Bruno Quaresma
Bruno Quaresma

Reputation: 10697

Rails - Query time shifts

currently I'm developing a system that manages work shifts.

The system contains an endpoint that returns the current shift based on server time. I created a query that works fine for regular shifts like 08:00 AM to 04:00 PM and so on but the challenge is when there is a dawn shift that starts at 10:00 PM until 06:00 AM from the other day.

My model contains the following fields:

Ps.: I'm using Postgres as the database.

My code:

class Shift < ApplicationRecord
  def self.current
    current_time = Time.now()
    current_time = current_time.change(year: 2000, month: 1, day: 1)

    @current ||= Shift
                  .where('start_time <= ?', current_time)
                  .where('end_time >= ?', current_time)
                  .first()
  end
end

Ps.: I guess, since I'm using the Time type in the database, I have to normalize the Time.now to use the 2000 - 01 - 01 date.

So, is there an easy/best way to do that?

Thanks for the help!

Upvotes: 2

Views: 358

Answers (2)

Fig
Fig

Reputation: 1

You might be interested in the tod gem that provides a TimeOfDay class and a Shift class that takes two TimeOfDay objects to represent the start and end of the shift. It handles dawn shifts as expected.

An implementation might look like this:

require 'tod'

class Shift < ApplicationRecord
  def self.current
    find(&:current?)
  end

  def current?
    schedule.include?(Tod::TimeOfDay(Time.now))
  end

  def schedule
    Tod::Shift.new(Tod::TimeOfDay(start_time), Tod::TimeOfDay(end_time))
  end
end

Upvotes: 0

Paige Ruten
Paige Ruten

Reputation: 176665

Interesting problem! So there's two cases: (1) a normal shift (where start_time <= end_time), and (2) a shift that overlaps midnight (where start_time > end_time).

You are already handling the first case by checking if the current time is between the start time and the end time.

I believe the second case can be handled by checking if the current time is either between the start time and midnight, or between midnight and the end time. Which translates to start_time <= ? OR end_time >= ?.

I haven't used Rails in a while, but I think you could do something like this:

@current ||= Shift
  .where('start_time <= end_time')
  .where('start_time <= ?', current_time)
  .where('end_time >= ?', current_time)
  .or(Shift
    .where('start_time > end_time')
    .or(Shift
      .where('start_time <= ?', current_time)
      .where('end_time >= ?', current_time)))
  .first()

If you go with this, consider splitting the two cases into separate scopes, so you can write something more readable like this in this method:

@current ||= current_normal_shifts.or(current_dawn_shifts).first

Upvotes: 1

Related Questions