How do I represent morning and midnight timings in ruby? - ruby

I need to store and retrieve restaurant timings in simple methods like morning and midnight. What I am doing now is:
def morning
Time.new("6:30 am")
end
def midnight
Time.new("12:00 am")
end
I can compare timings now but this seems to be the wrong way to do it and then I don't know how to read those time values back for a method like:
def open?(time)
time >= morning && time <= midnight
end
What is the right way to do this?

Using the chronic gem I was able to do this:
def opens_at
morning
end
def closes_at
midnight
end
def open?(time)
Chronic.parse(time) >= morning && Chronic.parse(time) < midnight
end
private
def morning
Chronic.parse("6:30 am")
end
def midnight
Chronic.parse("midnight")
end
This works for comparisons

For people like me who do not like having a extra gem in gemfile for smaller things, will go for:
def morning
DateTime.now.beginning_of_day + (6.5).hours
end
def midnight
DateTime.now.end_of_day
# Or DateTime.now.at_midnight + 1
end
there are a lot more options DateTime gives us like:
:at_beginning_of_day, :at_beginning_of_month, :at_beginning_of_quarter, :at_beginning_of_week, :at_beginning_of_year, :at_end_of_month, :at_end_of_quarter, :at_end_of_week, :at_end_of_year, :at_midnight, :awesome_inspect, :awesome_print, :beginning_of_day, :beginning_of_month, :beginning_of_quarter, :beginning_of_week, :beginning_of_year, :wday, :sunday?, :monday?, :tuesday?, :wednesday?, :thursday?, :friday?, :saturday?
Just do( trust me you wont regret it):
DateTime.now.methods.sort

Related

tell if DateTime is within one hour of current time

My goal is to stop the loop if dte is within 1 hour of the current time. Is there a "ruby way" to do this?
#THIS IS AN INFINITE LOOP, DONT RUN THIS
dte=DateTime.strptime("2000-01-01 21:00:00", '%Y-%m-%d %H:%M:%S')
while(dte<(DateTime.now.to_time-1).to_datetime)
#increments dte by one hour, not shown here
end
Pure Ruby way (without including rails's active_support) :
You just need to take off fractions of a day.
one_hour_ago_time = (DateTime.now - (1.0/24))
1.0 = one day
1.0/24 = 1 hour
1.0/(24*60) = 1 minute
1.0/(24*60*60) = 1 second
If you really need to use the DateTime, then you are doing it the right way.
Subtract n hours from a DateTime in Ruby
You can also do:
require 'time'
time = Time.parse('2000-01-01 21:00:00')
while time < Time.now - 3600 do
...
end
You can be more efficient using active_support core extensions.
require 'active_support/core_ext/numeric/time'
while time < Time.now - 1.hour do
...
end
Even better
while time < 1.hour.ago do
...
end
You can also try this:
current_time = DateTime.now.strftime("%Y-%m-%d %H:%M:%S")
last_time = (DateTime.now - (1.0/24)).strftime("%Y-%m-%d %H:%M:%S")
last_time.upto(current_time) {|date| puts date }

Can I periodically recalculate an attribute after a given time interval?

I have a model "User", with a float attribute "Value". I want each User's Value to depreciate 1%(i.e., be multiplied by 0.99) every 24 hours after the User's creation. For example, if a User is created with a Value of 5 at 02:34 on January 1st, then at 02:34 on January 2nd, its value should be recalculated to be 4.95. Then at 02:34 on January 3rd, it should be recalculated to be 4.9005, and so on.
Can this be done?
I'm using a Rails 4.0.10 app, if that matters.
With the delayed_job gem you could do something like this:
# in user.rb
after_create :decrease_value_periodically
private
def decrease_value_periodically
self.value = value * 0.99
self.save!
decrease_value_periodically if value > 0
end
handle_asynchronously :decrease_value_periodically,
:run_at => Proc.new { 24.hours.from_now }
This example would run 24 hours after creation of an user and would reschedule itself over and over again.
Another option might be a daily Cron task that recalculates all user's values at the same time (midnight for example). You could setup a Cron task that runs a Rails method like this:
# command for Cron
rails runner "User.recalculate_all_values"
# in user.rb
def self.recalculate_all_values
User.all.each { |user| user.decrease_value }
end
private
def decrease_value
if value > 0
self.value = value * 0.99
self.save!
end
end
IMO it depends on your priorities: If you want a precise run after 24 hours, choose delayed_jobs. Whereas the Cron solution is easier to maintain, but you might end up with decreasing values to early or to late (Imagine a user is created at 11pm and one hour later the Cron task starts to decrase the value for the first time).

Ruby in 100 Minutes, good_morning method

I have been doing the Ruby in 100 minutes website, and encountered a problem during part 5.
I was asked to create a good_morning method that would print out a greeting such as 'Happy Monday, it's the 130 day of 2013'. Here is my current program:
class PersonalChef
def good_morning
date = Time.new
today = Time("%A")
day_of_year = Time.yday
this_year = Time("%Y")
puts "Happy " + "#{today}" + "! It is the " + "#{day_of_year}" + " day of the year" + "#{this_year}"
return self
end
def make_toast(color)
puts "Making your toast #{color}!"
return self
end
def make_milkshake(flavor)
puts "Don\'t worry boss, my #{flavor} milkshake brings all the boys to the yard!"
return self
end
def make_eggs(quantity)
puts "Making you #{quantity} eggs sir!"
return self
end
end
but when I run the program via irb (load 'personal_chef.rb', frank = PersonalChef.new, frank.make_milkshake('chocolate'), etc, everything works fine until I try to type frank.good_morning into irb, which gives the following error message
"NoMethodError: undefined method Time' for #<PersonalChef:0x00000001b61808>
from personal_chef.rb:4:ingood_morning'
from (irb):3
from /usr/bin/irb:12:in `'
I tried substituting the Date method instead of Time and still encountered the same problem.
Thanks for reading, and hopefully for your forthcoming helpful advice! If there is more information that would be helpful to solve this issue, please let me know.
The line of code
today = Time("%A")
uses Time, the class, as a method call, which explains the error. I see that you are trying to extract the day name and the day of year from a time object, but passing the format string to Time isn't the way to do it.
You are looking for the strftime method.
Example:
>> today = Time.new()
=> 2013-06-19 21:58:34 -0700
>> today.strftime("%A")
=> "Wednesday"
>> today.strftime("%j")
=> "170"
Wednesday, the 170th day of the year.

Does Ruby have an equivalent to TimeSpan in C#?

In C# there is a TimeSpan class. It represents a period of time and is returned from many date manipulation options. You can create one and add or subtract from a date etc.
In Ruby and specifically rails there seems to be lots of date and time classes but nothing that represents a span of time?
Ideally I'd like an object that I could use for outputting formatted dates easily enough using the standard date formatting options.
eg.
ts.to_format("%H%M")
Is there such a class?
Even better would be if I could do something like
ts = end_date - start_date
I am aware that subtracting of two dates results in the number of seconds separating said dates and that I could work it all out from that.
You can do something similar like this:
irb(main):001:0> require 'time' => true
irb(main):002:0> initial = Time.now => Tue Jun 19 08:19:56 -0400 2012
irb(main):003:0> later = Time.now => Tue Jun 19 08:20:05 -0400 2012
irb(main):004:0> span = later - initial => 8.393871
irb(main):005:0>
This just returns a time in seconds which isn't all that pretty to look at, you can use the strftime() function to make it look pretty:
irb(main):010:0> Time.at(span).gmtime.strftime("%H:%M:%S") => "00:00:08"
Something like this? https://github.com/abhidsm/time_diff
require 'time_diff'
time_diff_components = Time.diff(start_date_time, end_date_time)
No, it doesn't. You can just add seconds or use advance method.
end_date - start_date will have Float type
In the end I forked the suggestion in #tokland's answer. Not quite sure how to make it a proper gem but it's currently working for me:
Timespan fork of time_diff
Not yet #toxaq... but I've started something!
https://gist.github.com/thatandyrose/6180560
class TimeSpan
attr_accessor :milliseconds
def self.from_milliseconds(milliseconds)
me = TimeSpan.new
me.milliseconds = milliseconds
return me
end
def self.from_seconds(seconds)
TimeSpan.from_milliseconds(seconds.to_d * 1000)
end
def self.from_minutes(minutes)
TimeSpan.from_milliseconds(minutes.to_d * 60000)
end
def self.from_hours(hours)
TimeSpan.from_milliseconds(hours.to_d * 3600000)
end
def self.from_days(days)
TimeSpan.from_milliseconds(days.to_d * 86400000)
end
def self.from_years(years)
TimeSpan.from_days(years.to_d * 365.242)
end
def self.diff(start_date_time, end_date_time)
TimeSpan.from_seconds(end_date_time - start_date_time)
end
def seconds
self.milliseconds.to_d * 0.001
end
def minutes
self.seconds.to_d * 0.0166667
end
def hours
self.minutes.to_d * 0.0166667
end
def days
self.hours.to_d * 0.0416667
end
def years
self.days.to_d * 0.00273791
end
end

I need to generate a random date, MM/DD/YYYY

I need to generate a random Date.
I do not need time in my calculation.
What I am trying to use is:
def date_rand from = 0.0, to = Time.now
Time.at(from + rand * (to.to_f - from.to_f))
end
This gets me close, but has a bunch other information I do not need. (time, zone, etc.)
If there is a way to get the date without all the other data I would appreciate some help on knowing it.
In Ruby 1.9, including the date library adds a #to_date method to the Time class (as well as a #to_datetime method). Ruby 1.8 has it too, but it's a private method.
require 'date'
def date_rand(from = 0.0, to = Time.now)
Time.at(from + rand * (to.to_f - from.to_f)).to_date
end
In Ruby 1.8, you could do something like this:
def date_rand(from = 0.0, to = Time.now)
time = Time.at(from + rand * (to.to_f - from.to_f))
Date.civil(time.year, time.month, time.day)
end
dmarkov's answer is fine. You can do the same with dates:
require 'date'
def date_rand(from = Date.new(1970,1,1), to = Date.today)
low, high = from.ajd.to_i, to.ajd.to_i
r = rand(high-low+1) + low
Date.jd(r)
end
From a blog post by Obie Fernandez.
class Time
def self.random(years_back=5)
year = Time.now.year - rand(years_back) - 1
month = rand(12) + 1
day = rand(31) + 1
Time.local(year, month, day)
end
end
This allows you to call Time.random. I'm presenting this as an alternate answer to your question and depending on how you're planning on using this, please be careful as monkey patching the standard lib classes isn't usually the best way to go about things if someone else is going to have to debug/support your code one of these days.

Resources