group_by_day and sum works incorrect - ruby-on-rails

I have a code:
total_rate = company.crews
.joins(:assignment)
.where(assignments: { date: week[:from]..week[:to] })
.group_by_day('assignments.date', format: '%Y-%m-%d')
.joins(:workers)
.sum('workers.rate')
in output, we have, as a crew with 2 workers, with rates = 4,5 and it should be 9. It's correct.
If in the last line it’s easy to do .sum ('workers.rate'), then everything is grouped normally, and summed up.
and the output is
{13:00 => 9} 9 is worker.first.rate = 4, worker.last = 5.
But, here, when I try to multiply by a condition more time, it turns out that every worker.reit is multiplied by the time difference,
But you need the amount of worker rate 9 * 13:00,
.sum('workers.rate * (time_arriving - time_leaving)')
not 4 * 13.00 + 5 * 13.00
That’s the trouble ...

Assuming Postgres:
'time' - 'time' will return an interval, multiplying it with an integer will not change the type.
You will need to extract the hours first, then you can multiply it by the rate.
This query shows you how to do get the hours:
select extract(epoch from time_leaving - time_starting) / 3600 from assignments
Note that select extract(hour from time_leaving - time_starting) from assignments will only extract full hours, so 12:00 - 10:15 will return '1'

Related

How to calculate the difference in minutes between two bedtimes (NOT time elapsed)?

I'm trying to get Google Sheets functions to calculate the difference in minutes between two bedtimes and have been spinning my wheels for at least five hours on this. Here are four examples of what I'm trying to accomplish:
BEDTIME 1 BEDTIME 2 DIFF IN MINS
9:00 PM 9:15 PM 15
9:00 PM 10:00 PM 60
11:30 PM 1:00 AM 90
1:00 AM 11:00 PM 120
As you can see, the date doesn't figure at all. I apologize for not offering up code, but I've tried at least half a dozen approaches from other answers and they aren't working -- mainly, I suspect, because most people are looking to find the time elapsed between the two times whereas I'm looking to determine "how much earlier" or "how much later" one bedtime is relative to another (always expressed as a positive value).
Any help would be appreciated. Thanks.
Times are stored as numbers between 0 and 1. If you subtract two times and multiply the result by 24 x 60 = 1440 and format as a number you’ll get number of minutes. I think you’ll need something like:
=MIN(ABS(1440*(B1-A1)), ABS(1440*(B1-A1-1)), ABS(1440*(B1-A1+1)))
The difference between two times is a duration. The question requests that durations be converted to "digital minutes", but that is often not as readable as one would think. 175 minutes is more difficult to understand than 2:55 hours.
There is therefore usually no point in multiplying by 24 * 60 — instead, just use the duration value as is:
=min( abs(B2 - A2), abs(B2 - A2 - 1), abs(B2 - A2 + 1) )
Format the result cell as Format > Number > Duration.
See this answer for an explanation of how date and time values work in spreadsheets.
use arrayformula:
=INDEX(IFERROR(1/(1/TRANSPOSE(QUERY(TRANSPOSE(
IF(A2:A&B2:B="", 0, ABS(1440*(B2:B-A2:A+{-1, 0, 1})))),
"select "&TEXTJOIN(",", 1,
"min(Col"&ROW(A2:A)-ROW(A2)+1&")"))))),, 2)
or:
=INDEX(IFERROR(1/(1/QUERY(SPLIT(FLATTEN(
ROW(A2:A)&"×"&ABS(1440*(B2:B-A2:A+{-1, 0, 1}))), "×"),
"select min(Col2) group by Col1 label min(Col2)''"))))
Try to implement a modulus function in your code. It would basically do something like this:
If x = -5, then y = f(x) = – (-5) = 5, since x is less than zero
If x = 10, then y = f(x) = 10, since x is greater than zero
If x = 0, then y = f(x) = 0, since x is equal to zero
Therefore calculating how much time passed without negative values.

How to compare two hours in erlang?

What is the way to compare two hours in erlang? The case is the following,
I have a time limit to perform a transaction reversal eg: 30 minutes, if the
time of the transaction to be reversed was more than 30 minutes ago I
should not allow the transaction to be reversed. But I need to be able to
compare the hours (from the transaction i want to revert and now hour)
and I can not find any erlang function that compares hours, can someone help me?
LocalTime = calendar:local_time()
{{2017,11,7},{9,43,32}}
TransactionToRevertTime = {{2017,11,7},{9,23,36}}
You can use calendar:datetime_to_gregorian_seconds/1 to convert the two datetimes to integers, subtract them, and check if their difference is greater than 30 * 60:
1> A = {{2017,11,7},{9,43,32}}.
{{2017,11,7},{9,43,32}}
2> B = {{2017,11,7},{9,23,36}}.
{{2017,11,7},{9,23,36}}
3> C = {{2017,11,7},{9,0,36}}.
{{2017,11,7},{9,0,36}}
4> A1 = calendar:datetime_to_gregorian_seconds(A).
63677267012
5> B1 = calendar:datetime_to_gregorian_seconds(B).
63677265816
6> C1 = calendar:datetime_to_gregorian_seconds(C).
63677264436
7> A1 - B1 > 30 * 60.
false
8> A1 - C1 > 30 * 60.
true
If A is now, B is less than 30 minutes ago while C is more than 30 minutes ago.
From the Erlang Docs:
"Take time stamps with erlang:monotonic_time/0 and calculate the time difference using ordinary subtraction"
Take a difference between two calls to the monotonic_time(second) and if the difference it's bigger than 1800, more than 30 minutes have elapsed.

Jenkins job scheduler

How can I set Jenkins to run a job at a particular time?
Like if I'd like to set it to 8:30am every weekday and this is what I could do
H 7 * * 1-5
this randomly picks up 7:35am as running time.
H is a pseudo-random number, based on the hash of the jobname.
When you configured:
H 7
you are telling it:
At 7 o'clock, at random minute, but that same minute very time
Here is the help directly from Jenkins (just click the ? icon)
To allow periodically scheduled tasks to produce even load on the system, the symbol H (for “hash”) should be used wherever possible. For example, using 0 0 * * * for a dozen daily jobs will cause a large spike at midnight. In contrast, using H H * * * would still execute each job once a day, but not all at the same time, better using limited resources.
The H symbol can be used with a range. For example, H H(0-7) * * * means some time between 12:00 AM (midnight) to 7:59 AM. You can also use step intervals with H, with or without ranges.
The H symbol can be thought of as a random value over a range, but it actually is a hash of the job name, not a random function, so that the value remains stable for any given project
If you want it at 8:30 every weekday, then you must specify just that:
30 8 * * 1-5
Take a look at http://www.cronmaker.com/
0 30 8 ? * MON,TUE,WED,THU,FRI *
30 8 * * 1-5
This would start at 8:30am Mon-Fri.
0 and 7 are Sundays.
Not sure what the H does but I am assuming it takes the lower case hex of h and applies 68 which is 35 in decimal... lol. Don't do that.
Following this format:
Minute Hour DayOfMonth DayOfWeek Day
It picks that time because you told it that it can, as imagine you already know:
minute, hour, day of month, month, day of week.
Now you have user H which allows Jenkins to pick at random. So you have told it to run between 7-8 every week day.
Change this to:
30 8 * * 1-5
Hope this helps!

Convert duration to hours:minutes:seconds (or similar) in Rails 3 or Ruby

I have a feeling there is a simple/built-in way to do this but I can't find it.
I have a duration (in seconds) in an integer and I want to display it in a friendly format.
e.g. 3600 would be displayed as "01:00:00" or "1 hour" or something.
I can do it with time_ago_in_words(Time.zone.now+3600) but that feels like a bit of a hack, there is no reason to add/subtract from the current time just to format this value. Is there a duration_in_words() or something?
Thanks
Summing up:
assuming that total_seconds = 3600
Option 1:
distance_of_time_in_words(total_seconds) #=> "about 1 hour"
Option 2:
Time.at(total_seconds).utc.strftime("%H:%M:%S") #=> "01:00:00"
Note: it overflows, eg. for total_seconds = 25.hours.to_i it'll return "01:00:00" also
Option 3:
seconds = total_seconds % 60
minutes = (total_seconds / 60) % 60
hours = total_seconds / (60 * 60)
format("%02d:%02d:%02d", hours, minutes, seconds) #=> "01:00:00"
Option 4:
ActiveSupport::Duration.build(total_seconds).inspect #=> "1 hour"
# OR
parts = ActiveSupport::Duration.build(total_seconds).parts
"%02d:%02d:%02d" % [parts.fetch(:hours, 0),
parts.fetch(:minutes, 0),
parts.fetch(:seconds, 0)] #=> "01:00:00"
See: http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
distance_of_time_in_words(3600)
=> "about 1 hour"
Ruby's string % operator is too unappreciated and oft forgotten.
"%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]
Given t is a duration in seconds, this emits a zero-padded colon-separated string including days. Example:
t = 123456
"%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]
=> "01:10:17:36"
Lovely.
I guess you could do also something like:
(Time.mktime(0)+3600).strftime("%H:%M:%S")
To format it as you wish.
BTW, originally I thought of using Time.at() but seems that EPOCH time on my Ubuntu is Thu Jan 01 01:00:00 +0100 1970 and not 00:00:00 hours as I expected, and therefore if I do:
Time.at(3600).strftime("%H:%M:%S")
Gives me 1 hour more than wanted.
I use this to show time durations in my Rails Project:
Add a custom method to the Integer class. You can create a new file called pretty_duration.rb in the initializers folder:
class Integer
def pretty_duration
parse_string =
if self < 3600
'%M:%S'
else
'%H:%M:%S'
end
Time.at(self).utc.strftime(parse_string)
end
end
Call seconds.pretty_duration anywhere in your project:
275.pretty_duration # => "04:35"
9823.pretty_duration # => "02:43:43"
This answer builds up on Lev Lukomsky's Code
This one uses the obscure divmod method to divide and modulo at the same time, so it handles Float seconds properly:
def duration(seconds)
minutes, seconds = seconds.divmod(60)
hours, minutes = minutes.divmod(60)
days, hours = hours.divmod(24)
"#{days.to_s.rjust(3)}d #{hours.to_s.rjust(2)}h #{minutes.to_s.rjust(2)}m #{seconds}s"
end
ActiveSupport::Duration.build + inspect gives you valid results
>> ActiveSupport::Duration.build(125557).inspect
=> "1 day, 10 hours, 52 minutes, and 37 seconds"
Using Time.utc.strftime works only for values when total number of hours is less then 24:
2.2.2 :004 > Time.at(60 * 60).utc.strftime('%H h %M m')
=> "01 h 00 m"
For greater values it returns incorrect results:
2.2.2 :006 > Time.at(60 * 60 * 24).utc.strftime('%H h %M m')
=> "00 h 00 m"
I suggest using the simplest method I found for this problem:
def formatted_duration total_seconds
hours = total_seconds / (60 * 60)
minutes = (total_seconds / 60) % 60
seconds = total_seconds % 60
"#{ hours } h #{ minutes } m #{ seconds } s"
end
You can always adjust returned value to your needs.
Be careful with the duration longer than one day.
(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
An answer inspired from Lev Lukomsky's one taking advantage of ActiveSupport::Duration, and handling milliseconds (useful to benchmark code)
# duration in ms modulus number of ms in one second
milliseconds = duration.in_milliseconds % 1.second.in_milliseconds
# duration in seconds modulus number of seconds in one minute
seconds = (duration / 1.second) % (1.minute / 1.second)
# duration in minutes modulus number of minutes in one hour
minutes = (duration / 1.minute) % (1.hour / 1.minute)
# duration in hours modulus number of hours in one day
hours = (duration / 1.hour) % (1.day / 1.hour)
format("%02d:%02d:%02d:%03d", hours, minutes, seconds, milliseconds) #=> "12:05:00:001"
Of course you can extend this easily with days, months, years, etc using related ActiveSupport methods and repeating the same structure.
Keep in mind that for too long durations, this may be inaccurate since the duration of 1 month is not fixed in number of days, and I'm not sure how AS:Duration deals with that.
Shout out to #joshuapinter who gave the best answer (in the form of a comment).
Use the drop-in replacement dotiw gem to gain more control over the accuracy of the output to suit different needs:
https://github.com/radar/distance_of_time_in_words
Sample view code:
%label
Logoff after:
- expire_in = distance_of_time_in_words(Time.now, Time.now + user.custom_timeout.minutes, :only => [:minutes, :hours, :days])
= expire_in
Resulting in something like this:
Logoff after: 1 day, 13 hours, and 20 minutes
Just to throw in my 2 cents:
Time.at(i).utc.strftime((i < 3600) ? '%-M minutes and %-S seconds' : '%-H hours, %-M minutes, and %-S seconds')
Built off of Xiao Bin's answer.
Here a simple solution using divmod and map:
hours = 3.5456
value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
=> "03:32"

How do I get the number of seconds between two DateTimes in Ruby on Rails

I've got code that does time tracking for employees. It creates a counter to show the employee how long they have been clocked in for.
This is the current code:
start_time = Time.parse(self.settings.first_clock_in)
total_seconds = Time.now - start_time
hours = (total_seconds/ 3600).to_i
minutes = ((total_seconds % 3600) / 60).to_i
seconds = ((total_seconds % 3600) % 60).to_i
This works fine. But because Time is limited to the range of 1970 - 2038 we are trying to replace all Time uses with DateTimes. I can't figure out how to get the number of seconds between two DateTimes. Subtracting them yields a Rational which I don't know how to interpret, whereas subtracting Times yields the difference in seconds.
NOTE: Since Ruby 1.9.2, the hard limit of Time is removed. However, Time is optimized for values between 1823-11-12 and 2116-02-20.
Subtracting two DateTimes returns the elapsed time in days, so you could just do:
elapsed_seconds = ((end_time - start_time) * 24 * 60 * 60).to_i
Or, more readably:
diff = datetime_1 - datetime_2
diff * 1.days # => difference in seconds; requires Ruby on Rails
Note, what you or some other searchers might really be looking for is this:
diff = datetime_1 - datetime_2
Date.day_fraction_to_time(diff) # => [h, m, s, frac_s]
You can convert them to floats with to_f, though this will incur the usual loss of precision associated with floats. If you're just casting to an integer for whole seconds it shouldn't be big enough to be a worry.
The results are in seconds:
>> end_time.to_f - start_time.to_f
=> 7.39954495429993
>> (end_time.to_f - start_time.to_f).to_i
=> 7
Otherwise, you could look at using to_formatted_s on the DateTime object and seeing if you can coax the output into something the Decimal class will accept, or just formatting it as plain Unix time as a string and calling to_i on that.
Others incorrectly rely on fractions or helper functions. It's much simpler than that. DateTime itself is integer underneath. Here's the Ruby way:
stop.to_i - start.to_i
Example:
start = Time.now
=> 2016-06-21 14:55:36 -0700
stop = start + 5.seconds
=> 2016-06-21 14:55:41 -0700
stop.to_i - start.to_i
=> 5
I am using ruby-2.1.4 and for me the following worked
Time.now - Time.new(2014,11,05,17,30,0)
gave me the time difference in seconds
reference: ruby doc
there's a method made for that:
Time.now.minus_with_coercion(10.seconds.ago)
equals 10.
Source: http://apidock.com/rails/Time/minus_with_coercion
Hope I helped.
Define a Ruby function like this,
def time_diff(start_time, end_time)
seconds_diff = (start_time - end_time).to_i.abs
days = seconds_diff / 86400
seconds_diff -= days * 86400
hours = seconds_diff / 3600
seconds_diff -= hours * 3600
minutes = seconds_diff / 60
seconds_diff -= minutes * 60
seconds = seconds_diff
"#{days} Days #{hours} Hrs #{minutes} Min #{seconds} Sec"
end
And Call this function,
time_diff(Time.now, Time.now-4.days-2.hours-1.minutes-53.seconds)

Resources