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 |