Rails 12 hour AM/PM range for a day - ruby-on-rails

This is a really simple question, and it's probably been asked and answered before, but I haven't been able to find anything.
Anyway, I need a range/array for 12 hour time, so like 12AM - 11AM, 12PM - 11PM. You probably get the gist of it. Right now I'm trying to do an absurdly complicated method involving mapping AM onto one array, PM onto another one, and then joining the two arrays together. There has to be an easier way to do this.
I know about Rails time_select, but I need a different format than what it provides. Any suggestions?
Clarification: So what I'm looking for is the 12-hour clock, with AM and PM. If I wanted a 24-hour clock, I could just do (0..24), and be done. But the 12-hour clock goes from 12-11 AM, and then goes from 12-11 PM. I'm pretty sure someone has done this before.

I agree with #MrYoshi's comment, the easiest way of formatting a date is .strftime(),
see RubyDoc for all possible options
Example:
Time.now.strftime("%I:%M %p")
output: HH:MM AM
Or what you literally asked for:
Time.now.strftime("%I:00")
output: HH:00
As you mentioned time_select I assume you want to offer time as a user selectable range, so try these options for time_select(more options):
time_select 'game', 'game_time', {:minute_step => 60, :ampm => true}
also this previous question: Time select form helper with 12 hour format for Rails 3?

Rails does this built in
<%= f.time_select :start, {ampm: true} %>
http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-time_select

I know this has already been answered awhile ago with the built in. But I needed a custom function to get these values and wrote this:
times = {"12 AM" => 0}.merge!(1.upto(11).collect { |n| {"#{n} AM" => n} }.reduce(Hash.new, :merge)).merge!({"12 PM" => 12}).merge!(1.upto(11).collect { |n| {"#{n} PM" => n + 12} }.reduce(Hash.new, :merge))
This yields:
{"12 AM"=>0, "1 AM"=>1, "2 AM"=>2, "3 AM"=>3, "4 AM"=>4, "5 AM"=>5, "6 AM"=>6, "7 AM"=>7, "8 AM"=>8, "9 AM"=>9, "10 AM"=>10, "11 AM"=>11, "12 PM"=>12, "1 PM"=>13, "2 PM"=>14, "3 PM"=>15, "4 PM"=>16, "5 PM"=>17, "6 PM"=>18, "7 PM"=>19, "8 PM"=>20, "9 PM"=>21, "10 PM"=>22, "11 PM"=>23}

Related

How to add the characters "st" ,"nd", "th" next to the day e.g 1st, 2nd, 28th

I am trying to setup a date format but from docs and other websites i checked there is no mention on how to make this type of format
2nd of January 2017
Date.tomorrow.strftime("%e %B %Y")
will give
28 January 2017
how can i make it
28th of January 2017 ?
is it possible?
If you want the exact format you mentioned :
d = Date.tomorrow
d.strftime("#{d.day.ordinalize} of %B %Y")
=> "28th of January 2017"
If you use it multiple times, you could define :
Date::DATE_FORMATS[:my_date_format] = lambda { |date| date.strftime("#{date.day.ordinalize} of %B %Y") }
in config/initializers/date_formats.rb (create it if not already present)
You can then call :
Date.tomorrow.to_s(:my_date_format)
=> "28th of January 2017"
In Rails Time, Date and DateTime have to_formatted_s methods:
In your case you're looking for Date#to_formatted_s:
Date.tomorrow.to_formatted_s(:long_ordinal)
#=> "January 28th, 2017"

Active Record order by current day

Im having an issue sorting an array by the current date.
my database has a field called day
day has the days of the week eg: Monday, Tuesday, etc.
I am trying to sort my index view page by the current day.
I would like to do somehting like this in my controller,
#happies = Happy.where(id: #search.results.map(&:id))
.page(params[:page])
.where(:day => Date.today.strftime('%A').capitalize.to_s)
but instead of returning only happies with the day Monday I would want to order by day where day is equal to the current day.
I also thought about doing this in my view
with something like
<% #happies.sort_by(:day => Date.today.strftime('%A').capitalize.to_s).each do |happy| %>
the above does not work but im trying to get accross what I wan to achieve. Any ideas on how to implement this?
Maybe there is an activeview helper?
If you were not paginating, you could sort the results in plain ruby like this:
day_order = %w(Tuesday Wednesday Thursday Friday Saturday Sunday Monday)
#happies = #happies.sort_by{|happy| day_order.index(happy.day)}
sort_by takes a block that returns the value to sort by.
However, you appear to be paginating using will_paginate. That's fine but it makes things more complicated.
The pagination necessarily happens in the database via limit and offset (so as to avoid returning the entire contents of the table to the Rails process). Therefore you want the sorting to also happen in the database. If you were to sort in ruby (as above) you would be sorting after pagination, meaning the first page would give you essentially random results, and then you'd sort them. Probably not what you want.
Long story short, you probably want to use order instead of sort_by, and I'm going to have to dip into SQL for this one:
#happies = Happy.where(id: #search.results.map(&:id))
.page(params[:page])
.order("CASE day WHEN 'Tuesday' THEN 0 " \
"WHEN 'Wednesday' THEN 1 " \
"WHEN 'Thursday' THEN 2 " \
"WHEN 'Friday' THEN 3 " \
"WHEN 'Saturday' THEN 4 " \
"WHEN 'Sunday' THEN 5 " \
"WHEN 'Monday' THEN 6 END")
If you want to avoid SQL, perhaps it is possible to use Arel for this, I'm not sure.
Edit
I see now you want to start with the current day, i.e. not hardcoded as Tuesday like I did. To fix my SQL version - and borrowing a bit from #Snarf's answer - you could do this:
days = %w(Monday Tuesday Wednesday Thursday Friday Saturday Sunday)
days.rotate!(days.index(Time.zone.now.strftime("%A")))
case_pieces = days.each_with_index.map do |day, i|
"WHEN '#{day}' THEN #{i}"
end
#happies = Happy.where(id: #search.results.map(&:id))
.page(params[:page])
.order("CASE day #{case_pieces.join(' ')} END")
Another thought
If I was writing the app myself, I would be tempted to store the day as an integer from 0 to 6, instead of as a string. Then you could order using the modulo operator, something like this:
days = %w(Monday Tuesday Wednesday Thursday Friday Saturday Sunday)
day_offset = days.index(Time.zone.now.strftime("%A"))
#happies = Happy.where(id: #search.results.map(&:id))
.page(params[:page])
.order("(day - #{day_offset} + 7) % 7")
Here's a pure ruby solution.
days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
days.rotate(days.index(Time.now.strftime("%A")))
#=> ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
Basically the same as #Snarf - this is only ruby and will not support pagination
today = Date.current.strftime('%A')
# => Monday
days = Date::DAYNAMES
# => ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
today_index = days.index(today)
# => 1
#ordered_days = days.rotate(today_index)
# => ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
#happies = Happy.where(id: #search.results)
<!-- view -->
<% #ordered_days.each do |day| %>
<!-- in memory selection from the #happies collection, could be emtpy -->
<% occurring_on = #happies.select { |happy| happy.day == day } %>
<%= day %>
<% occuring_on.each do |happy| %>
<%= happy %>
<% end %>
<% end %>

Is there a time ahead words in rails (opposite of time_ago_in_words)

Is there a rails helper opposite of time_ago_in_words ? I can't seem to find the opposite...where I can have 7 days from now converted to "in about 1 week".
I can get this to work for the past:
<%= time_ago_in_words( milestone.due_date) %>
But this doesn't work for the future:
<%= time_ahead_in_words(milestone.due_date) %>
stumped.
You can use distance_of_time_in_words(from_time, to_time = 0, options = {}) instead.
So you could set it up like so:
<%= distance_of_time_in_words(Time.now, milestone.due_date) %>
Sounds like you're in need of the "distance_in_time" helper method.
Ex.
from_time = Time.current
helper.distance_of_time_in_words(from_time, from_time + 50.minutes)
=>about 1 hour
More specifically, in your case you would do:
from_time = Time.current
helper.distance_of_time_in_words(from_time, from_time + 6.days)
=>about 7 days
You can use distance_of_time_in_words. For example:
distance_of_time_in_words(Time.current, milestone.due_date)
would be equivalent to your time_ahead_in_words pseudo code.
See distance_of_time_in_words documentation for details.
distance_of_time_in_words is what you're after. Be aware that:
distance_of_time_in_words doesn't assume you mean from now, so you have to tell it that with Time.current. E.g.
appointment_start_time = ActiveSupport::TimeZone['Sydney'].parse("2022-11-14 07:00:00 UTC")
distance_of_time_in_words(Time.current, appointment_start_time)
=> "about 2 years"
distance_of_time_in_words will still return a result if the time was in the past, so beware of that!
if using distance_of_time_in_words in the rails console, you need to prepend helper., like so:
helper.distance_of_time_in_words(Time.current, appointment_start_time)
=> "about 2 years"
References:
docs for distance_of_time_in_words
ActiveSupport TimeZones and their aliases

Ordinalize in embedded ruby

Is there a quick way to be able to ordinalize the following code?
<%= time_tag(Date.today, :format=>'%A %d %b') %>
The current output reads
Tuesday 18 Feb
I want to ordinalize the date to show
Tuesday 18th Feb
Any suggestions?
You can use Date::DATE_FORMATS to add a new customized format, and Integer.ordinalize to get the day ordinal:
Date::DATE_FORMATS[:month_ordinal] = lambda { |date|
date.strftime("%A #{date.day.ordinalize}, %B")
}
>> Date.today.to_formatted_s(:month_ordinal)
=> "Tuesday 18th, Feb"
Write as below using #ordinalize :
<%= time_tag(Date.today, :format=>"%A #{Date.today.day.ordinalize} %b") %>

time_select form helper interprets new time object as UTC not as configured time zone

I am running into what to me seems like a simple issue, but I can't figure out what I am doing wrong. In my app users can add their course through a simple form_for. They can enter a start_time and end_time for the course's lectures, like so:
<div class="field">
Start Time<br />
<%= time_select :course, :start_time, { :minute_step => 5, :ampm => true } %>
</div>
<div class="field">
End Time<br />
<%= time_select :course, :end_time, { :minute_step => 5, :ampm => true } %>
</div>
I configured my time zone in application.rb to be set to Eastern Time and this seems to work correctly as created_at is returned in the right time zone.
However, the problem I am encountering is that start_time and end_time are entered as UTC into the database. So when a user selects a class to start at 10 AM it is entered as 10 UTC not 10 AM EST / 15:00 UTC. What am I missing here? It seems like what I want to happen should be possible.
Summarizing the answer from the comments in order to remove this question from the "Unanswered" filter:
Suggested by Tim Brandes:
Maybe this helps you: Timezone with rails 3 ... I have never used a time_select but always a datetime_select when dealing with timezones, maybe this could be an issue, but it seems rather unlikely.
Solution that worked for Philip V:
My start_time and end_time are not being saved within the time zone but saved as a UTC time. ... I changed the column type for start_time and end_time to datetime and this fixed the issues I had. I guess time columns only allow UTC.
I was struggling with the same problem. But then I realized I didn't need the timezone information of the Time object.
As you said, the database stores it as UTC, but the really important thing we need to do with this data is:
course.start_time.hour
course.start_time.min
This is timezone independent. (8 hours is 8 hours from midnight, no matter what midnight you're comparing. 30 minutes is 30 minutes from the minute 0 of the hour. No matter in what timezone is that hour you're using).
So in most applications where the need of a Time storage structure is required, like to know when a recurrent event starts or finish, to store the value of a timer, or a the time of a lap in a race, you can use the UTC Time the database returns with no problem.

Resources