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.
Related
timestr = '15h 37m 5s'
I want to get the hours minutes and seconds from the above string and add it to current time.
def next_run
timestr = '15h 37m 5s'
timearr = timestr.split(' ').map { |t| t.to_i }
case timearr.count
when 3
next_ = (timearr[0] * 3600) + (timearr[1] * 60) + timearr[2]
when 2
next_ = (timearr[1] * 60) + timearr[2]
when 1
next_ = timearr[2]
else
raise 'Unknown length for timestr'
end
time_to_run_in_secs = next_
end
Now I get the total seconds. I want to make it into hours minutes and seconds, then add it to current time to get next run time. Is there any easy way to do this?
The following method can be used to compute the the numbers of seconds from the string.
def seconds(str)
3600 * str[/\d+h/].to_i + 60 * str[/\d+m/].to_i + str[/\d+s/].to_i
end
Note nil.to_i #=>0. A slight variant would be to write 3600 * (str[/\d+h/] || 0) +....
Then
Time.now + seconds(str)
Examples of possible values of str are as follows: ”3h 26m 41s”, ”3h 26m”, ”3h 41s”, ”41s 3h”, ”3h”,”41s” and ””.
One could instead write the operative line of the method as follows.
%w| s m h |.each_with_index.sum { |s,i| 60**i * str[/\d+#{s}/].to_i }
Though DRYer, I find that less readable.
DateTime#+ accepts Rational instance as days to be added. All you need as you have seconds would be to convert it to a number of days and plain add to the current timestamp:
DateTime.now.tap do |dt|
break [dt, dt + Rational(100, 3600 * 24) ]
end
#⇒ [
# [0] #<DateTime: 2018-05-27T11:13:00+02:00 ((2458266j,33180s,662475814n),+7200s,2299161j)>,
# [1] #<DateTime: 2018-05-27T11:14:40+02:00 ((2458266j,33280s,662475814n),+7200s,2299161j)>
# ]
you can convert your string into seconds from this method
def seconds(str)
(3600 * str[/\d+(h|H)/].to_i) + (60 * str[/\d+(m|M)/].to_i) + (str[/\d+(s|S)/].to_i)
end
and then convert current time to seconds using method
next_run_time = Time.now.to_i + seconds(<Your Time String>)
now get next run time using
Time.at(next_run_time)
get desired format of time by using strftime method, in your case
Time.at(next_run_time).strftime("%Hh %Mm %Ss")
If you don't need to parse the duration of time, and just want to define it in your code, use ActiveSupport::Duration for readability . (add the gem to your Gemfile, and read the guide on how to use it)
Then you can use it like this:
require 'active_support'
require 'active_support/core_ext/integer'
DURATION = 15.hours + 37.minutes + 5.seconds
# use DURATION.seconds or DURATION.to_i to get the seconds
def next_run
Time.now + DURATION
end
See the API documentation of ActiveSupport::Duration
If you need to define the next run by a user input, it's a good practice to use ISO 8601 to define a duration of time: https://en.wikipedia.org/wiki/ISO_8601#Durations
ISO 8601 durations are parseable:
ActiveSupport::Duration.parse('PT15H37M5S') # => 15 hours, 37 minutes, and 5 seconds (duration)
Firstly instead of spliting the string, you can use Time#parse method. Make sure you have required the library as well.
require 'time'
=> true
Time.parse('15h 37m 5s')
=> 2018-05-27 15:37:05 +0300
This returns a new object of class Time and it has some really useful methods for you - #sec, #min, #hour.
time = Time.parse('15h 37m 5s')
time.sec #=> 5
time.min #=> 37
time.hour #=> 15
Adding adding one Time object to another is pretty straightforward since you can do it only by seconds. A simple solution for the current problem would be:
def next_run
time = Time.parse('15h 37m 5s')
seconds_to_add = time.hour * 3600 + time.min * 60 + time.sec
Time.now + seconds_to_add
end
Hopefully this will answer your question! :)
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 }
I have a method which takes ID of project and find all payments, do the math and the output is percentage of success (target amount vs collected)
def projectCollectedMoneyPerc(id)
#project= Project.find(id)
#collected = Payment.where('project_id = ? and confirmed = true', #project.id)
#col = #collected.sum(:amount)
#perc = ((#col.to_f / #project.amount) * 100).round(0)
end
now I need to find projects which have most % success. My idea was to call this method by sort_by but I have no idea how to put ID from collection to this sort
my collection is simple
#projects=Project.where('enabled = true and enddate > ?', Time.now)
thanks
I would define a method like to following in your model:
# in app/models/project.rb
has_many :payments
def collected_money_percentage
sum = payments.where(confirmed: true).sum(:amount)
(100.0 * sum / amount ).round
end
Then you cound use that method like this:
Project.where('enabled = true and enddate > ?', Time.now)
.sort_by(&:collected_money_percentage)
Please note that this first loads all matching record and then calculations the percentage in memory. It would probably be faster to calculate this values in your database:
Project.joins(:payments)
.where('enabled = true and enddate > ?', Time.now)
.group('projects.id')
.order('SUM(payments.amount) / projects.amount')
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
Is there a gem or something to parse strings like "4h 30m" "1d 4h" -- sort of like the estimates in JIRA or task planners, maybe, with internationalization?
Posting a 2nd answer, as chronic (which my original answer suggested) doesn't give you timespans but timestamps.
Here's my go on a parser.
class TimeParser
TOKENS = {
"m" => (60),
"h" => (60 * 60),
"d" => (60 * 60 * 24)
}
attr_reader :time
def initialize(input)
#input = input
#time = 0
parse
end
def parse
#input.scan(/(\d+)(\w)/).each do |amount, measure|
#time += amount.to_i * TOKENS[measure]
end
end
end
The strategy is fairly simple. Split "5h" into ["5", "h"], define how many seconds "h" represents (TOKENS), and add that amount to #time.
TimeParser.new("1m").time
# => 60
TimeParser.new("1m wtf lol").time
# => 60
TimeParser.new("4h 30m").time
# => 16200
TimeParser.new("1d 4h").time
# => 100800
It shouldn't be too hard making it handle "1.5h" either, seeing the codebase is as simple as it is.
chronic_duration does this.
You can use chronic. It can parse pretty much everything you trhow at it, including "yesterday", "last week" etc.
Update: As the OP points out in the comment, Chronic is for dates, not timespans. See my other answer.
I wrote this method that does it pretty well
def parse_duration(dur)
duration = 0
number_tokens = dur.gsub(/[a-z]/i,"").split
times = dur.gsub(/[\.0-9]/,"").split
if number_tokens.size != times.size
raise "unrecognised duration!"
else
dur_tokens = number_tokens.zip(times)
for d in dur_tokens
number_part = d[0].to_f
time_part = d[1]
case time_part.downcase
when "h","hour","hours"
duration += number_part.hours
when "m","minute","minutes","min","mins"
duration += number_part.minutes
when "d","day","days"
duration += number_part.days
when "w","week","weeks"
duration += number_part.weeks
when "month", "months"
duration += number_part.months
when "y", "year", "years"
duration += number_part.years
else
raise "unrecognised duration!"
end
end
end
duration
end
Parse into what though?
This will parse into a Hash:
"4h 30m".split(/\s/).each{|i| h[i.gsub(/\d+/,"")] = i.gsub(/\w/,"")}
Sorry. not familiar with JIRA....