Skip to content

Instantly share code, notes, and snippets.

@mattrayner
Created April 8, 2015 09:05
Show Gist options
  • Save mattrayner/82c858b6c4a5bfd30dfe to your computer and use it in GitHub Desktop.
Save mattrayner/82c858b6c4a5bfd30dfe to your computer and use it in GitHub Desktop.
Date Range Formatter Test
# Format a single date instance with an optional time
#
# @author Matt Rayner
# @since 1.0.0
#
# @attr [Date] Date The date that we are formatting
# @attr [String] Time An optional time value for this date
class DateFormatter
attr_reader :date, :time
# Create a new DateFormatter with an optional time
#
# @author Matt Rayner
#
# @attr [Date] Date The date to be formatter
# @attr [String] Time The optional time string (optional || can be nil)
def initialize date, time = nil
@date = date
@time = time
end
# Output a string representation of our date
#
# @author Matt Rayner
#
# @attr [Symbol] Format How are we formatting this date? Options are: :full, :time, :day, :no_year
# @return [String] The formatted date string
def to_s format = :full
# Create a placeholder for our output string
output_string = ''
# How are we formatting this date?
case format
when :full # 1st November 2008 at 10:00
# Format the date object into full string
output_string += format_date_with(:day, :month, :year, :time)
when :time # 10:00
# Format the date so that only the time appears (unless nil)
output_string += format_date_with(:time)
when :day # 1st
# Format the date so only the day appears
output_string += format_date_with(:day)
when :no_year # 1st November
# Format the date object into a no-year string
output_string += format_date_with(:day, :month)
end
# Return our formatted string
output_string
end
private
# Format the Date object using a number of object passed by the user
#
# @author Matt Rayner
#
# Accepted Arguments:
# :day # 1st
# :month # November
# :year # 2008
# :time # 10:00
#
# @param [Array<Symbol>] Args An array of the format sections you want to have included
# @return [String] Formatted Date string
def format_date_with(*args)
# Which pieces are going to make up this date string?
format_array = []
# Add the day, month and year if it is in our array of arguments
format_array << "#{date.day.ordinalize}" if args.include? :day
format_array << '%B' if args.include? :month
format_array << '%Y' if args.include? :year
# If we have a time value set, and the user has requested time, attempt to add time formatting
if time && args.include?(:time)
# Add an 'at' in front the time, IF there is at least one other element in the format_array
format_array << 'at' unless format_array.empty?
# Add the time
format_array << time
end
date.strftime(format_array.join(' '))
end
end
# Given a start date, end date, and optional start / end times, produce a nicely formatted, abbreviated representation
# of the time span that the dates represent.
#
# @author Nature Publishing Group & Matt Rayner
# @since 0.0.0
#
# @attr [Date] Start_on The date we are starting on
# @attr [Date] End_on The date we are ending on
# @attr [String] Starting_time The time we are starting (optional || can be nil)
# @attr [String] Ending_time The time we are ending (optional || can be nil)
class DateRangeFormatter
attr_reader :start_on, :end_on, :starting_time, :ending_time
# Create a new DateRangeFormatter with optional times
#
# @author Nature Publishing Group & Matt Rayner
#
# @attr [String] Start_on A string representation of the starting date
# @attr [String] End_on A string representation of the ending date
# @attr [String] Starting_time A string representation of the starting time
# @attr [String] Ending_time A string representation of the ending time
def initialize start_on, end_on, starting_time = nil, ending_time = nil
@start_on = Date.parse(start_on)
@end_on = Date.parse(end_on)
@starting_time, @ending_time = starting_time, ending_time
end
# Output a string representation of our date range which changes depending on the start and end dates and times
#
# @author Nature Publishing Group & Matt Rayner
#
# @return [String] Formatted date
def to_s
# Create a date formatter for our starting date and end date
starting_date_formatter = DateFormatter.new start_on, starting_time
end_date_formatter = DateFormatter.new end_on, ending_time
# Get the formatting we should use (as a Hash)
range_format = get_range_format
# Add our starting date (based on tests this will always be present)
output_string = starting_date_formatter.to_s range_format[:start_date_format]
# Add our end date (unless we have a nil format)
output_string += " #{range_format[:date_seperator]} #{end_date_formatter.to_s range_format[:end_date_format]}" unless range_format[:end_date_format].nil?
# Return out formatted range string
output_string
end
private
# Given the two dates, decide which formatting the range should have.
#
# @author Matt Rayner
#
# @return [Hash] Formats to use within our range
# { start_date_format: :full,
# end_date_format: :full, # (can be nil if no end date should be shown)
# date_seperator: '-' }
def get_range_format
# What format should the start and end dates have? (default to :full)
start_format = :full
end_format = :full
# What should the seperator be between the date string? (defaults to '-')
# i.e. '1st November 2008 - 3rd November 2008' or '1st November 2008 at 10:00 to 11:00'
seperator = '-'
# Are the main dates the same?
if @start_on == @end_on
# Default to no end date
end_format = nil
# If there is an end time, then show that and change the seperator to 'to'
unless ending_time.nil?
end_format = :time
seperator = 'to'
end
elsif is_same_month && is_same_year && times_are_not_set # Are the months and years the same, and no times set?
# Only show the start day
# e.g. '1st - 3rd November 2008'
start_format = :day
elsif is_same_year && times_are_not_set # Are the years the same, and no times set?
# Only show the start day and month
# e.g. '1st November - 7th December 2008'
start_format = :no_year
end
# Generate and return our hash
return { start_date_format: start_format, end_date_format: end_format, date_seperator: seperator }
end
# A small helper function returning a boolean if time values have been set
#
# @author Matt Rayner
#
# @return [Boolean] Is there a time set?
def times_are_not_set
starting_time.nil? && ending_time.nil?
end
# A small helper function returning a boolean if the start and end dates are within the same month
#
# @author Matt Rayner
#
# @return [Boolean] Are the months for both the start and end dates the same
def is_same_month
start_on.month == end_on.month
end
# A small helper function returning a boolean if the start and end dates are within the same year
#
# @author Matt Rayner
#
# @return [Boolean] Are the years for both the start and end dates the same?
def is_same_year
start_on.year == end_on.year
end
end
class Fixnum
def ordinalize
if (11..13).include?(self.abs % 100)
"#{self}th"
else
case self.to_i.abs % 10
when 1; "#{self}st"
when 2; "#{self}nd"
when 3; "#{self}rd"
else "#{self}th"
end
end
end
end
require 'spec_helper'
describe DateRangeFormatter do
it "formats date range for the same day" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-11-1"
formatter.to_s.should == "1st November 2009"
end
it "formats date range for the same day with starting time" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-11-1", "10:00"
formatter.to_s.should == "1st November 2009 at 10:00"
end
it "formats date range for the same day with starting and ending times" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-11-1", "10:00", "11:00"
formatter.to_s.should == "1st November 2009 at 10:00 to 11:00"
end
it "formats date range for the same month" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-11-3"
formatter.to_s.should == "1st - 3rd November 2009"
end
it "formats date range for the same month with starting time" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-11-3", "10:00"
formatter.to_s.should == "1st November 2009 at 10:00 - 3rd November 2009"
end
it "formats date range for the same month with starting and ending times" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-11-3", "10:00", "11:00"
formatter.to_s.should == "1st November 2009 at 10:00 - 3rd November 2009 at 11:00"
end
it "formats date range for the same year" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-12-1"
formatter.to_s.should == "1st November - 1st December 2009"
end
it "formats date range for the same year with starting time" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-12-1", "10:00"
formatter.to_s.should == "1st November 2009 at 10:00 - 1st December 2009"
end
it "formats date range for the same year with starting and ending times" do
formatter = DateRangeFormatter.new "2009-11-1", "2009-12-1", "10:00", "11:00"
formatter.to_s.should == "1st November 2009 at 10:00 - 1st December 2009 at 11:00"
end
it "formats date range for different year" do
formatter = DateRangeFormatter.new "2009-11-1", "2010-12-1"
formatter.to_s.should == "1st November 2009 - 1st December 2010"
end
it "formats date range for different year with starting time" do
formatter = DateRangeFormatter.new "2009-11-1", "2010-12-1", "10:00"
formatter.to_s.should == "1st November 2009 at 10:00 - 1st December 2010"
end
it "should give long date range for different year with starting and ending times" do
formatter = DateRangeFormatter.new "2009-11-1", "2010-12-1", "10:00", "11:00"
formatter.to_s.should == "1st November 2009 at 10:00 - 1st December 2010 at 11:00"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment