Random space in Rails DateTime when using RSpec? - ruby-on-rails

I have a method in my Event model:
def when
self.starts_at.strftime("%a %b %e # %-I:%M")
end
which outputs correctly in the rails console or on a webpage.
However, doing an rspec test like this:
it "has simple date display" do
game = FactoryGirl.build(:event, starts_at: DateTime.parse("1/1/2014 3:30pm"))
game.when.should == "Wed Jan 1 # 3:30"
end
fails, because of:
1) Event has simple date display
Failure/Error: event.when.should == "Wed Jan 1 # 3:30"
expected: "Wed Jan 1 # 3:30"
got: "Wed Jan 1 # 3:30" (using ==)
Why is there a random space in my formatted DateTime? Shouldn't the same DateTime code be loaded/running for tests and console?

You're using the %e format directive, which is for the blank-padded day of month. It sounds like you want the unpadded day of the month directive, which is %-d. Here are the docs.

The reason your test is failing is because the %e directives in strftime is:
%e - Day of the month, blank-padded ( 1..31)
So the statement DateTime.parse("1/1/2014 3:30pm").strftime("%a %b %e # %-I:%M") yields Wed Jan 1 # 3:30 (with extra space prepended for days between 1 through 9).
You could use %d directive instead which gives day of month, zero-padded. E.g.
# Event model
def when
self.starts_at.strftime("%a %b %d # %-I:%M")
end
Then in your spec:
it "has simple date display" do
game = FactoryGirl.build(:event, starts_at: DateTime.parse("1/1/2014 3:30pm"))
game.when.should == "Wed Jan 01 # 3:30"
end

Related

Get start date and end date of month

I want to get start and end date of month in ruby on rails. User enter integer value from 1..12.
If user enter 1 then JAN start_date,end_date
If user enter 2 then FEB start_date,end_date
If user enter 3 then MARCH start_date,end_date
and so on
There are built-in methods in active_support (part of Rails):
month_number = 5 # user-provided month number
month_beginning = Date.new(Date.today.year, month_number)
# => Sun, 01 May 2016
month_ending = month_beginning.end_of_month
# => Tue, 31 May 2016

How do I push dates into an array by week?

I am writing this code that maps dates by day and pushes them into an array:
rtn_ary = []
(2.weeks.ago.to_date..Date.today).map do |date|
rtn_ary << {period: date}
end
How can I write the code if my expected result is based on weeks instead of days like this?
[[{:period=>Mon, 28 Sep 2015}, {:period=>Tue, 05 Oct 2015}, {:period=>Tue, 12 Oct 2015}]
A simple solution could be to iterate by 7 days instead of 1 day:
rtn_ary = (2.weeks.ago.to_date..Date.today).step(7).map do |date|
{period: date}
end
=> [{:period=>Mon, 28 Sep 2015}, {:period=>Mon, 05 Oct 2015}]
More info about step: http://ruby-doc.org/core-1.9.3/Range.html#method-i-step
To get what you want you'll need an additional step:
(2.weeks.ago.to_date..Date.today).map do |date|
rtn_ary << {period: date.to_formatted_s(:long)}
end
The to_formatted_s() method convert the date into a formatted string and takes a parameter which will define how the date will be formatted into a string.
date.to_formatted_s(:short) # => "10 Nov"
date.to_formatted_s(:number) # => "20071110"
date.to_formatted_s(:long) # => "November 10, 2007"
date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
date.to_formatted_s(:rfc822) # => "10 Nov 2007"
date.to_formatted_s(:iso8601) # => "2007-11-10"
Take a look at the rails Documentation

Format the date using Ruby on Rails

The flickr api provides a posted date as unix timestamp one: "The posted date is always passed around as a unix timestamp, which is an unsigned integer specifying the number of seconds since Jan 1st 1970 GMT."
For example, here is the date '1100897479'. How do I format it using Ruby on Rails?
Once you have parsed the timestamp string and have a time object (see other answers for details), you can use Time.to_formatted_s from Rails. It has several formats built in that you can specify with symbols.
Quote:
time = Time.now # => Thu Jan 18 06:10:17 CST 2007
time.to_formatted_s(:time) # => "06:10"
time.to_s(:time) # => "06:10"
time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
time.to_formatted_s(:number) # => "20070118061017"
time.to_formatted_s(:short) # => "18 Jan 06:10"
time.to_formatted_s(:long) # => "January 18, 2007 06:10"
time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
(Time.to_s is an alias)
You can also define your own formats - usually in an initializer (Thanks to Dave Newton for pointing this out). This is how it's done:
# config/initializers/time_formats.rb
Time::DATE_FORMATS[:month_and_year] = "%B %Y"
Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
Here's my go at answering this,
so first you will need to convert the timestamp to an actual Ruby Date/Time. If you receive it just as a string or int from facebook, you will need to do something like this:
my_date = Time.at(timestamp_from_facebook.to_i)
OK, so now assuming you already have your date object...
to_formatted_s is a handy Ruby function that turns dates into formatted strings.
Here are some examples of its usage:
time = Time.now # => Thu Jan 18 06:10:17 CST 2007
time.to_formatted_s(:time) # => "06:10"
time.to_s(:time) # => "06:10"
time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
time.to_formatted_s(:number) # => "20070118061017"
time.to_formatted_s(:short) # => "18 Jan 06:10"
time.to_formatted_s(:long) # => "January 18, 2007 06:10"
time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
As you can see: :db, :number, :short ... are custom date formats.
To add your own custom format, you can create this file: config/initializers/time_formats.rb and add your own formats there, for example here's one:
Date::DATE_FORMATS[:month_day_comma_year] = "%B %e, %Y" # January 28, 2015
Where :month_day_comma_year is your format's name (you can change this to anything you want), and where %B %e, %Y is unix date format.
Here's a quick cheatsheet on unix date syntax, so you can quickly setup your custom format:
From http://linux.die.net/man/3/strftime
%a - The abbreviated weekday name (``Sun'')
%A - The full weekday name (``Sunday'')
%b - The abbreviated month name (``Jan'')
%B - The full month name (``January'')
%c - The preferred local date and time representation
%d - Day of the month (01..31)
%e - Day of the month without leading 0 (1..31)
%g - Year in YY (00-99)
%H - Hour of the day, 24-hour clock (00..23)
%I - Hour of the day, 12-hour clock (01..12)
%j - Day of the year (001..366)
%m - Month of the year (01..12)
%M - Minute of the hour (00..59)
%p - Meridian indicator (``AM'' or ``PM'')
%S - Second of the minute (00..60)
%U - Week number of the current year,
starting with the first Sunday as the first
day of the first week (00..53)
%W - Week number of the current year,
starting with the first Monday as the first
day of the first week (00..53)
%w - Day of the week (Sunday is 0, 0..6)
%x - Preferred representation for the date alone, no time
%X - Preferred representation for the time alone, no date
%y - Year without a century (00..99)
%Y - Year with century
%Z - Time zone name
%% - Literal ``%'' character
t = Time.now
t.strftime("Printed on %m/%d/%Y") #=> "Printed on 04/09/2003"
t.strftime("at %I:%M%p") #=> "at 08:56AM"
Hope this helped you.
I've also made a github gist of this little guide, in case anyone prefers.
Easiest is to use strftime (docs).
If it's for use on the view side, better to wrap it in a helper, though.
#CMW's answer is bang on the money. I've added this answer as an example of how to configure an initializer so that both Date and Time objects get the formatting
config/initializers/time_formats.rb
date_formats = {
concise: '%d-%b-%Y' # 13-Jan-2014
}
Time::DATE_FORMATS.merge! date_formats
Date::DATE_FORMATS.merge! date_formats
Also the following two commands will iterate through all the DATE_FORMATS in your current environment, and display today's date and time in each format:
Date::DATE_FORMATS.keys.each{|k| puts [k,Date.today.to_formatted_s(k)].join(':- ')}
Time::DATE_FORMATS.keys.each{|k| puts [k,Time.now.to_formatted_s(k)].join(':- ')}
Have a look at localize, or l
eg:
l Time.at(1100897479)
First you will need to convert the timestamp to an actual Ruby Date/Time.
If you receive it just as a string or int from facebook, you will need to do something like this:
my_date = Time.at(timestamp_from_facebook.to_i)
Then to format it nicely in the view, you can just use to_s (for the default formatting):
<%= my_date.to_s %>
Note that if you don't put to_s, it will still be called by default if you use it in a view or in a string e.g. the following will also call to_s on the date:
<%= "Here is a date: #{my_date}" %>
or if you want the date formatted in a specific way (eg using "d/m/Y") - you can use strftime as outlined in the other answer.
Since the timestamps are seconds since the UNIX epoch, you can use DateTime.strptime ("string parse time") with the correct specifier:
Date.strptime('1100897479', '%s')
#=> #<Date: 2004-11-19 ((2453329j,0s,0n),+0s,2299161j)>
Date.strptime('1100897479', '%s').to_s
#=> "2004-11-19"
DateTime.strptime('1100897479', '%s')
#=> #<DateTime: 2004-11-19T20:51:19+00:00 ((2453329j,75079s,0n),+0s,2299161j)>
DateTime.strptime('1100897479', '%s').to_s
#=> "2004-11-19T20:51:19+00:00"
Note that you have to require 'date' for that to work, then you can call it either as Date.strptime (if you only care about the date) or DateTime.strptime (if you want date and time). If you need different formatting, you can call DateTime#strftime (look at strftime.net if you have a hard time with the format strings) on it or use one of the built-in methods like rfc822.

Ruby expression evaluation: whitespace matters?

Imagine it's Jan 19. This will not be hard if you look at this question today.
Date.today
=> Thu, 19 Jan 2012 # as expected
Date.today + 1
=> Fri, 20 Jan 2012 # as expected
Date.today+1
=> Fri, 20 Jan 2012 # as expected
Date.today +1
=> Thu, 19 Jan 2012 # ?!
What am I missing here?
The difference is that:
Date.today + 1
is an addition of two numerical values and
Date.today +1
is a call to the method today with the parameter sg(day of calendar reform) with value +1
The best way to examine this is to monkey patch the original method with debug output included. See this script as example:
require 'date'
class Date
def self.today(sg=ITALY)
puts "ITALY default("+sg.to_s+")" if sg==ITALY
puts sg unless sg==ITALY
jd = civil_to_jd(*(Time.now.to_a[3..5].reverse << sg))
new0(jd_to_ajd(jd, 0, 0), 0, sg)
end
end
puts "- Addition:"
Date.today + 1
puts "- Parameter:"
Date.today +1
This will print the following console output:
- Addition:
ITALY default(2299161)
- Parameter:
1
Yes, whitespace does matter in Ruby, contrary to popular belief. For example, foo bar is not the same as foobar.
In this particular case,
Date.today + 1
is the same as
Date.today().+(1)
Whereas
Date.today +1
is the same as
Date.today(+1)
which is the same as
Date.today(1.+#())

In Ruby on Rails, how do I format a date with the "th" suffix, as in, "Sun Oct 5th"?

I want to display dates in the format: short day of week, short month, day of month without leading zero but including "th", "st", "nd", or "rd" suffix.
For example, the day this question was asked would display "Thu Oct 2nd".
I'm using Ruby 1.8.7, and Time.strftime just doesn't seem to do this. I'd prefer a standard library if one exists.
Use the ordinalize method from 'active_support'.
>> time = Time.new
=> Fri Oct 03 01:24:48 +0100 2008
>> time.strftime("%a %b #{time.day.ordinalize}")
=> "Fri Oct 3rd"
Note, if you are using IRB with Ruby 2.0, you must first run:
require 'active_support/core_ext/integer/inflections'
You can use active_support's ordinalize helper method on numbers.
>> 3.ordinalize
=> "3rd"
>> 2.ordinalize
=> "2nd"
>> 1.ordinalize
=> "1st"
Taking Patrick McKenzie's answer just a bit further, you could create a new file in your config/initializers directory called date_format.rb (or whatever you want) and put this in it:
Time::DATE_FORMATS.merge!(
my_date: lambda { |time| time.strftime("%a, %b #{time.day.ordinalize}") }
)
Then in your view code you can format any date simply by assigning it your new date format:
My Date: <%= h some_date.to_s(:my_date) %>
It's simple, it works, and is easy to build on. Just add more format lines in the date_format.rb file for each of your different date formats. Here is a more fleshed out example.
Time::DATE_FORMATS.merge!(
datetime_military: '%Y-%m-%d %H:%M',
datetime: '%Y-%m-%d %I:%M%P',
time: '%I:%M%P',
time_military: '%H:%M%P',
datetime_short: '%m/%d %I:%M',
due_date: lambda { |time| time.strftime("%a, %b #{time.day.ordinalize}") }
)
>> require 'activesupport'
=> []
>> t = Time.now
=> Thu Oct 02 17:28:37 -0700 2008
>> formatted = "#{t.strftime("%a %b")} #{t.day.ordinalize}"
=> "Thu Oct 2nd"
Although Jonathan Tran did say he was looking for the abbreviated day of the week first followed by the abbreviated month, I think it might be useful for people who end up here to know that Rails has out-of-the-box support for the more commonly usable long month, ordinalized day integer, followed by the year, as in June 1st, 2018.
It can be easily achieved with:
Time.current.to_date.to_s(:long_ordinal)
=> "January 26th, 2019"
Or:
Date.current.to_s(:long_ordinal)
=> "January 26th, 2019"
You can stick to a time instance if you wish as well:
Time.current.to_s(:long_ordinal)
=> "January 26th, 2019 04:21"
You can find more formats and context on how to create a custom one in the Rails API docs.
Create your own %o format.
Initializer
config/initializers/srtftime.rb
module StrftimeOrdinal
def self.included( base )
base.class_eval do
alias_method :old_strftime, :strftime
def strftime( format )
old_strftime format.gsub( "%o", day.ordinalize )
end
end
end
end
[ Time, Date, DateTime ].each{ |c| c.send :include, StrftimeOrdinal }
Usage
Time.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
You can use this with Date and DateTime as well:
DateTime.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
Date.new( 2018, 10, 2 ).strftime( "%a %b %o" )
=> "Tue Oct 2nd"
I like Bartosz's answer, but hey, since this is Rails we're talking about, let's take it one step up in devious. (Edit: Although I was going to just monkeypatch the following method, turns out there is a cleaner way.)
DateTime instances have a to_formatted_s method supplied by ActiveSupport, which takes a single symbol as a parameter and, if that symbol is recognized as a valid predefined format, returns a String with the appropriate formatting.
Those symbols are defined by Time::DATE_FORMATS, which is a hash of symbols to either strings for the standard formatting function... or procs. Bwahaha.
d = DateTime.now #Examples were executed on October 3rd 2008
Time::DATE_FORMATS[:weekday_month_ordinal] =
lambda { |time| time.strftime("%a %b #{time.day.ordinalize}") }
d.to_formatted_s :weekday_month_ordinal #Fri Oct 3rd
But hey, if you can't resist the opportunity to monkeypatch, you could always give that a cleaner interface:
class DateTime
Time::DATE_FORMATS[:weekday_month_ordinal] =
lambda { |time| time.strftime("%a %b #{time.day.ordinalize}") }
def to_my_special_s
to_formatted_s :weekday_month_ordinal
end
end
DateTime.now.to_my_special_s #Fri Oct 3rd

Resources