Defect #40914 » 40914.patch
| app/models/time_entry.rb | ||
|---|---|---|
| 191 | 191 |
def hours |
| 192 | 192 |
h = read_attribute(:hours) |
| 193 | 193 |
if h.is_a?(Float) |
| 194 |
h.round(2) |
|
| 194 |
# Convert the float value to a rational to avoid rounding errors |
|
| 195 |
# |
|
| 196 |
# Examples: |
|
| 197 |
# 0.38333333333333336 => (23/60) # 23 minutes |
|
| 198 |
# 1.1166666666666667 => (67/60) # 1 hour 7 minutes |
|
| 199 |
(h * 60).round / 60r |
|
| 195 | 200 |
else |
| 196 | 201 |
h |
| 197 | 202 |
end |
| lib/redmine/i18n.rb | ||
|---|---|---|
| 50 | 50 |
end |
| 51 | 51 | |
| 52 | 52 |
def l_hours(hours) |
| 53 |
hours = hours.to_f
|
|
| 53 |
hours = hours.to_r
|
|
| 54 | 54 |
l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => format_hours(hours)) |
| 55 | 55 |
end |
| 56 | 56 | |
| 57 | 57 |
def l_hours_short(hours) |
| 58 |
l(:label_f_hour_short, :value => format_hours(hours.to_f))
|
|
| 58 |
l(:label_f_hour_short, :value => format_hours(hours.to_r))
|
|
| 59 | 59 |
end |
| 60 | 60 | |
| 61 | 61 |
def ll(lang, str, arg=nil) |
| ... | ... | |
| 93 | 93 |
return "" if hours.blank? |
| 94 | 94 | |
| 95 | 95 |
if Setting.timespan_format == 'minutes' |
| 96 |
h = hours.floor |
|
| 97 |
m = ((hours - h) * 60).round |
|
| 96 |
# Ensure the hours value is rational |
|
| 97 |
rational_hours = hours.is_a?(Rational) ? hours : (hours * 60).round / 60r |
|
| 98 |
h = rational_hours.truncate |
|
| 99 |
m = ((rational_hours - h) * 60).round |
|
| 98 | 100 |
"%d:%02d" % [h, m] |
| 99 | 101 |
else |
| 100 | 102 |
number_with_delimiter(sprintf('%.2f', hours.to_f), delimiter: nil)
|
| test/functional/my_controller_test.rb | ||
|---|---|---|
| 48 | 48 |
preferences.save! |
| 49 | 49 |
with_issue = |
| 50 | 50 |
TimeEntry.create!( |
| 51 |
:user => User.find(2), :spent_on => Date.yesterday,
|
|
| 51 |
:user => User.find(2), :spent_on => User.current.today.yesterday,
|
|
| 52 | 52 |
:hours => 2.5, :activity_id => 10, :issue_id => 1 |
| 53 | 53 |
) |
| 54 | 54 |
without_issue = |
| 55 | 55 |
TimeEntry.create!( |
| 56 |
:user => User.find(2), :spent_on => Date.yesterday,
|
|
| 56 |
:user => User.find(2), :spent_on => User.current.today.yesterday,
|
|
| 57 | 57 |
:hours => 3.5, :activity_id => 10, :project_id => 1 |
| 58 | 58 |
) |
| 59 |
# Issues that causes a rounding error |
|
| 60 |
# The sum of 10m, 40m and 10m should be 1h, but it may displayed as 1h1m |
|
| 61 |
# if hours are rounded to 2 decimals |
|
| 62 |
# [0.17, 0.67, 0.17].sum => 1.01 |
|
| 63 |
%w[10m 40m 10m].each do |hours| |
|
| 64 |
TimeEntry.create!( |
|
| 65 |
:user => User.find(2), :spent_on => User.current.today, |
|
| 66 |
:hours => hours, :activity_id => 10, :issue_id => 1 |
|
| 67 |
) |
|
| 68 |
end |
|
| 59 | 69 |
get :page |
| 60 | 70 |
assert_response :success |
| 61 | 71 |
assert_select "tr#time-entry-#{with_issue.id}" do
|
| ... | ... | |
| 65 | 75 |
assert_select "tr#time-entry-#{without_issue.id}" do
|
| 66 | 76 |
assert_select 'td.hours', :text => '3:30' |
| 67 | 77 |
end |
| 78 |
assert_select 'tr:first' do # today's total |
|
| 79 |
assert_select 'td.hours span.hours-int', :text => '1' |
|
| 80 |
assert_select 'td.hours span.hours-dec', :text => ':00' |
|
| 81 |
end |
|
| 68 | 82 |
end |
| 69 | 83 | |
| 70 | 84 |
def test_page_with_assigned_issues_block_and_no_custom_settings |
| test/unit/time_entry_test.rb | ||
|---|---|---|
| 87 | 87 |
"3 hours" => 3.0, |
| 88 | 88 |
"12min" => 0.2, |
| 89 | 89 |
"12 Min" => 0.2, |
| 90 |
"0:23" => Rational(23, 60), # 0.38333333333333336 |
|
| 91 |
"1:07" => Rational(67, 60) # 1.1166666666666667 |
|
| 90 | 92 |
} |
| 91 | 93 |
assertions.each do |k, v| |
| 92 | 94 |
t = TimeEntry.new(:hours => k) |
| 93 |
assert_equal v, t.hours, "Converting #{k} failed:"
|
|
| 95 |
assert v == t.hours && t.hours.is_a?(Rational), "Converting #{k} failed:"
|
|
| 94 | 96 |
end |
| 95 | 97 |
end |
| 96 | 98 | |