The Ruby Programming Language is one of my favorit programming languages. Which for some reason suprise many people, so I will cover it in another post. Anyway, back on topic, and I have been using it for quite a while for personal projects. In particular for building APIs based on Sinatra and this blog is made using Jekyll.

When I make Sinatra based APIs I almost always have a database backend, most commonly I use PostgresSQL because it is epic. However, I always strive to make my code portable and whilst the pg is amazing, it does lock me to Postgres if I start making weird queries. So I often stick to using an Object-relational mapping (ORM) framework and more often than not I find that I use activerecord. The reason I use activerecord is due to its simplicity and available guides. Now you might think “wait, isn’t activerecord Rails only?”. No, whilst it is true that it is propperly most use with Ruby on Rails, you can use it as a standalone gem. Actually, the link to activerecord above is a link to the gem itself on rubygems.org. You still have support for migrations… if you write them by hand and you get all the nice features of activerecord. One of which is interactions with time.

So let us play a little bit around with this. Let us assume we have a user model, that has username, email, password, when it was created, and updated. Then we can write the migration something like this:

class User < ActiveRecord::Migration[7.0]

  def change
    create_table :users do |t|
      t.string :username, index: { unique: true }
      t.string :email, index: { unique: true }
      t.string :password, null: false
      t.timestamps
    end
  end
end

t.timestamps gives us the columns created_at and updated_at. It is important to note here that activerecord assumes the date type for these are Ruby’s Time. In the database from what I know with Postgres the date type used is timestamp without time zone.

Now let us play at little bit with activerecord modules before we move on. When you install the activerecord gem, you also get a module active_support and it is filled with a lot of other submodules that are awesome. One of these is active_support/core_ext/numeric/time, which gives us some nice extensions for how to interact with Time. In vanilla Ruby if you want to get the time 24 hours ago you would do something like Time.now - (24 * 60 * 60). For the basically subtraction the amount of seconds in 24 hours from the current time. However, with active_support you can simply write 24.hours.ago. You can even play with this in a tiny ruby program if you want to without a database. The program below is quite simple and you can see the nice output below it.

require 'active_support'
require 'active_support/core_ext/numeric/time'

puts 24.days.ago
puts 24.hours.ago
puts 24.minutes.ago
puts 24.seconds.ago

puts Time.now + 24.seconds
puts Time.now + 24.minutes
puts Time.now + 24.days
2026-02-18 20:08:48 +0100
2026-03-13 20:08:48 +0100
2026-03-14 19:44:48 +0100
2026-03-14 20:08:24 +0100
2026-03-14 20:09:12 +0100
2026-03-14 20:32:48 +0100
2026-04-07 20:08:48 +0200

Now this is nifty, as we do not how to do “ugly” maths, like Time.now - (24 * 60 * 60) or Time.now + (24 * 60 * 60). We can write it much more readable. Only limitation I have found is that it does not have the option to work in years, yes I need that from time to time…. So what can we use this for? Well let us say we want the users created in the last 24 hours. we can express this as User.where('created_at > ?', 24.hours.ago). But what if we want to have the users created between 24 and 22 hours ago? Well activerecord actually supports Ruby’s Range class. Thus, we can simply express our query as User.where(created_by: 24.hours.ago..22.hours.ago). We can even make exclusive upper bound check by adding a . so it becomes User.where(created_by: 24.hours.ago...22.hours.ago).

This is quite nifty and powerful once you get use to it and I highly recommend that you do! Firstly, because it makes you code easier to write. Secondly, it makes it easier to read, always a very strong positive. Thirdly, you do not have to make weird constants for like TWENTYFOUR_HOURS_IN_SECONDS = 24 * 60 * 60, which makes you code more easy to maintain in the long run. Finally, I have found that I make much less computation errors when I express my time based queries in this manner. Which, well… just makes me more productive which is also a huge bonus.

Finally, some may wonder “WAIT DO YOU NOT MAINTAIN QueryC++??? why do you need activerecord???”. Well, first of yes through Obsidian Wolf Labs I am still the main maintainer and currently only developer of QueryC++. But at Obsidian Wolf Labs, we do not only use C++, we also use Ruby, Swift, and Python. Also, I do not exclusively code in C++, for spare time projects I mainly use Swift and Ruby, with some C++ and C. Therefore, it is very nice for me to consistently improve my tool box.

./Lars