Created
April 8, 2015 09:05
-
-
Save mattrayner/82c858b6c4a5bfd30dfe to your computer and use it in GitHub Desktop.
Date Range Formatter Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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