Haml - get current week start and end date - ruby-on-rails

I'm new into HAML, and I have ( I hope easy ) question regarding date/ time. Is it possible to get start and end date of current week in HAML in not very very commplicated way? I want to use those dates for navigation in my calendar.
For month and year is rather easy:
- year = Date.today.year
- month = Date.today.month
- day = Date.today
- monthEnd = year.to_s + "-" + month.to_s + "-" + Integer(Date.new(year, month, -1).strftime("%d")).to_s
- monthStart = Date.new(year, month, 1)
- yearStart = year.to_s + "-01-01";
- yearEnd = year.to_s + "-12-31";
If it is not possible, I will do it in Javascript, but I would like to have this consistent.

In short, these are the methods you're looking for:
Date.today.beginning_of_week
Date.today.end_of_week
If you want to set the beginning of the week to sunday, you can do so in an initializer. Remember to restart your server.
initializers/set_beginning_of_week.rb
Date.today.beginning_of_week = :sunday
Further Improvements
The HAML syntax - is equivalent to ERB <% %>. So this is used for coding Ruby code in your views.
In your views you should not set a long list of variables or perform complex functions. You should try to limit these to if, else, each & other basic operations.
Additionally, for these type of functions you can call helper methods.
I'd setup some basic helpers like:
helpers/application_helper.rb
def current_date
Date.today # or use Time.zone.today as Rob suggested
end
def current_month
current_date.month
end
def current_year
current_date.year
end
Resulting in easy calls
current_year
current_month
current_date
current_date.beginning_of_week
current_date.end_of_week
current_month.beginning_of_month
current_month.end_of_month
current_year.beginning_of_year
current_year.end_of_year
This can then be implemented in any view.
views/foo/show.html.haml
.calendar_header
- current_year
%p Beginning of month is
- current_month.beginning_of_month

Assuming the week starts on Sunday and ends on Saturday, and you want to use Ruby's standard library:
- weekStart = Date.today.prev_day(Date.today.cwday)
- weekEnd = Date.today.next_day(6-Date.today.cwday)
BTW, I would probably take advantage of Time.zone.now instead of Date.today if you're using Rails. Same idea for your other ones. More on that in this blog.
#TheChamp:
Rails has convenience methods for doing just this:
weekStart = Time.zone.today.beginning_of_week
weekEnd = Time.zone.today.end_of_week

This isn't really a HAML issue, it's more a Ruby and/or Rails issue. Have a look at the rails date and time helpers:
http://api.rubyonrails.org/classes/DateAndTime/Calculations.html
They allow you to do:
week_start = Time.now.beginning_of_week

Related

How to know if a date is the previous month or earlier?

I'm looking for some conditions to start billing my clients.
Every time that my client make a contract with me, i initialize a date in the attribute start_billing_at. I'd like to know now, if the start_billing_at attribute has been initialized during the previous month. Hope that my problem is more clear now
Thx for help
EDIT
I'v got to know if my date is between the first and the last day of the previous month
Subtracting two dates and calling to_i on it will give you the difference in days, you can switch on that:
if (Date.today - other_date).to_i < 30
# less than a month
else
# more than a month
end
Of course this doesn't exactly follow the months, but for my use cases it's generally good enough.
Another alternative is:
if date_to_check > Date.today.last_month
# within the last month
end
Or checking for inclusion in the range of last month's dates:
last_month = Date.today.last_month
(last_month.beginning_of_month..last_month.end_of_month).cover?(date_to_check)
%w|2017-07-01 2017-06-01|.map do |d|
(Date.today.month - Date.parse(d).month) % 12 == 1
end
#⇒ [true, false]
Here is my solution, based on Michael kohl solutions
def calcul_bill(date)
if Time.zone.today.last_month.strftime('%b, %Y') == date.strftime('%b, %Y')
#Do actions
else
#Do other actions
end
end
My date format is "Wed, 30 Aug 2017" is this case, so i just compare Month and year
I believe I would go for:
start_billing_at.beginning_of_month == Date.today.last_month.beginning_of_month
With a refinement you could define a method on date that allows you to:
start_billing_at.last_month?
So:
module BillingDateExtensions
refine Date do
def last_month?
self.beginning_of_month == Date.today.last_month.beginning_of_month
end
end
end
... and you can allow this to be mixed in to Date where you need it with:
using BillingDateExtensions

Get most recently occurring Wednesday?

How would I get the most recently occurring Wednesday, using Ruby (and Rails, if there's a pertinent helper method)?
Ultimately need the actual date (5/1/2013).
time = Time.now
days_to_go_back = (time.wday + 4) % 7
last_wed = days_to_go_back.days.ago
This works in Ruby:
require 'date'
def last_wednesday(date)
date - (date.wday - 3) % 7
end
last_wednesday(Date.today)
# => #<Date: 2013-05-01 ((2456414j,0s,0n),+0s,2299161j)>
In Rails there's beginning_of_week:
Date.today.beginning_of_week(:wednesday)
# => Wed, 01 May 2013
If you are okay with using another gem, I recommend Chronic.
With it, you can get last Wednesday by doing:
Chronic.parse('last Wednesday')
The simplest way (to me) is:
require 'date'
date = Date.today
date -= 1 until date.wednesday?
Pretty straightforward with Date:
require 'date'
today = DateTime.now.to_date
last_wednesday = today.downto(today - 6).select { |d| d.wednesday? }
You can even get the last weekday of your choice like this (here without error handling):
def last_weekday(weekday)
today = Time.now.to_date
today.downto(today-6).select do |d|
d.send((weekday.to_s + "?").to_sym)
end
end

Given a timestamp, how to get 1 month ago

I'm trying to build a method where I pass a month and can then query for the previous month dynamically.
total_churn(month)
last_month = month - 1
companies = Company.where("created_at BETWEEN '#{last_month}' AND '#{month}')
return companies.count
end
How do I pass the method 'month' in a way where I can dynamically determine the last month using ruby on rails? Thanks
My suggestion: accept a date rather than a month.
total_churn(date)
month_previous = date - 1.month
companies = Company.where("created_at BETWEEN ? AND ?, '#{month_previous}', '#{date}')
return companies.count
end
Current month:
Time.now.month
Date.today.month
Time or day one month ago:
(Time.now - 1.month).month
(Date.today - 1.month).month
...also equivalent to:
Time.now.month - 1
Date.today.month - 1
Previous month for any given date:
#date - 1.month
I would personally build your method to accept a date rather than just a month number. As long as the created_at field is storing dates, you'll need to give the query two dates for it to run, even if those dates are the 1st.
Rails has some helpful time helpers to create upper and lower bounds for your query. (beginning_of_month and end_of_month in the Time class)
This method is also properly escaped with question marks instead of string interpolation, which is open to SQL injection attacks.
def total_churn(month)
companies = Company.where('created_at BETWEEN ? and ?',(Time.now - 1.month).beginning_of_month,(Time.now - 1.month).end_of_month)
companies.count
end
I would also say that this will only work for the most recent year. If you want to be able to query for earlier data, you may want to either add a year param or simply pass in a date and let it use that in place of Time.now.
# with separate year and month params
def total_churn(month, year)
date = DateTime.parse("#{year}/#{month}/01")
companies = Company.where('created_at BETWEEN ? and ?',(date - 1.month).beginning_of_month,(date - 1.month).end_of_month)
companies.count
end
# with a random date input
def total_churn(date_in_month)
companies = Company.where('created_at BETWEEN ? and ?',(date_in_month - 1.month).beginning_of_month,(date_in_month - 1.month).end_of_month)
companies.count
end
You need at least 2 parameters to accomplish this, one for the month and one for the year. This is needed to establish what month you want.
def total_churn(year, month)
date = Date.new year, month
one_month_ago = date - 1.month
...
However, if you already have a date object, then you can use sscirrus' answer. If the date is in a string, you'd probably want to parse it first (like in your comment)
def total_churn(date_string)
date = Date.parse(date_string)
one_month_ago = date - 1.month
...
>> date_string = '2012-09-01 00:00:00.000000'
>> total_churn date_string

Rails: Formatting a date: "HH:MM" for today, "Yesterday" for yesterday, "Weekday" for other days of last week, "DD.MM." for everything else

I'm sure there is a already a solution for what I need, but I guess I don't know what to search for. Any pointings into the right direction?
I'm thinking of something like Rails' distance_of_time_in_words_to_now.
Thank you.
I believe you could use a helper like this.
def custom_format(date)
if date == Date.today
"Today"
elsif date == Date.yesterday
"Yesterday"
elsif (date > Date.today - 7) && (date < Date.yesterday)
date.strftime("%A")
else
date.strftime("%B %-d")
end
end
Didn't test the code, it's just a pointer to your problem.
Create a file .../config/initializers/time_format.rb and put this code in it:
Time::DATE_FORMATS[:humanized_ago] = ->(time) do
st = Time.now.beginning_of_day
nd = Time.now.end_of_day
case
when time.between?(st + 1.day, nd + 1.day)
"Tomorrow #{time.strftime('%H:%M')}"
when time.between?(st, nd)
"Today #{time.strftime('%H:%M')}"
when time.between?(st - 1.day, nd - 1.day)
"Yesterday #{time.strftime('%H:%M')}"
when time.between?(st - 6.day, nd - 2.day)
time.strftime('%a %H:%M')
else
time.strftime('%y-%b-%d %H:%M')
end
end
On a Rails Time object, call function time.to_s(:humanized_ago). If you don't like the symbol ":humanized_ago", change it it whatever you want in the first line of time_format.rb." If you want other formatting, you can figure it out.
I wrote the comparisons the way I did for a reason. I couldn't find a way to use the Ruby built-in ranges to test Time, and you need to be able to test Time intervals excluding the end point.
Try this
<%= time_ago_in_words(time) %> ago

Rails how to create an array of months based on a range of dates

Example I have:
range = start.to_date..(end.to_date + 1.day)
end and start are dates.
How do I create a month array based on this range?
Example:
I have the dates 23/1/2012 and 15/3/2012
The months are Januar, Februar and Marts.
I want to get a array like ["1/1/2012", "1/2/2012", "1/3/2012"]
and if the range was betweeen 25/6/2012 to the 10/10/2012
the array would be: ["1/6/2012", "1/7/2012", "1/8/2012", "1/9/2012", "1/10/2012"]
require 'date'
date_from = Date.parse('2011-10-14')
date_to = Date.parse('2012-04-30')
date_range = date_from..date_to
date_months = date_range.map {|d| Date.new(d.year, d.month, 1) }.uniq
date_months.map {|d| d.strftime "%d/%m/%Y" }
# => ["01/10/2011", "01/11/2011", "01/12/2011", "01/01/2012",
# "01/02/2012", "01/03/2012", "01/04/2012"]
Rails ActiveSupport core extensions includes a method for Date: beginning_of_month. Your function could be written as follows:
def beginning_of_month_date_list(start, finish)
(start.to_date..finish.to_date).map(&:beginning_of_month).uniq.map(&:to_s)
end
Caveats: this could be written more efficiently, assumes start and finish are in the expected order, but otherwise should give you the months you're looking for. You could also rewrite to pass a format symbol to the #to_s method to get the expected month format.
I was curious about performance here so I tested some variations. Here's a solution better optimized for performance (about 8x faster in my benchmark than the accepted solution). By incrementing by a month at a time we can remove the call to uniq which cuts quite a bit of time.
start_date = 1.year.ago.to_date
end_date = Date.today
dates = []
date = start_date.beginning_of_month
while date <= end_date.beginning_of_month
dates << date.to_date.to_s
date += 1.month
end
dates
#=> ["2019-02-01", "2019-03-01", "2019-04-01", "2019-05-01", "2019-06-01", "2019-07-01", "2019-08-01", "2019-09-01", "2019-10-01", "2019-11-01", "2019-12-01", "2020-01-01", "2020-02-01"]
Benchmark Results:
Comparison:
month increment loop: 17788.3 i/s
accepted solution: 2140.1 i/s - 8.31x slower
gist of the benchmark code
Similar to one of the solutions above using beginning_of_month .. but taking less space (by using Set) and is neater for using inject.
(start_month..end_month).inject(Set.new) { |s, i| s << i.beginning_of_month; s }.to_a

Resources