Logo

Detecting a Trial Period Date Range with Ruby

Many of the web application that you may work on will have some sort of trial period. A user signs up today, and in 14 days they either have to choose an account type or have their account deactivated.

Calculating the ending date seems simple. Take today's date, add 14 days to it, and you end up with the ending date. But of course, nothing is ever as simple as it seems.

What if we are near the end of the month?

What if our month is part of a leap year?

And finally, keeping a list of months with how may days are in each month is a pain.

Lucky for us Ruby takes care of all of this logic for us, so with a little digging through the Date class, we are able to build a system for detecting this range.

Using the Date Class

Before we dive into the Date class, we should go over a snag you might hit. Ruby's default Date class is very limited and you need to require date to get the full set of methods that we will need. I wrote about how this works if you are interested in reading up about it.

The Setup

Okay, to get going we are going to create a new class with a few methods in it to work with, and then run those through IRB.

In your editor of choice, create a date_range.rb file with the following contents.

    class DateRange
      require 'date'
      attr_reader :starting_date

      def initialize
        @starting_date = Date.today
      end
    end

In this class we are requiring date so we can have the full list of methods from both the Date class and the DateTime class.

We are creating an initialize method so we can create a new instance and immediately have a reference to today's date with Date.today.

And finally we are calling attr_reader on starting_date so we can have our getter method. We don't need to set the starting_date outside of the initializer, so using attr_accessible is unnecessary.

Now with a shell of a class, we can fire up IRB, load our new class, and see how it works so far.

In your terminal, navigate to where you created the date_range.rb file and enter the following commands.

    irb
    load 'date_range.rb'

You will now able to use the class you created. Type this into your IRB session.

    d = DateRange.new

You should see something like this.

     => #<DateRange:0x007fde515dd680 @starting_date=#<Date: 2014-07-16 ((2456855j,0s,0n),+0s,2299161j)>>

This signifies that you have created an instance of the DateRange class and that the starting_date has been set for you.

Because we used attr_reader we can get all the date info we need from starting_date. Like the day of the month, for example.

    d.starting_date.day

Should return

     => 16

I'm writing this on July 16th

Ordinal Dates

But to really get a starting point, we need to know we are based on the year. This is where ordinal comes in.

Taken right from the Ruby docs

The ordinal date is a particular day of a calendar year identified by its ordinal number within the year.

So, an ordinal date is basically where at within the year we are. We can get the ordinal day of our DateRange instance with yday.

    d.starting_date.yday
    => 197

We have the 197th day of the year.

Let's create an ordinal method to return the day within the year. And let's create it in a way that allows us to pass any date in and get an integer returned. We will need this for the ending date later.

    def ordinal(date)
      date.yday
    end

With this data, we can simply add 14 to it and have the ordinal day when the trial should expire. Save the starting date in some sort of persistent storage.

Presenting the Data in a Readable Format

If we have saved the data in reference to the day of the year for a starting point, we can grab that and our trial (14 days for this instance) and have our range. Starting on the 197th day of the year and ending on the 211th day of the year.

It would be nice to show the user how many days they have left in their trial, so they can gauge how much time they have left to signup before the trial expires.

Add this method to your class.

    def display_message(start_point, end_point)
      if end_point >= start_point
        return "Your trial has expired"
      else
        return "You have #{end_point - start_point} days left in your trial"
      end
    end

This method takes two numbers and returns a message based on the difference in the numbers. If the ending_point is equal to or greater than the starting_point, we show the user the trial has expired. Otherwise, we show them how many days are left.

Assuming the ending date and starting date are stored in variables, we could use this

    starting_point = d.ordinal(d.starting_date)
    ending_point = (d.ordinal(d.starting_date) + 14)

Assuming today is the 200th day of the year, we could call d.display_message(starting_point, ending_point) and get back You have 11 days left in your trial.

But wait, there's more

I have showed you how to set up a simple date range in Ruby using Ruby's built in tools and an ordinal date. Now you will be able to display how much time is left in a trial. But there is obviously more. Like a trial that happens to span from one year to the next.

Hopefully you have learned enough to jump in and build something to suit your needs. If you have any questions or issues, feel free to email me scott@10pixels.net.

If you enjoyed this and want to receive tutorials and articles about software development in your inbox, I would like to send you an occasional email. I just need to know who to send it to.