How to extract only year from created_at column - ruby-on-rails

Querying the database for the created_at value gives the following output:
>> kevin.created_at
=> Sun, 21 Aug 2016 07:46:26 UTC +00:00
How can I extract only the year from this information?
I tried to treat kevin.created_at as a string and see if I could get what I want with:
>> kevin.created_at.split[3].to_i
However I get this message:
NoMethodError: undefined method `split' for Sun, 21 Aug 2016 07:46:26 UTC +00:00:Time
Therefore I tried with:
>> kevin.created_at.to_a
=> [26, 46, 7, 21, 8, 2016, 0, 234, false, "UTC"]
So I may have a solution with:
>> kevin.created_at.to_a[5]
=> 2016
Is there any better or more elegant solution to query Postgresql for this information?

You can use .year function from ruby Time class as:
kevin.created_at.year

In Postgres, the EXTRACT function can get you just the year from a date:
# SELECT EXTRACT('year' FROM created_at) as year FROM USERS LIMIT 1;
year
------
2016
(1 row)
In Ruby, if you already have your record on hand, you can just use the Time#year method:
user.created_at.year # => 2016

Alternately you can use date_part function
SELECT DATE_PART('year' , created_at) AS year FROM USERS
please refer datetime function in postgresql
hope it helps.

Related

Adding year for all records with update_all

I need to update all records in my model by adding another year before the date saved , seek information and an option is with update_all , I really would help an example with dates update_all database: PostgreSQL
An example:
the saved date is this 15/01/16 after executing the action 01/15/17 and so on all records.
or some other option would be very helpful!
If you use the PostgreSQL database you can use the interval from datetime functions:
$ rails console
=> User.last
=#<User:0x00563c0ed6e0c0
id: 7,
name: "foo",
email: "foo#test.ru",
created_at: Tue, 15 Mar 2016 19:54:52 MSK +03:00,
^^^^
.......
=> User.update_all("created_at = created_at + '1 year'::interval")
=> #<User:0x00563c0c9e4f78
id: 7,
name: "foo",
email: "foo#test.ru",
created_at: Wed, 15 Mar 2017 19:54:52 MSK +03:00,
^^^^
........
If MySQL database you can use the DATE_ADD function.
All of this should work if the column have a right type.

Select object based on date range

Tech specs: ruby 2.1.5p273, Rails 4.2.3.
I have an array of Days that I want to loop through to pick the right Exits (model) that fall within a date range.
#exits has :start_date and :end_date
#days is an array of dates like:
=> [Sun, 06 Sep 2015, Sat, 12 Sep 2015, Tue, 15 Sep 2015, Fri, 18 Sep 2015, Sat, 19 Sep 2015, Sun, 20 Sep 2015, Wed, 23 Sep 2015]
I thought something like this would work:
#days.each do |day|
#exits.where(:start_date..:end_date).include?(day)
end
but I get an error:
TypeError: Cannot visit Range
What is the best way to query an object that has a date range (between two fields) by comparing it against a single date? Thanks!
You can use the following:
#days.each do |day|
exits = Exit.where('? BETWEEN start_date AND end_date', day)
# etc.
end
If you don't want to loop over them then you can do:
Event.where("start_date IN (:days) AND end_date IN (:days)", { days: #days })
or
Event.where(start_date: #days, end_date: #days)
Exit.where(day: #exit.start_date..#exits.end_date)
or
Exit.where('day >= ? AND day <= ?', #exit.start_date, #exits.end_date)
Doing SQL queries in a loop is probably a bad idea, it could be refactored to be be one call most likely. And this should happen in the controller not in the view.

One year and one week time scope

I'm trying to notify my customers that their subscription is about to expire. This is how I look for those users to notify them. They will be billed on the date they subscribed + 1.year:
User.where(subscribed_at: 53.weeks.ago.beginning_of_day..53.weeks.ago.beginning_of_day)
My question is will this create issue with leap years? or is there a better way to do this?
Rails provides Time#advance for "precise Time calculations":
Time.now.advance(years: -1, weeks: -1)
#=> 2013-10-08 17:54:36 +0200
Time#all_day returns a whole day's range:
Time.now.advance(years: -1, weeks: -1).all_day
#=> 2013-10-08 00:00:00 +0200..2013-10-08 23:59:59 +0200
I think you should use 1.year.ago since 52.weeks.ago is not equal to a full year (52*7 = 364 days).
The usage of 1.year.ago would be better because it actually changes the year field of the DateTime, nothing else:
1.9.3p489 :005 > 2.year.ago
# => Mon, 15 Oct 2012 11:51:44 EDT -04:00
1.9.3p489 :006 > 5.year.ago
# => Thu, 15 Oct 2009 11:51:47 EDT -04:00
1.9.3p489 :007 > 999.year.ago
# => Sun, 15 Oct 1015 11:51:50 LMT -04:56 # For some reason the TimeZone changed!
In your case, I would use the following logic: NOPE NOPE, I would use #Stefan's answer!
range = (1.year.ago-1.week).beginning_of_day..(1.year.ago-1.week).end_of_day
User.where(subscribed_at: range)

Querying for date range in rails

I have a scope that queries for today's calls. Based off of the scope I use it to count the amount of calls for today.
My dates are stored in UTC but rails converts to my local timezone. What I'm trying to do is find all calls between today at 00:00 and 23:59.
Scope:
scope :today, where("DATE(transfer_date) BETWEEN ? AND ?", Time.zone.now.utc.beginning_of_day, Time.zone.now.utc.end_of_day)
irb output: (The call it catches due to UTC)
irb(main):010:0> Call.last.transfer_date
Call Load (0.9ms) SELECT "calls".* FROM "calls" ORDER BY "calls"."id" DESC LIMIT 1
=> Sun, 07 Oct 2012 19:45:00 CDT -05:00
irb(main):011:0>
irb(main):011:0> Call.last.transfer_date.utc
Call Load (1.3ms) SELECT "calls".* FROM "calls" ORDER BY "calls"."id" DESC LIMIT 1
=> 2012-10-08 00:45:00 UTC
I'm trying to figure out how to query only calls that were between 00:00 and 23:59 for today. So far trying to scope with and without utc, zone, etc doesn't work. It keeps pulling the scope based off of UTC which includes the call from yesterday (yesterday if it's formatted with the local timezone).
How can I query between the two times to get the correct output? I'm kind of lost here.
You can use an exclusive range.
scope :today, where(:transfer_date => Date.today...Date.tomorrow)
I was able to compensate for UTC by rewriting my scope as follows:
scope :today, where("transfer_date BETWEEN ? AND ?", Time.zone.now.beginning_of_day, Time.zone.now.end_of_day)
Maybe this is overkill, but I would suggest using a helper method for getting the time range and then querying the db. Something like
# Gets time range for x number timeunits ago
def time_range(unit, timeunit = nil)
if timeunit == "weeks"
now = Time.zone.now.beginning_of_week
elsif timeunit == "months"
now = Time.zone.now.beginning_of_month
else
now = Time.zone.now.beginning_of_day
end
# Ex: time_range(0, "days") --> Get the time range for today between the beginning of today and the beginning of tommorow - 1 second
now - unit.send(timeunit)..now + 1.send(timeunit) - 1.seconds - unit.send(timeunit)
end
will help you to request time ranges. So when you request something like;
time_range(0, "days")
it will return the time range for 0 days ago (today);
Wed, 07 Sep 2016 00:00:00 UTC +00:00..Wed, 07 Sep 2016 23:59:59 UTC +00:00
And then you can simply query the database object and count everything within the range with;
Calls.where(transfer_date: time_range(unit, timeunit)).count

How do I calculate the next annual occurrence of a date?

Given a Ruby date, does a one liner exist for calculating the next anniversary of that date?
For example, if the date is May 01, 2011 the next anniversary would be May 01, 2012, however if it is December 01, 2011, the next anniversary is December 01, 2011 (as that date hasn't yet arrived).
If you date variable is an instance of Date then you can use >>:
Return a new Date object that is n months later than the current one.
So you could do this:
one_year_later = date >> 12
The same approach applies to DateTime. If all you have is a string, then you can use the parse method:
next_year = Date.parse('May 01, 2011') >> 12
next_year_string = (Date.parse('May 01, 2011') >> 12).to_s
IMHO you're better off using the date libraries (Date and DateTime) as much as possible but you can use the Rails extensions (such as 1.year) if you know that Rails will always be around or you don't mind manually pulling in active_support as needed.
An excellent gem exists for doing this called recurrence. You can checkout the source code or some samples:
https://github.com/fnando/recurrence
http://blog.plataformatec.com.br/tag/recurrence/
For example, if you have a date set you could try:
date = ...
recurrence = Recurrence.new(every: :year, on: [date.month, date.day])
puts recurrence.next
You can do it using Ruby's Date class:
the_date = Date.parse('jan 1, 2011')
(the_date < Date.today) ? the_date + 365 : the_date # => Sun, 01 Jan 2012
the_date = Date.parse('dec 31, 2011')
(the_date < Date.today) ? the_date.next_year : the_date # => Sat, 31 Dec 2011
Or, for convenience use ActiveSupport's Date class extensions:
require 'active_support/core_ext/date/calculations'
the_date = Date.parse('jan 1, 2011')
(the_date < Date.today) ? the_date.next_year : the_date # => Sun, 01 Jan 2012
the_date = Date.parse('dec 31, 2011')
(the_date < Date.today) ? the_date.next_year : the_date # => Sat, 31 Dec 2011
Try this:
def next_anniversary(d)
Date.today > d ? 1.year.from_now(d) : d
end
Pulling in a gem just to do this is overkill.
your_date > Date.today ? your_date : your_date >> 12

Resources