From 049cab563b4afa53cdd2528bfd3d24558d1facfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Wed, 7 Oct 2026 17:58:03 +0200 Subject: [PATCH] mi: fix NewInterval inflating durations of 60s or more (#2516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Opus 5.5 Signed-off-by: Jan-Otto Kröpke --- internal/mi/types.go | 16 ++++----- internal/mi/types_test.go | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 internal/mi/types_test.go diff --git a/internal/mi/types.go b/internal/mi/types.go index c48b7ed1..1bddf1a1 100644 --- a/internal/mi/types.go +++ b/internal/mi/types.go @@ -98,17 +98,15 @@ type Interval struct { Padding3 uint32 } +// NewInterval converts a duration into an MI_Interval. MI sums the fields, +// so each field holds only the remainder after the next larger unit. func NewInterval(interval time.Duration) *Interval { - // Convert the duration to a number of microseconds - microseconds := interval.Microseconds() - - // Create a new interval with the microseconds return &Interval{ - Days: uint32(microseconds / (24 * 60 * 60 * 1000000)), - Hours: uint32(microseconds / (60 * 60 * 1000000)), - Minutes: uint32(microseconds / (60 * 1000000)), - Seconds: uint32(microseconds / 1000000), - Microseconds: uint32(microseconds % 1000000), + Days: uint32(interval / (24 * time.Hour)), + Hours: uint32(interval / time.Hour % 24), + Minutes: uint32(interval / time.Minute % 60), + Seconds: uint32(interval / time.Second % 60), + Microseconds: uint32(interval / time.Microsecond % 1000000), } } diff --git a/internal/mi/types_test.go b/internal/mi/types_test.go new file mode 100644 index 00000000..1d812dc8 --- /dev/null +++ b/internal/mi/types_test.go @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build windows + +package mi_test + +import ( + "testing" + "time" + + "github.com/prometheus-community/windows_exporter/internal/mi" + "github.com/stretchr/testify/require" +) + +func TestNewInterval(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + duration time.Duration + expected mi.Interval + }{ + { + name: "zero", + duration: 0, + expected: mi.Interval{}, + }, + { + name: "sub-minute", + duration: 9*time.Second + 500*time.Millisecond, + expected: mi.Interval{Seconds: 9, Microseconds: 500000}, + }, + { + name: "over a minute", + duration: 90 * time.Second, + expected: mi.Interval{Minutes: 1, Seconds: 30}, + }, + { + name: "hours minutes seconds", + duration: 2*time.Hour + 3*time.Minute + 4*time.Second + 500*time.Millisecond, + expected: mi.Interval{Hours: 2, Minutes: 3, Seconds: 4, Microseconds: 500000}, + }, + { + name: "over a day", + duration: 26 * time.Hour, + expected: mi.Interval{Days: 1, Hours: 2}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + require.Equal(t, tc.expected, *mi.NewInterval(tc.duration)) + }) + } +}