Calculate closing date with openedon date - ruby-on-rails

I'm doing reccuring deposit calculations, When open reccuring deposit account on that time itself i have set opening date as today's date using before_save. I dono how to calculate account maturity date. Because, in my reccuring deposit i have many fields in my dropdown like 6months, 1year, 2years ... upto 5 years. According to that openedon date i have to close that account or renew it on that maturity date itself.
i have use the below code for openedon date
before_save :set_openedon
def set_openedon
self.openedon = Date.today
end
for example:
for first rd account openedon date is 13/05/2013 and maturity date must be 13/05/2014 for one year.
for second rd account openedon date is 15/09/2012 and maturity date must be 15/09/2014 for two years.
like that i have to calculate maturity date for all the accounts. But i dono how to do.
Kindly help me.
-Thanks in advance.

Rails has quite easy-to-use date operators:
Date.parse('13/05/2013') + 1.year
# => Tue, 13 May 2014
Date.parse('15/09/2012') + 2.years
# => Mon, 15 Sep 2014
Same for 6.months, etc.

Something alike:
def calculate_dates
self.openedon = Date.today
case self.noy
when '1'
self.md = self.openedon+1.year
when '2'
self.md = self.openedon+2.years
when '3'
self.md = self.openedon+3.years
when '4'
self.md = self.openedon+4.years
when '5'
self.md = self.openedon+5.years
else
self.md = self.openedon+6.months
end
end

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

How do I specify a time given a date object?

I am trying to create an object which has both a date field and a time field. I'd like the time field to be generated based on the date in the date field. For example:
SampleObject.new(
date = Date.today #Sat, 28 Dec 2013
time = Date.today.at(5:00pm EST) #Sat, 28 Dec 2013 2013-12-28 17:00:00 -0500 Not valid ruby syntax)
)
How can I make time = line work as intended?
I'd do something like:
DateTime.parse Date.today.to_s + ' 5pm'
#<DateTime: 2013-12-28T17:00:00+00:00 ((2456655j,61200s,0n),+0s,2299161j)>
I am interpreting this question as "a way to set the time of the day on a given date to a given time".
One way you could do it is as follows:
def set_date_and_time(date = Date.today, time = Time.now)
this_day = [date.year, date.month, date.day]
this_time = [time.hour, time.min, time.sec]
Time.new(*this_day, *this_time)
end
Hope this helps.

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

Best way to create random DateTime in Rails

What is the best way to generate a random DateTime in Ruby/Rails? Trying to create a nice seeds.rb file. Going to use it like so:
Foo.create(name: Faker::Lorem.words, description: Faker::Lorem.sentence, start_date: Random.date)
Here is how to create a date in the last 10 years:
rand(10.years).ago
You can also get a date in the future:
rand(10.years).from_now
Update – Rails 4.1+
Rails 4.1 has deprecated the implicit conversion from Numeric => seconds when you call .ago, which the above code depends on. See Rails PR #12389 for more information about this. To avoid a deprecation warning in Rails 4.1 you need to do an explicit conversion to seconds, like so:
rand(10.years).seconds.ago
Here are set of methods for generating a random integer, amount, time/datetime within a range.
def rand_int(from, to)
rand_in_range(from, to).to_i
end
def rand_price(from, to)
rand_in_range(from, to).round(2)
end
def rand_time(from, to=Time.now)
Time.at(rand_in_range(from.to_f, to.to_f))
end
def rand_in_range(from, to)
rand * (to - from) + from
end
Now you can make the following calls.
rand_int(60, 75)
# => 61
rand_price(10, 100)
# => 43.84
rand_time(2.days.ago)
# => Mon Mar 08 21:11:56 -0800 2010
I prefer use (1..500).to_a.rand.days.ago
You are using Faker; why not use one of the methods provided by Faker::Date?
# Random date between dates
# Keyword arguments: from, to
Faker::Date.between(from: 2.days.ago, to: Date.today) #=> "Wed, 24 Sep 2014"
# Random date between dates except for certain date
# Keyword arguments: from, to, excepted
Faker::Date.between_except(from: 1.year.ago, to: 1.year.from_now, excepted: Date.today) #=> "Wed, 24 Sep 2014"
# Random date in the future (up to maximum of N days)
# Keyword arguments: days
Faker::Date.forward(days: 23) # => "Fri, 03 Oct 2014"
# Random date in the past (up to maximum of N days)
# Keyword arguments: days
Faker::Date.backward(days: 14) #=> "Fri, 19 Sep 2014"
# Random birthday date (maximum age between 18 and 65)
# Keyword arguments: min_age, max_age
Faker::Date.birthday(min_age: 18, max_age: 65) #=> "Mar, 28 Mar 1986"
# Random date in current year
Faker::Date.in_date_period #=> #<Date: 2019-09-01>
# Random date for range of year 2018 and month 2
# Keyword arguments: year, month
Faker::Date.in_date_period(year: 2018, month: 2) #=> #<Date: 2018-02-26>
# Random date for range of current year and month 2
# Keyword arguments: month
Faker::Date.in_date_period(month: 2) #=> #<Date: 2019-02-26>
current Faker version: 2.11.0
Here is how to create a date in this month:
day = 1.times.map{ 0+Random.rand(30) }.join.to_i
rand(day.days).ago
Another approach using DateTime's advance
def rand_date
# return a random date within 100 days of today in both past and future directions.
n = rand(-100..100)
Date.today.advance(days: n)
end
This is what I use:
# get random DateTime in last 3 weeks
DateTime.now - (rand * 21)
other way:
(10..20).to_a.sample.years.ago
I haven't tried this myself but you could create a random integer between two dates using the number of seconds since epoch. For example, to get a random date for the last week.
end = Time.now
start = (end - 1.week).to_i
random_date = Time.at(rand(end.to_i - start)) + start
Of course you end up with a Time object instead of a DateTime but I'm sure you can covert from here.
As I already mentioned in another question I think the following code-snippet is more consisent regarding the data-types of the parameters and of the value to be returned. Stackoverflow: How to generate a random date in Ruby?
Anyway this uses the rand() method's internal logic what is the random Date or random Time within a span. Maybe someone has a more efficient way to set the default-parameter to (Time.now.to_date) of the method random_date, so it doesn't need this typecasting.
def random_time from = Time.at(0.0), to = Time.now
rand(from..to)
end
# works quite similar to date :)
def random_date from = Date.new(1970), to = Time.now.to_date
rand(from..to)
end
Edit: this code won't work before ruby v1.9.3
You can pass Time Range to rand
rand(10.weeks.ago..1.day.ago)
Output Example:
=> Fri, 10 Jan 2020 10:28:52 WIB +07:00
Without user faker (cause I'm using an old version of ruby):
Time.zone.now - rand(16..35.years) - rand(1..31).days
My 'ish' gem provides a nice way of handling this:
# plus/minus 5 min of input date
Time.now.ish
# override that time range like this
Time.now.ish(:offset => 1.year)
https://github.com/spilliton/ish

How to calculate how many years passed since a given date in Ruby?

This question was here for other languages, so let here be one for Ruby.
How do I calculate number of complete years that have passed from a given date? As you probably have guessed, that's to calculate person's age automatically. The closest one is distance_of_time_in_words Rails helper, so the following template
Jack is <%= distance_of_time_in_words (Time.now, Time.local(1950,03,22)) %> old.
yields
Jack is over 59 years old.
But I need more precise function that yields just number. Is there one?
If there exists some kind of Ruby on Rails helper function for this, this is OK, although pure Ruby solution would be better.
Edit: the gist of the question is that a non-approximate solution is needed. At the 2nd of March Jack should be 59 years old and the next day he should be 60 years old. Leap years and such should be taken into account.
Do you want age as people typically understand it, or are you looking for a precise measure of time elapsed? If the former, there is no need to worry about leap years and other complications. You simply need to compute a difference in years and reduce it if the person has not had a birthday yet this year. If the latter, you can convert seconds elapsed into years, as other answers have suggested.
def age_in_completed_years (bd, d)
# Difference in years, less one if you have not had a birthday this year.
a = d.year - bd.year
a = a - 1 if (
bd.month > d.month or
(bd.month >= d.month and bd.day > d.day)
)
a
end
birthdate = Date.new(2000, 12, 15)
today = Date.new(2009, 12, 14)
puts age_in_completed_years(birthdate, today)
require 'date'
def years_since(dt)
delta = (Date.today - Date.parse(dt)) / 365
delta.to_i
end
I have a gem/plugin called dotiw that has a distance_of_time_in_words_hash that will return a hash like: { :years => 59, :months => 11, :days => 27 }. From that you could work out if it's near a certain limit.
An approach that handles leap years
Whenever you're calculating elapsed years since a date, you have to decide how to handle leap year. Here is my approach, which I think is very readable, and is able to take leap years in stride without using any "special case" logic.
def years_completed_since(start_date, end_date)
if end_date < start_date
raise ArgumentError.new(
"End date supplied (#{end_date}) is before start date (#{start_date})"
)
end
years_completed = end_date.year - start_date.year
unless reached_anniversary_in_year_of(start_date, end_date)
years_completed -= 1
end
years_completed
end
# No special logic required for leap day; its anniversary in a non-leap
# year is considered to have been reached on March 1.
def reached_anniversary_in_year_of(original_date, new_date)
if new_date.month == original_date.month
new_date.day >= original_date.day
else
new_date.month > original_date.month
end
end
I came up with the following, based on a similar reasoning as #FMc
First, compute the diff between today's year and birthday's year. Then, sum it to birthday and check the resulting date: if it's greater than today, decrease diff by 1.
To be used in Rails apps as it relies on ActiveSupport's years method
def age(birthday, today)
diff = today.year - birthday.year
(birthday + diff.years > today ) ? (diff - 1) : diff
end
Same idea as FM but with a simplified if statement. Obviously, you could add a second argument instead of using current time.
def age(birthdate)
now = DateTime.now
age = now.year - birthdate.year
age -= 1 if(now.yday < birthdate.yday)
age
end
withing http://github.com/radar/dotiw
Jack is <%= distance_of_time_in_words (Time.now, Time.local(1950,03,22)) %> old.
produce
Jack is 60 years old
you can use the ruby gem adroit-age
It works for leap years also..
age = AdroitAge.find_age("23/01/1990")
Update
require 'adroit-age'
dob = Date.new(1990,1,23)
or
dob = "23/01/1990".to_date
age = dob.find_age
#=> 23
I think this will always work, even for someone with a birthday near a leap day:
require 'date'
def calculate_age(start_date, end_date)
end_date.year - start_date.year - ((end_date.month > start_date.month || (end_date.month == start_date.month && end_date.day >= start_date.day)) ? 0 : 1)
end
puts calculate_age( Date.strptime('03/02/1968', '%m/%d/%Y'), Date.strptime('03/02/2010', '%m/%d/%Y'))
The calculated age with this method in the example call above is 42, which is correct despite 1968 being a leap year and the birthday being near a leap day.
Plus, this way there is no need to create a local variable.
How about this:
def age_in_years(date)
# Difference in years, less one if you have not had a birthday this year.
today = Date.today
age = today.year - date.year
age = age - 1 if [date.day, date.month, today.year].join('/').to_date > Date.today
end
d2.year - d1.year - (d2.month > d1.month || (d2.month == d1.month && d2.day >= d1.day) ? 0 : 1)
How about something like:
def years_diff(from_time,to_time)
(((to_time - from_time).abs)/ (365 * 24 * 60 * 60)).to_i
end
years_diff(Time.now,Time.local(1950,03,22)) #=> 59
years_diff(Time.now,Time.local(2009,03,22)) #=> 0
years_diff(Time.now,Time.local(2008,03,22)) #=> 1
To calculate number of Years and Months between two dates, I used this function
def calculate_year_month(from_date, to_date)
num_days = (from_date - to_date).to_i
num_months = (num_days / 30)
num_years = (num_months / 12)
num_months = (num_months % 12)
return num_years.to_s + " year(s) and " + num_months.to_s + " month(s)"
end

Resources