2010-08-27 13:56:58 -04:00
require 'date'
2008-02-19 23:23:18 -05:00
require 'action_view/helpers/tag_helper'
2011-02-12 23:10:13 -05:00
require 'active_support/core_ext/date/conversions'
2010-03-05 16:40:41 -05:00
require 'active_support/core_ext/hash/slice'
2010-08-27 13:56:58 -04:00
require 'active_support/core_ext/object/with_options'
2004-11-23 20:04:44 -05:00
module ActionView
module Helpers
2010-06-16 14:17:49 -04:00
# = Action View Date Helpers
#
2011-07-19 14:04:24 -04:00
# The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
2011-07-17 02:29:51 -04:00
# elements. All of the select-type methods share a number of common options that are as follows:
2004-11-23 20:04:44 -05:00
#
2008-07-21 13:57:15 -04:00
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
2011-05-10 19:19:47 -04:00
# would give birthday[month] instead of date[month] if passed to the <tt>select_month</tt> method.
2011-12-26 07:56:47 -05:00
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
2008-07-21 13:57:15 -04:00
# * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
2011-05-10 19:19:47 -04:00
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
# of "date[month]".
2004-11-23 20:04:44 -05:00
module DateHelper
2011-07-17 02:27:26 -04:00
# Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
2011-05-10 19:19:47 -04:00
# Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs.
2008-03-15 16:00:41 -04:00
# Distances are reported based on the following table:
2005-06-17 09:42:23 -04:00
#
2008-03-15 16:00:41 -04:00
# 0 <-> 29 secs # => less than a minute
# 30 secs <-> 1 min, 29 secs # => 1 minute
# 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
# 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
2011-04-12 20:51:25 -04:00
# 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
# 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
2011-04-12 21:01:24 -04:00
# 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
2008-03-15 16:00:41 -04:00
# 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
2009-09-26 15:42:18 -04:00
# 1 yr <-> 1 yr, 3 months # => about 1 year
# 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
# 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
# 2 yrs <-> max time or date # => (same rules as 1 yr)
2005-06-17 09:42:23 -04:00
#
2008-03-15 16:00:41 -04:00
# With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
# 0-4 secs # => less than 5 seconds
# 5-9 secs # => less than 10 seconds
# 10-19 secs # => less than 20 seconds
# 20-39 secs # => half a minute
# 40-59 secs # => less than a minute
# 60-89 secs # => 1 minute
2006-09-04 13:00:37 -04:00
#
2007-06-23 13:49:18 -04:00
# ==== Examples
2006-09-04 13:00:37 -04:00
# from_time = Time.now
2007-06-23 13:49:18 -04:00
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
# distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
# distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds
2009-09-04 12:43:17 -04:00
# distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
2011-04-15 20:08:11 -04:00
# distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
2007-06-23 13:49:18 -04:00
# distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute
# distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
2007-10-07 23:46:23 -04:00
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
2009-09-04 12:43:17 -04:00
# distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
2007-06-23 13:49:18 -04:00
#
# to_time = Time.now + 6.years + 19.days
2009-09-26 15:42:18 -04:00
# distance_of_time_in_words(from_time, to_time, true) # => about 6 years
# distance_of_time_in_words(to_time, from_time, true) # => about 6 years
2007-06-23 13:49:18 -04:00
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
2006-09-04 13:00:37 -04:00
#
2008-06-19 10:25:27 -04:00
def distance_of_time_in_words ( from_time , to_time = 0 , include_seconds = false , options = { } )
2005-06-17 09:42:23 -04:00
from_time = from_time . to_time if from_time . respond_to? ( :to_time )
to_time = to_time . to_time if to_time . respond_to? ( :to_time )
distance_in_minutes = ( ( ( to_time - from_time ) . abs ) / 60 ) . round
distance_in_seconds = ( ( to_time - from_time ) . abs ) . round
2005-03-06 06:50:41 -05:00
2008-06-23 08:37:50 -04:00
I18n . with_options :locale = > options [ :locale ] , :scope = > :'datetime.distance_in_words' do | locale |
2008-06-19 10:25:27 -04:00
case distance_in_minutes
when 0 .. 1
2008-07-21 13:57:15 -04:00
return distance_in_minutes == 0 ?
2008-06-19 10:25:27 -04:00
locale . t ( :less_than_x_minutes , :count = > 1 ) :
locale . t ( :x_minutes , :count = > distance_in_minutes ) unless include_seconds
case distance_in_seconds
when 0 .. 4 then locale . t :less_than_x_seconds , :count = > 5
when 5 .. 9 then locale . t :less_than_x_seconds , :count = > 10
when 10 .. 19 then locale . t :less_than_x_seconds , :count = > 20
when 20 .. 39 then locale . t :half_a_minute
when 40 .. 59 then locale . t :less_than_x_minutes , :count = > 1
else locale . t :x_minutes , :count = > 1
end
when 2 .. 44 then locale . t :x_minutes , :count = > distance_in_minutes
when 45 .. 89 then locale . t :about_x_hours , :count = > 1
when 90 .. 1439 then locale . t :about_x_hours , :count = > ( distance_in_minutes . to_f / 60 . 0 ) . round
2011-04-12 20:51:25 -04:00
when 1440 .. 2519 then locale . t :x_days , :count = > 1
when 2520 .. 43199 then locale . t :x_days , :count = > ( distance_in_minutes . to_f / 1440 . 0 ) . round
2008-06-19 10:25:27 -04:00
when 43200 .. 86399 then locale . t :about_x_months , :count = > 1
2009-09-26 15:42:18 -04:00
when 86400 .. 525599 then locale . t :x_months , :count = > ( distance_in_minutes . to_f / 43200 . 0 ) . round
2009-09-04 12:43:17 -04:00
else
2010-11-29 10:15:43 -05:00
fyear = from_time . year
fyear += 1 if from_time . month > = 3
tyear = to_time . year
tyear -= 1 if to_time . month < 3
leap_years = ( fyear > tyear ) ? 0 : ( fyear .. tyear ) . count { | x | Date . leap? ( x ) }
minute_offset_for_leap_year = leap_years * 1440
# Discount the leap year days when calculating year distance.
2011-05-06 06:21:09 -04:00
# e.g. if there are 20 leap year days between 2 dates having the same day
2010-11-29 10:15:43 -05:00
# and month then the based on 365 days calculation
2011-05-06 06:21:09 -04:00
# the distance in years will come out to over 80 years when in written
2010-11-29 10:15:43 -05:00
# english it would read better as about 80 years.
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
remainder = ( minutes_with_offset % 525600 )
distance_in_years = ( minutes_with_offset / 525600 )
2009-09-26 15:42:18 -04:00
if remainder < 131400
locale . t ( :about_x_years , :count = > distance_in_years )
elsif remainder < 394200
locale . t ( :over_x_years , :count = > distance_in_years )
else
locale . t ( :almost_x_years , :count = > distance_in_years + 1 )
end
2008-06-19 10:25:27 -04:00
end
2004-11-23 20:04:44 -05:00
end
2008-07-21 13:57:15 -04:00
end
2006-12-06 14:15:24 -05:00
2011-05-10 19:19:47 -04:00
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
2007-06-23 13:49:18 -04:00
#
# ==== Examples
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
2011-04-15 20:03:52 -04:00
# time_ago_in_words(Time.now - 15.hours) # => about 15 hours
2007-06-23 13:49:18 -04:00
# time_ago_in_words(Time.now) # => less than a minute
#
2011-04-15 19:56:48 -04:00
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
# time_ago_in_words(from_time) # => 3 days
#
2005-03-30 08:06:19 -05:00
def time_ago_in_words ( from_time , include_seconds = false )
2005-03-26 17:00:23 -05:00
distance_of_time_in_words ( from_time , Time . now , include_seconds )
2004-11-23 20:04:44 -05:00
end
2006-12-06 14:15:24 -05:00
2005-03-30 08:06:19 -05:00
alias_method :distance_of_time_in_words_to_now , :time_ago_in_words
2004-11-23 20:04:44 -05:00
2008-07-21 13:57:15 -04:00
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
2009-04-17 09:28:46 -04:00
# attribute (identified by +method+) on an object assigned to the template (identified by +object+).
#
2008-08-07 14:13:47 -04:00
#
# ==== Options
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
2008-12-19 09:27:43 -05:00
# "2" instead of "February").
2009-07-25 11:03:58 -04:00
# * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
# month names (e.g. "Feb" instead of "February").
# * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
2008-08-07 14:13:47 -04:00
# "2 - February" instead of "February").
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
2009-07-25 11:03:58 -04:00
# Note: You can also use Rails' i18n functionality for this.
2008-08-07 14:13:47 -04:00
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
# first of the given month in order to not create invalid dates like 31 February.
# * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
# as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
# * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
# as a hidden field instead of showing a select field.
2009-07-25 11:03:58 -04:00
# * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
2008-08-07 14:13:47 -04:00
# customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
# select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
2008-11-18 03:59:46 -05:00
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
2008-08-07 14:13:47 -04:00
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
# dates.
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
2008-12-21 14:46:33 -05:00
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
# Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
# or the given prompt string.
2005-09-11 01:58:00 -04:00
#
2008-05-02 09:45:23 -04:00
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
2008-03-01 23:40:54 -05:00
#
2005-02-19 13:26:37 -05:00
# NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
2004-11-23 20:04:44 -05:00
#
2007-06-23 13:49:18 -04:00
# ==== Examples
2011-07-17 02:42:35 -04:00
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
# date_select("article", "written_on")
2007-06-23 13:49:18 -04:00
#
2011-07-17 02:42:35 -04:00
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
2007-06-23 13:49:18 -04:00
# # with the year in the year drop down box starting at 1995.
2011-07-17 02:42:35 -04:00
# date_select("article", "written_on", :start_year => 1995)
2007-06-23 13:49:18 -04:00
#
2011-07-17 02:42:35 -04:00
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
2007-06-23 13:49:18 -04:00
# # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
2008-07-21 13:57:15 -04:00
# # and without a day select box.
2011-07-17 02:42:35 -04:00
# date_select("article", "written_on", :start_year => 1995, :use_month_numbers => true,
2004-11-23 20:04:44 -05:00
# :discard_day => true, :include_blank => true)
2007-06-23 13:49:18 -04:00
#
2011-07-17 02:42:35 -04:00
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
2007-06-23 13:49:18 -04:00
# # with the fields ordered as day, month, year rather than month, day, year.
2011-07-17 02:42:35 -04:00
# date_select("article", "written_on", :order => [:day, :month, :year])
2004-11-23 20:04:44 -05:00
#
2007-06-23 13:49:18 -04:00
# # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
# # lacking a year field.
# date_select("user", "birthday", :order => [:month, :day])
#
2011-07-17 02:42:35 -04:00
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
2007-06-23 13:49:18 -04:00
# # which is initially set to the date 3 days from the current date
2011-07-17 02:42:35 -04:00
# date_select("article", "written_on", :default => 3.days.from_now)
2007-06-23 13:49:18 -04:00
#
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
# # that will have a default day of 20.
2007-01-28 11:44:44 -05:00
# date_select("credit_card", "bill_due", :default => { :day => 20 })
#
2011-05-10 19:19:47 -04:00
# # Generates a date select with custom prompts.
2011-07-17 02:42:35 -04:00
# date_select("article", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' })
2008-12-21 14:46:33 -05:00
#
2004-11-23 20:04:44 -05:00
# The selects are prepared for multi-parameter assignment to an Active Record object.
2007-05-31 12:38:36 -04:00
#
2008-07-21 13:57:15 -04:00
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
# all month choices are valid.
2008-03-01 23:40:54 -05:00
def date_select ( object_name , method , options = { } , html_options = { } )
2012-01-17 13:07:05 -05:00
Tags :: DateSelect . new ( object_name , method , self , options , html_options ) . render
2004-11-23 20:04:44 -05:00
end
2008-08-07 14:13:47 -04:00
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
# specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
2011-04-28 16:33:56 -04:00
# +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
# with <tt>:ampm</tt> option.
2008-07-01 10:08:25 -04:00
#
# This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
2011-05-10 10:37:27 -04:00
# <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
# +date_select+ on the same method within the form otherwise an exception will be raised.
2008-07-01 10:08:25 -04:00
#
2008-03-01 23:40:54 -05:00
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
2007-06-23 13:49:18 -04:00
# ==== Examples
2011-07-17 02:42:35 -04:00
# # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
# time_select("article", "sunrise")
2007-06-23 13:49:18 -04:00
#
2011-07-17 02:42:35 -04:00
# # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
2008-07-21 13:57:15 -04:00
# # the sunrise attribute.
2011-07-17 02:42:35 -04:00
# time_select("article", "start_time", :include_seconds => true)
2006-12-06 14:15:24 -05:00
#
2011-05-10 19:19:47 -04:00
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
2007-11-06 18:00:15 -05:00
# time_select 'game', 'game_time', {:minute_step => 15}
#
2011-05-10 19:19:47 -04:00
# # Creates a time select tag with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
2011-07-17 02:42:35 -04:00
# time_select("article", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
# time_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
# time_select("article", "written_on", :prompt => true) # generic prompts for all
2008-12-21 14:46:33 -05:00
#
2011-04-28 16:33:56 -04:00
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
# time_select 'game', 'game_time', {:ampm => true}
#
2006-12-06 14:15:24 -05:00
# The selects are prepared for multi-parameter assignment to an Active Record object.
2007-05-31 12:38:36 -04:00
#
2008-07-21 13:57:15 -04:00
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
# all month choices are valid.
2008-03-01 23:40:54 -05:00
def time_select ( object_name , method , options = { } , html_options = { } )
2012-01-17 13:07:05 -05:00
Tags :: TimeSelect . new ( object_name , method , self , options , html_options ) . render
2006-12-06 14:15:24 -05:00
end
2008-07-21 13:57:15 -04:00
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
# specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
2009-04-17 09:28:46 -04:00
# by +object+).
2004-11-23 20:04:44 -05:00
#
2008-03-01 23:40:54 -05:00
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
2007-06-23 13:49:18 -04:00
# ==== Examples
2011-07-17 02:42:35 -04:00
# # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
2011-05-10 19:19:47 -04:00
# # attribute.
2011-07-17 02:42:35 -04:00
# datetime_select("article", "written_on")
2007-06-23 13:49:18 -04:00
#
2008-07-21 13:57:15 -04:00
# # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
2011-07-17 02:42:35 -04:00
# # article variable in the written_on attribute.
# datetime_select("article", "written_on", :start_year => 1995)
2004-11-23 20:04:44 -05:00
#
2008-07-21 13:57:15 -04:00
# # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
# # be stored in the trip variable in the departing attribute.
2007-06-23 13:49:18 -04:00
# datetime_select("trip", "departing", :default => 3.days.from_now)
#
2011-04-28 16:33:56 -04:00
# # Generate a datetime select with hours in the AM/PM format
2011-07-17 02:42:35 -04:00
# datetime_select("article", "written_on", :ampm => true)
2011-04-28 16:33:56 -04:00
#
2011-07-17 02:42:35 -04:00
# # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
2008-07-21 13:57:15 -04:00
# # as the written_on attribute.
2011-07-17 02:42:35 -04:00
# datetime_select("article", "written_on", :discard_type => true)
2007-06-23 13:49:18 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
2011-07-17 02:42:35 -04:00
# datetime_select("article", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
# datetime_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
# datetime_select("article", "written_on", :prompt => true) # generic prompts for all
2008-12-21 14:46:33 -05:00
#
2004-11-23 20:04:44 -05:00
# The selects are prepared for multi-parameter assignment to an Active Record object.
2008-03-01 23:40:54 -05:00
def datetime_select ( object_name , method , options = { } , html_options = { } )
2012-01-17 13:07:05 -05:00
Tags :: DatetimeSelect . new ( object_name , method , self , options , html_options ) . render
2004-11-23 20:04:44 -05:00
end
2011-07-23 06:14:10 -04:00
# Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
2008-08-07 14:13:47 -04:00
# +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
# an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
# supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
# <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
# control visual display of the elements.
2007-06-23 13:49:18 -04:00
#
2008-03-01 23:40:54 -05:00
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
2007-06-23 13:49:18 -04:00
# ==== Examples
# my_date_time = Time.now + 4.days
#
2011-05-10 19:19:47 -04:00
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
2007-06-23 13:49:18 -04:00
# select_datetime(my_date_time)
#
# # Generates a datetime select that defaults to today (no specified datetime)
# select_datetime()
#
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
2007-12-05 13:54:41 -05:00
# # with the fields ordered year, month, day rather than month, day, year.
2007-06-23 13:49:18 -04:00
# select_datetime(my_date_time, :order => [:year, :month, :day])
#
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
# # with a '/' between each date field.
# select_datetime(my_date_time, :date_separator => '/')
#
2008-07-21 13:57:15 -04:00
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
# # with a date fields separated by '/', time fields separated by '' and the date and time fields
# # separated by a comma (',').
# select_datetime(my_date_time, :date_separator => '/', :time_separator => '', :datetime_separator => ',')
#
# # Generates a datetime select that discards the type of the field and defaults to the datetime in
2007-06-23 13:49:18 -04:00
# # my_date_time (four days after today)
# select_datetime(my_date_time, :discard_type => true)
#
2011-04-28 16:33:56 -04:00
# # Generate a datetime field with hours in the AM/PM format
# select_datetime(my_date_time, :ampm => true)
#
2007-06-23 13:49:18 -04:00
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
# # prefixed with 'payday' rather than 'date'
# select_datetime(my_date_time, :prefix => 'payday')
#
2011-05-10 19:19:47 -04:00
# # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
2008-12-21 14:46:33 -05:00
# select_datetime(my_date_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
# select_datetime(my_date_time, :prompt => {:hour => true}) # generic prompt for hours
# select_datetime(my_date_time, :prompt => true) # generic prompts for all
#
2008-04-21 01:40:04 -04:00
def select_datetime ( datetime = Time . current , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( datetime , options , html_options ) . select_datetime
2008-07-21 13:57:15 -04:00
end
2006-12-06 14:15:24 -05:00
2004-11-23 20:04:44 -05:00
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
2006-12-06 14:15:24 -05:00
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
2011-07-19 14:04:24 -04:00
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
# If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
2007-06-23 13:49:18 -04:00
#
2008-03-01 23:40:54 -05:00
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
2007-06-23 13:49:18 -04:00
# ==== Examples
2011-07-17 02:47:06 -04:00
# my_date = Time.now + 6.days
2007-06-23 13:49:18 -04:00
#
2011-07-17 02:44:01 -04:00
# # Generates a date select that defaults to the date in my_date (six days after today).
2007-06-23 13:49:18 -04:00
# select_date(my_date)
#
2011-05-10 19:19:47 -04:00
# # Generates a date select that defaults to today (no specified date).
2007-06-23 13:49:18 -04:00
# select_date()
#
# # Generates a date select that defaults to the date in my_date (six days after today)
2007-12-05 13:54:41 -05:00
# # with the fields ordered year, month, day rather than month, day, year.
2007-06-23 13:49:18 -04:00
# select_date(my_date, :order => [:year, :month, :day])
#
2008-07-21 13:57:15 -04:00
# # Generates a date select that discards the type of the field and defaults to the date in
2011-05-10 19:19:47 -04:00
# # my_date (six days after today).
2008-07-16 08:00:36 -04:00
# select_date(my_date, :discard_type => true)
2007-06-23 13:49:18 -04:00
#
2008-07-21 13:57:15 -04:00
# # Generates a date select that defaults to the date in my_date,
2011-05-10 19:19:47 -04:00
# # which has fields separated by '/'.
2008-07-21 13:57:15 -04:00
# select_date(my_date, :date_separator => '/')
#
2007-06-23 13:49:18 -04:00
# # Generates a date select that defaults to the datetime in my_date (six days after today)
2011-05-10 19:19:47 -04:00
# # prefixed with 'payday' rather than 'date'.
2008-07-16 08:00:36 -04:00
# select_date(my_date, :prefix => 'payday')
2007-06-23 13:49:18 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a date select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
2008-12-21 14:46:33 -05:00
# select_date(my_date, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
# select_date(my_date, :prompt => {:hour => true}) # generic prompt for hours
# select_date(my_date, :prompt => true) # generic prompts for all
#
2008-05-08 23:48:47 -04:00
def select_date ( date = Date . current , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( date , options , html_options ) . select_date
2004-11-23 20:04:44 -05:00
end
2011-05-10 19:19:47 -04:00
# Returns a set of html select-tags (one for hour and minute).
2008-07-21 13:57:15 -04:00
# You can set <tt>:time_separator</tt> key to format the output, and
2007-06-23 13:49:18 -04:00
# the <tt>:include_seconds</tt> option to include an input for seconds.
#
2008-03-01 23:40:54 -05:00
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
2007-06-23 13:49:18 -04:00
# ==== Examples
# my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
#
2011-05-10 19:19:47 -04:00
# # Generates a time select that defaults to the time in my_time.
2007-06-23 13:49:18 -04:00
# select_time(my_time)
#
2011-05-10 19:19:47 -04:00
# # Generates a time select that defaults to the current time (no specified time).
2007-06-23 13:49:18 -04:00
# select_time()
#
# # Generates a time select that defaults to the time in my_time,
2011-05-10 19:19:47 -04:00
# # which has fields separated by ':'.
2007-06-23 13:49:18 -04:00
# select_time(my_time, :time_separator => ':')
#
# # Generates a time select that defaults to the time in my_time,
2011-05-10 19:19:47 -04:00
# # that also includes an input for seconds.
2007-06-23 13:49:18 -04:00
# select_time(my_time, :include_seconds => true)
#
# # Generates a time select that defaults to the time in my_time, that has fields
2011-05-10 19:19:47 -04:00
# # separated by ':' and includes an input for seconds.
2007-06-23 13:49:18 -04:00
# select_time(my_time, :time_separator => ':', :include_seconds => true)
#
2011-04-28 16:33:56 -04:00
# # Generate a time select field with hours in the AM/PM format
# select_time(my_time, :ampm => true)
#
2011-05-14 05:21:27 -04:00
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
2008-12-21 14:46:33 -05:00
# select_time(my_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
# select_time(my_time, :prompt => {:hour => true}) # generic prompt for hours
# select_time(my_time, :prompt => true) # generic prompts for all
#
2008-04-21 01:40:04 -04:00
def select_time ( datetime = Time . current , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( datetime , options , html_options ) . select_time
2005-01-02 10:32:01 -05:00
end
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
2011-12-20 12:25:56 -05:00
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
2005-07-02 14:38:22 -04:00
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
2007-06-23 13:49:18 -04:00
#
# ==== Examples
# my_time = Time.now + 16.minutes
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for seconds that defaults to the seconds for the time in my_time.
2007-06-23 13:49:18 -04:00
# select_second(my_time)
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for seconds that defaults to the number given.
2007-06-23 13:49:18 -04:00
# select_second(33)
2008-07-21 13:57:15 -04:00
#
2007-06-23 13:49:18 -04:00
# # Generates a select field for seconds that defaults to the seconds for the time in my_time
2011-05-10 19:19:47 -04:00
# # that is named 'interval' rather than 'second'.
2007-06-23 13:49:18 -04:00
# select_second(my_time, :field_name => 'interval')
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for seconds with a custom prompt. Use <tt>:prompt => true</tt> for a
2008-12-21 14:46:33 -05:00
# # generic prompt.
2011-07-17 02:09:59 -04:00
# select_second(14, :prompt => 'Choose seconds')
2008-12-21 14:46:33 -05:00
#
2008-03-01 23:40:54 -05:00
def select_second ( datetime , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( datetime , options , html_options ) . select_second
2005-01-02 10:32:01 -05:00
end
2004-11-23 20:04:44 -05:00
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
2008-08-07 14:13:47 -04:00
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
2011-12-20 12:25:56 -05:00
# selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
2005-07-02 14:38:22 -04:00
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
2007-06-23 13:49:18 -04:00
#
# ==== Examples
# my_time = Time.now + 6.hours
#
2011-07-17 03:24:31 -04:00
# # Generates a select field for minutes that defaults to the minutes for the time in my_time.
2007-06-23 13:49:18 -04:00
# select_minute(my_time)
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for minutes that defaults to the number given.
2007-06-23 13:49:18 -04:00
# select_minute(14)
2008-07-21 13:57:15 -04:00
#
2007-06-23 13:49:18 -04:00
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
2011-07-17 02:11:48 -04:00
# # that is named 'moment' rather than 'minute'.
# select_minute(my_time, :field_name => 'moment')
2007-06-23 13:49:18 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for minutes with a custom prompt. Use <tt>:prompt => true</tt> for a
2008-12-21 14:46:33 -05:00
# # generic prompt.
# select_minute(14, :prompt => 'Choose minutes')
#
2008-03-01 23:40:54 -05:00
def select_minute ( datetime , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( datetime , options , html_options ) . select_minute
2004-11-23 20:04:44 -05:00
end
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
2011-12-20 12:25:56 -05:00
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
2005-07-02 14:38:22 -04:00
# Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
2007-06-23 13:49:18 -04:00
#
# ==== Examples
# my_time = Time.now + 6.hours
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for hours that defaults to the hour for the time in my_time.
2008-10-21 13:33:40 -04:00
# select_hour(my_time)
2007-06-23 13:49:18 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for hours that defaults to the number given.
2008-10-21 13:33:40 -04:00
# select_hour(13)
2008-07-21 13:57:15 -04:00
#
2011-07-17 02:08:30 -04:00
# # Generates a select field for hours that defaults to the hour for the time in my_time
# # that is named 'stride' rather than 'hour'.
2008-10-21 13:33:40 -04:00
# select_hour(my_time, :field_name => 'stride')
2007-06-23 13:49:18 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for hours with a custom prompt. Use <tt>:prompt => true</tt> for a
2008-12-21 14:46:33 -05:00
# # generic prompt.
2011-04-15 21:34:49 -04:00
# select_hour(13, :prompt => 'Choose hour')
2008-12-21 14:46:33 -05:00
#
2011-04-28 16:19:45 -04:00
# # Generate a select field for hours in the AM/PM format
# select_hour(my_time, :ampm => true)
#
2008-03-01 23:40:54 -05:00
def select_hour ( datetime , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( datetime , options , html_options ) . select_hour
2004-11-23 20:04:44 -05:00
end
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
2011-07-17 03:31:28 -04:00
# The <tt>date</tt> can also be substituted for a day number.
2005-07-02 14:38:22 -04:00
# Override the field name using the <tt>:field_name</tt> option, 'day' by default.
2007-06-23 13:49:18 -04:00
#
# ==== Examples
2011-07-17 02:47:06 -04:00
# my_date = Time.now + 2.days
2007-06-23 13:49:18 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for days that defaults to the day for the date in my_date.
2007-06-23 13:49:18 -04:00
# select_day(my_time)
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for days that defaults to the number given.
2007-06-23 13:49:18 -04:00
# select_day(5)
2008-07-21 13:57:15 -04:00
#
2007-06-23 13:49:18 -04:00
# # Generates a select field for days that defaults to the day for the date in my_date
2011-05-10 19:19:47 -04:00
# # that is named 'due' rather than 'day'.
2007-06-23 13:49:18 -04:00
# select_day(my_time, :field_name => 'due')
#
2011-05-23 19:22:33 -04:00
# # Generates a select field for days with a custom prompt. Use <tt>:prompt => true</tt> for a
2008-12-21 14:46:33 -05:00
# # generic prompt.
# select_day(5, :prompt => 'Choose day')
#
2008-03-01 23:40:54 -05:00
def select_day ( date , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( date , options , html_options ) . select_day
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2008-07-21 13:57:15 -04:00
# Returns a select tag with options for each of the months January through December with the current month
# selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
# used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
# instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
# want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
# to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
# to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
# Override the field name using the <tt>:field_name</tt> option, 'month' by default.
2006-12-06 14:15:24 -05:00
#
2007-06-23 13:49:18 -04:00
# ==== Examples
# # Generates a select field for months that defaults to the current month that
# # will use keys like "January", "March".
# select_month(Date.today)
2004-11-23 20:04:44 -05:00
#
2007-06-23 13:49:18 -04:00
# # Generates a select field for months that defaults to the current month that
2011-05-10 19:19:47 -04:00
# # is named "start" rather than "month".
2007-06-23 13:49:18 -04:00
# select_month(Date.today, :field_name => 'start')
#
# # Generates a select field for months that defaults to the current month that
2008-07-21 13:57:15 -04:00
# # will use keys like "1", "3".
2007-06-23 13:49:18 -04:00
# select_month(Date.today, :use_month_numbers => true)
#
# # Generates a select field for months that defaults to the current month that
# # will use keys like "1 - January", "3 - March".
# select_month(Date.today, :add_month_numbers => true)
#
# # Generates a select field for months that defaults to the current month that
# # will use keys like "Jan", "Mar".
# select_month(Date.today, :use_short_month => true)
#
# # Generates a select field for months that defaults to the current month that
# # will use keys like "Januar", "Marts."
# select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
2005-07-02 14:38:22 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for months with a custom prompt. Use <tt>:prompt => true</tt> for a
2008-12-21 14:46:33 -05:00
# # generic prompt.
# select_month(14, :prompt => 'Choose month')
#
2008-03-01 23:40:54 -05:00
def select_month ( date , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( date , options , html_options ) . select_month
2008-07-21 13:57:15 -04:00
end
2005-03-06 06:50:41 -05:00
2008-07-21 13:57:15 -04:00
# Returns a select tag with options for each of the five years on each side of the current, which is selected.
# The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
# +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
# greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
# Override the field name using the <tt>:field_name</tt> option, 'year' by default.
2007-06-23 13:49:18 -04:00
#
# ==== Examples
# # Generates a select field for years that defaults to the current year that
2011-05-10 19:19:47 -04:00
# # has ascending year values.
2007-06-23 13:49:18 -04:00
# select_year(Date.today, :start_year => 1992, :end_year => 2007)
#
# # Generates a select field for years that defaults to the current year that
2011-05-10 19:19:47 -04:00
# # is named 'birth' rather than 'year'.
2007-06-23 13:49:18 -04:00
# select_year(Date.today, :field_name => 'birth')
#
# # Generates a select field for years that defaults to the current year that
2011-05-10 19:19:47 -04:00
# # has descending year values.
2007-06-23 13:49:18 -04:00
# select_year(Date.today, :start_year => 2005, :end_year => 1900)
2004-11-23 20:04:44 -05:00
#
2007-06-23 13:49:18 -04:00
# # Generates a select field for years that defaults to the year 2006 that
2011-05-10 19:19:47 -04:00
# # has ascending year values.
2006-12-06 14:15:24 -05:00
# select_year(2006, :start_year => 2000, :end_year => 2010)
2005-07-02 14:38:22 -04:00
#
2011-05-10 19:19:47 -04:00
# # Generates a select field for years with a custom prompt. Use <tt>:prompt => true</tt> for a
2008-12-21 14:46:33 -05:00
# # generic prompt.
# select_year(14, :prompt => 'Choose year')
#
2008-03-01 23:40:54 -05:00
def select_year ( date , options = { } , html_options = { } )
2008-08-07 14:13:47 -04:00
DateTimeSelector . new ( date , options , html_options ) . select_year
end
2011-02-12 19:13:03 -05:00
2011-02-12 18:56:04 -05:00
# Returns an html time tag for the given date or time.
#
# ==== Examples
# time_tag Date.today # =>
# <time datetime="2010-11-04">November 04, 2010</time>
# time_tag Time.now # =>
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
# time_tag Date.yesterday, 'Yesterday' # =>
# <time datetime="2010-11-03">Yesterday</time>
# time_tag Date.today, :pubdate => true # =>
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
#
def time_tag ( date_or_time , * args )
options = args . extract_options!
2011-02-12 19:13:03 -05:00
format = options . delete ( :format ) || :long
content = args . first || I18n . l ( date_or_time , :format = > format )
2011-02-12 18:56:04 -05:00
datetime = date_or_time . acts_like? ( :time ) ? date_or_time . xmlschema : date_or_time . rfc3339
2011-02-12 19:13:03 -05:00
content_tag ( :time , content , options . reverse_merge ( :datetime = > datetime ) )
2011-02-12 18:56:04 -05:00
end
2008-08-07 14:13:47 -04:00
end
class DateTimeSelector #:nodoc:
include ActionView :: Helpers :: TagHelper
2010-06-21 16:11:12 -04:00
DEFAULT_PREFIX = 'date' . freeze
2008-08-07 14:13:47 -04:00
POSITION = {
:year = > 1 , :month = > 2 , :day = > 3 , :hour = > 4 , :minute = > 5 , :second = > 6
2010-06-21 16:11:12 -04:00
} . freeze
2008-08-07 14:13:47 -04:00
2011-04-28 16:19:45 -04:00
AMPM_TRANSLATION = Hash [
[ [ 0 , " 12 AM " ] , [ 1 , " 01 AM " ] , [ 2 , " 02 AM " ] , [ 3 , " 03 AM " ] ,
[ 4 , " 04 AM " ] , [ 5 , " 05 AM " ] , [ 6 , " 06 AM " ] , [ 7 , " 07 AM " ] ,
[ 8 , " 08 AM " ] , [ 9 , " 09 AM " ] , [ 10 , " 10 AM " ] , [ 11 , " 11 AM " ] ,
[ 12 , " 12 PM " ] , [ 13 , " 01 PM " ] , [ 14 , " 02 PM " ] , [ 15 , " 03 PM " ] ,
[ 16 , " 04 PM " ] , [ 17 , " 05 PM " ] , [ 18 , " 06 PM " ] , [ 19 , " 07 PM " ] ,
[ 20 , " 08 PM " ] , [ 21 , " 09 PM " ] , [ 22 , " 10 PM " ] , [ 23 , " 11 PM " ] ]
] . freeze
2008-08-07 14:13:47 -04:00
def initialize ( datetime , options = { } , html_options = { } )
@options = options . dup
@html_options = html_options . dup
@datetime = datetime
2010-04-29 00:15:01 -04:00
@options [ :datetime_separator ] || = ' — '
@options [ :time_separator ] || = ' : '
2008-08-07 14:13:47 -04:00
end
def select_datetime
2010-04-29 00:15:01 -04:00
order = date_order . dup
order -= [ :hour , :minute , :second ]
@options [ :discard_year ] || = true unless order . include? ( :year )
@options [ :discard_month ] || = true unless order . include? ( :month )
@options [ :discard_day ] || = true if @options [ :discard_month ] || ! order . include? ( :day )
@options [ :discard_minute ] || = true if @options [ :discard_hour ]
@options [ :discard_second ] || = true unless @options [ :include_seconds ] && ! @options [ :discard_minute ]
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
2011-03-04 00:09:26 -05:00
# valid (otherwise it could be 31 and February wouldn't be a valid date)
2010-04-29 00:15:01 -04:00
if @datetime && @options [ :discard_day ] && ! @options [ :discard_month ]
@datetime = @datetime . change ( :day = > 1 )
end
2008-08-07 14:13:47 -04:00
if @options [ :tag ] && @options [ :ignore_date ]
select_time
2010-04-29 00:15:01 -04:00
else
2008-08-07 14:13:47 -04:00
[ :day , :month , :year ] . each { | o | order . unshift ( o ) unless order . include? ( o ) }
order += [ :hour , :minute , :second ] unless @options [ :discard_hour ]
build_selects_from_types ( order )
end
end
def select_date
order = date_order . dup
2010-04-29 00:15:01 -04:00
@options [ :discard_hour ] = true
@options [ :discard_minute ] = true
@options [ :discard_second ] = true
2008-08-07 14:13:47 -04:00
2010-04-29 00:15:01 -04:00
@options [ :discard_year ] || = true unless order . include? ( :year )
@options [ :discard_month ] || = true unless order . include? ( :month )
@options [ :discard_day ] || = true if @options [ :discard_month ] || ! order . include? ( :day )
2008-08-07 14:13:47 -04:00
2010-04-29 00:15:01 -04:00
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
2011-03-04 00:09:26 -05:00
# valid (otherwise it could be 31 and February wouldn't be a valid date)
2010-04-29 00:15:01 -04:00
if @datetime && @options [ :discard_day ] && ! @options [ :discard_month ]
@datetime = @datetime . change ( :day = > 1 )
2008-08-07 14:13:47 -04:00
end
[ :day , :month , :year ] . each { | o | order . unshift ( o ) unless order . include? ( o ) }
build_selects_from_types ( order )
end
def select_time
order = [ ]
2010-04-29 00:15:01 -04:00
@options [ :discard_month ] = true
@options [ :discard_year ] = true
@options [ :discard_day ] = true
@options [ :discard_second ] || = true unless @options [ :include_seconds ]
2008-08-07 14:13:47 -04:00
2010-04-29 00:15:01 -04:00
order += [ :year , :month , :day ] unless @options [ :ignore_date ]
2008-08-07 14:13:47 -04:00
order += [ :hour , :minute ]
order << :second if @options [ :include_seconds ]
build_selects_from_types ( order )
end
def select_second
if @options [ :use_hidden ] || @options [ :discard_second ]
build_hidden ( :second , sec ) if @options [ :include_seconds ]
else
build_options_and_select ( :second , sec )
end
end
def select_minute
if @options [ :use_hidden ] || @options [ :discard_minute ]
build_hidden ( :minute , min )
else
build_options_and_select ( :minute , min , :step = > @options [ :minute_step ] )
end
end
def select_hour
if @options [ :use_hidden ] || @options [ :discard_hour ]
build_hidden ( :hour , hour )
else
2011-04-29 00:51:16 -04:00
build_options_and_select ( :hour , hour , :end = > 23 , :ampm = > @options [ :ampm ] )
2008-08-07 14:13:47 -04:00
end
end
def select_day
if @options [ :use_hidden ] || @options [ :discard_day ]
build_hidden ( :day , day )
else
2011-12-11 16:19:11 -05:00
build_options_and_select ( :day , day , :start = > 1 , :end = > 31 , :leading_zeros = > false , :use_two_digit_numbers = > @options [ :use_two_digit_numbers ] )
2008-08-07 14:13:47 -04:00
end
end
def select_month
if @options [ :use_hidden ] || @options [ :discard_month ]
build_hidden ( :month , month )
else
month_options = [ ]
1 . upto ( 12 ) do | month_number |
options = { :value = > month_number }
options [ :selected ] = " selected " if month == month_number
month_options << content_tag ( :option , month_name ( month_number ) , options ) + " \n "
end
build_select ( :month , month_options . join )
end
end
def select_year
if ! @datetime || @datetime == 0
2008-07-21 13:57:15 -04:00
val = ''
2008-06-30 15:15:12 -04:00
middle_year = Date . today . year
else
2008-08-07 14:13:47 -04:00
val = middle_year = year
2008-06-30 15:15:12 -04:00
end
2008-08-07 14:13:47 -04:00
if @options [ :use_hidden ] || @options [ :discard_year ]
build_hidden ( :year , val )
2006-12-06 14:15:24 -05:00
else
2011-09-14 18:35:53 -04:00
options = { }
options [ :start ] = @options [ :start_year ] || middle_year - 5
options [ :end ] = @options [ :end_year ] || middle_year + 5
options [ :step ] = options [ :start ] < options [ :end ] ? 1 : - 1
options [ :leading_zeros ] = false
options [ :max_years_allowed ] = @options [ :max_years_allowed ] || 1000
if ( options [ :end ] - options [ :start ] ) . abs > options [ :max_years_allowed ]
raise ArgumentError , " There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter "
end
2008-08-07 14:13:47 -04:00
build_options_and_select ( :year , val , options )
2004-11-23 20:04:44 -05:00
end
end
2005-03-06 06:50:41 -05:00
2004-11-23 20:04:44 -05:00
private
2008-08-07 14:13:47 -04:00
%w( sec min hour day month year ) . each do | method |
define_method ( method ) do
@datetime . kind_of? ( Fixnum ) ? @datetime : @datetime . send ( method ) if @datetime
end
end
# Returns translated month names, but also ensures that a custom month
2011-05-10 19:19:47 -04:00
# name array has a leading nil element.
2008-08-07 14:13:47 -04:00
def month_names
2011-06-16 07:01:35 -04:00
@month_names || = begin
month_names = @options [ :use_month_names ] || translated_month_names
month_names . unshift ( nil ) if month_names . size < 13
month_names
end
2008-08-07 14:13:47 -04:00
end
2011-05-10 19:19:47 -04:00
# Returns translated month names.
2008-08-07 14:13:47 -04:00
# => [nil, "January", "February", "March",
# "April", "May", "June", "July",
# "August", "September", "October",
# "November", "December"]
#
2011-05-10 19:19:47 -04:00
# If <tt>:use_short_month</tt> option is set
2008-08-07 14:13:47 -04:00
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
def translated_month_names
2010-08-26 21:07:56 -04:00
key = @options [ :use_short_month ] ? :'date.abbr_month_names' : :'date.month_names'
I18n . translate ( key , :locale = > @options [ :locale ] )
2008-08-07 14:13:47 -04:00
end
2011-05-10 19:19:47 -04:00
# Lookup month name for number.
2008-08-07 14:13:47 -04:00
# month_name(1) => "January"
#
2011-05-10 19:19:47 -04:00
# If <tt>:use_month_numbers</tt> option is passed
2008-08-07 14:13:47 -04:00
# month_name(1) => 1
#
2011-05-10 19:19:47 -04:00
# If <tt>:add_month_numbers</tt> option is passed
2008-08-07 14:13:47 -04:00
# month_name(1) => "1 - January"
def month_name ( number )
if @options [ :use_month_numbers ]
number
2011-12-11 16:19:11 -05:00
elsif @options [ :use_two_digit_numbers ]
sprintf " %02d " , number
2008-08-07 14:13:47 -04:00
elsif @options [ :add_month_numbers ]
" #{ number } - #{ month_names [ number ] } "
else
month_names [ number ]
end
end
def date_order
2011-06-16 07:01:35 -04:00
@date_order || = @options [ :order ] || translated_date_order
2008-08-07 14:13:47 -04:00
end
def translated_date_order
2012-01-22 16:47:36 -05:00
date_order = I18n . translate ( :'date.order' , :locale = > @options [ :locale ] ) || [ ]
forbidden_elements = date_order - [ :year , :month , :day ]
if forbidden_elements . any?
raise StandardError ,
" #{ @options [ :locale ] } .date.order only accepts :year, :month and :day "
end
date_order
2008-08-07 14:13:47 -04:00
end
2011-05-10 19:19:47 -04:00
# Build full select tag from date type and options.
2008-08-07 14:13:47 -04:00
def build_options_and_select ( type , selected , options = { } )
build_select ( type , build_options ( selected , options ) )
end
2011-05-10 19:19:47 -04:00
# Build select option html from date value and options.
2008-08-07 14:13:47 -04:00
# build_options(15, :start => 1, :end => 31)
# => "<option value="1">1</option>
2011-05-14 18:40:32 -04:00
# <option value="2">2</option>
# <option value="3">3</option>..."
2011-05-10 19:19:47 -04:00
#
# If <tt>:step</tt> options is passed
# build_options(15, :start => 1, :end => 31, :step => 2)
# => "<option value="1">1</option>
2011-05-14 18:40:32 -04:00
# <option value="3">3</option>
# <option value="5">5</option>..."
2008-08-07 14:13:47 -04:00
def build_options ( selected , options = { } )
start = options . delete ( :start ) || 0
stop = options . delete ( :end ) || 59
step = options . delete ( :step ) || 1
2011-12-11 16:19:11 -05:00
options . reverse_merge! ( { :leading_zeros = > true , :ampm = > false , :use_two_digit_numbers = > false } )
2010-07-17 16:08:17 -04:00
leading_zeros = options . delete ( :leading_zeros )
2008-07-21 13:57:15 -04:00
select_options = [ ]
2008-08-07 14:13:47 -04:00
start . step ( stop , step ) do | i |
value = leading_zeros ? sprintf ( " %02d " , i ) : i
2008-07-21 13:57:15 -04:00
tag_options = { :value = > value }
tag_options [ :selected ] = " selected " if selected == i
2011-12-11 16:19:11 -05:00
text = options [ :use_two_digit_numbers ] ? sprintf ( " %02d " , i ) : value
2011-12-20 12:25:56 -05:00
text = options [ :ampm ] ? AMPM_TRANSLATION [ i ] : text
2011-04-29 00:51:16 -04:00
select_options << content_tag ( :option , text , tag_options )
2008-07-21 13:57:15 -04:00
end
2010-02-13 16:53:26 -05:00
( select_options . join ( " \n " ) + " \n " ) . html_safe
2008-07-21 13:57:15 -04:00
end
2006-12-06 14:15:24 -05:00
2011-05-10 19:19:47 -04:00
# Builds select tag from date type and html select options.
2008-08-07 14:13:47 -04:00
# build_select(:month, "<option value="1">January</option>...")
# => "<select id="post_written_on_2i" name="post[written_on(2i)]">
# <option value="1">January</option>...
# </select>"
def build_select ( type , select_options_as_html )
select_options = {
:id = > input_id_from_type ( type ) ,
:name = > input_name_from_type ( type )
} . merge ( @html_options )
select_options . merge! ( :disabled = > 'disabled' ) if @options [ :disabled ]
2008-02-19 23:23:18 -05:00
select_html = " \n "
2011-12-26 07:56:47 -05:00
select_html << content_tag ( :option , '' , :value = > '' ) + " \n " if @options [ :include_blank ]
2008-12-21 14:46:33 -05:00
select_html << prompt_option_tag ( type , @options [ :prompt ] ) + " \n " if @options [ :prompt ]
2010-02-13 16:53:26 -05:00
select_html << select_options_as_html
2008-08-07 14:13:47 -04:00
2010-02-13 16:53:26 -05:00
( content_tag ( :select , select_html . html_safe , select_options ) + " \n " ) . html_safe
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2011-05-10 19:19:47 -04:00
# Builds a prompt option tag with supplied options or from default options.
2008-12-21 14:46:33 -05:00
# prompt_option_tag(:month, :prompt => 'Select month')
# => "<option value="">Select month</option>"
def prompt_option_tag ( type , options )
2010-08-26 21:07:56 -04:00
prompt = case options
when Hash
default_options = { :year = > false , :month = > false , :day = > false , :hour = > false , :minute = > false , :second = > false }
default_options . merge! ( options ) [ type . to_sym ]
when String
options
else
I18n . translate ( :" datetime.prompts. #{ type } " , :locale = > @options [ :locale ] )
2008-12-21 14:46:33 -05:00
end
2008-12-22 06:41:47 -05:00
prompt ? content_tag ( :option , prompt , :value = > '' ) : ''
2008-12-21 14:46:33 -05:00
end
2011-05-10 19:19:47 -04:00
# Builds hidden input tag for date part and value.
2008-08-07 14:13:47 -04:00
# build_hidden(:year, 2008)
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
def build_hidden ( type , value )
2010-01-08 14:48:38 -05:00
( tag ( :input , {
2008-08-07 14:13:47 -04:00
:type = > " hidden " ,
:id = > input_id_from_type ( type ) ,
:name = > input_name_from_type ( type ) ,
:value = > value
2010-03-05 16:40:41 -05:00
} . merge ( @html_options . slice ( :disabled ) ) ) + " \n " ) . html_safe
2006-12-06 14:15:24 -05:00
end
2011-05-10 19:19:47 -04:00
# Returns the name attribute for the input tag.
2008-08-07 14:13:47 -04:00
# => post[written_on(1i)]
def input_name_from_type ( type )
prefix = @options [ :prefix ] || ActionView :: Helpers :: DateTimeSelector :: DEFAULT_PREFIX
2009-01-04 09:54:35 -05:00
prefix += " [ #{ @options [ :index ] } ] " if @options . has_key? ( :index )
2008-08-07 14:13:47 -04:00
field_name = @options [ :field_name ] || type
if @options [ :include_position ]
field_name += " ( #{ ActionView :: Helpers :: DateTimeSelector :: POSITION [ type ] } i) "
end
@options [ :discard_type ] ? prefix : " #{ prefix } [ #{ field_name } ] "
end
2011-05-10 19:19:47 -04:00
# Returns the id attribute for the input tag.
2008-08-07 14:13:47 -04:00
# => "post_written_on_1i"
def input_id_from_type ( type )
input_name_from_type ( type ) . gsub ( / ([ \ [ \ (])|( \ ] \ [) / , '_' ) . gsub ( / [ \ ] \ )] / , '' )
end
2009-03-16 07:28:36 -04:00
# Given an ordering of datetime components, create the selection HTML
# and join them with their appropriate separators.
2008-08-07 14:13:47 -04:00
def build_selects_from_types ( order )
select = ''
2011-12-14 15:20:23 -05:00
first_visible = order . find { | type | ! @options [ :" discard_ #{ type } " ] }
2008-08-07 14:13:47 -04:00
order . reverse . each do | type |
2011-12-14 15:20:23 -05:00
separator = separator ( type ) unless type == first_visible # don't add before first visible field
2008-08-07 14:13:47 -04:00
select . insert ( 0 , separator . to_s + send ( " select_ #{ type } " ) . to_s )
end
For performance reasons, you can no longer call html_safe! on Strings. Instead, all Strings are always not html_safe?. Instead, you can get a SafeBuffer from a String by calling #html_safe, which will SafeBuffer.new(self).
* Additionally, instead of doing concat("</form>".html_safe), you can do
safe_concat("</form>"), which will skip both the flag set, and the flag
check.
* For the first pass, I converted virtually all #html_safe!s to #html_safe,
and the tests pass. A further optimization would be to try to use
#safe_concat as much as possible, reducing the performance impact if
we know up front that a String is safe.
2010-01-31 22:17:42 -05:00
select . html_safe
2008-08-07 14:13:47 -04:00
end
2011-05-10 19:19:47 -04:00
# Returns the separator for a given datetime component.
2008-08-07 14:13:47 -04:00
def separator ( type )
case type
2010-10-15 13:13:01 -04:00
when :year
@options [ :discard_year ] ? " " : @options [ :date_separator ]
2010-06-14 06:17:42 -04:00
when :month
@options [ :discard_month ] ? " " : @options [ :date_separator ]
when :day
@options [ :discard_day ] ? " " : @options [ :date_separator ]
2008-08-07 14:13:47 -04:00
when :hour
( @options [ :discard_year ] && @options [ :discard_day ] ) ? " " : @options [ :datetime_separator ]
when :minute
2010-02-24 19:28:40 -05:00
@options [ :discard_minute ] ? " " : @options [ :time_separator ]
2008-08-07 14:13:47 -04:00
when :second
@options [ :include_seconds ] ? @options [ :time_separator ] : " "
end
2006-12-06 14:15:24 -05:00
end
2004-11-23 20:04:44 -05:00
end
2005-11-13 06:13:11 -05:00
class FormBuilder
2008-03-01 23:40:54 -05:00
def date_select ( method , options = { } , html_options = { } )
2009-01-04 09:54:35 -05:00
@template . date_select ( @object_name , method , objectify_options ( options ) , html_options )
2005-11-13 06:13:11 -05:00
end
2008-03-01 23:40:54 -05:00
def time_select ( method , options = { } , html_options = { } )
2009-01-04 09:54:35 -05:00
@template . time_select ( @object_name , method , objectify_options ( options ) , html_options )
2006-12-06 14:15:24 -05:00
end
2008-03-01 23:40:54 -05:00
def datetime_select ( method , options = { } , html_options = { } )
2009-01-04 09:54:35 -05:00
@template . datetime_select ( @object_name , method , objectify_options ( options ) , html_options )
2005-11-13 06:13:11 -05:00
end
end
2004-11-23 20:04:44 -05:00
end
2005-05-19 13:40:02 -04:00
end