cycle

package
v1.7.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 1 Imported by: 0

Documentation

Overview

Package cycle computes the billing cycle a node's traffic belongs to.

It exists on the panel side because the panel did not have it. The agent has always computed when its own month resets (`agent/utils.GetLastResetDate`, used by netstatic), but it reports a *cumulative* total without saying which window that total covers — so the panel could compare usage against a limit, which is arithmetic on two numbers it has, but could not ask "how far through the cycle are we", which is what any projection needs.

The rules are a deliberate mirror of the agent's, including the one that looks like an edge case and is not: **a reset day the month does not have rolls forward to the first of the next month.** A node configured for the 31st is reset on the 31st in January and on the 1st in February, and a projection that disagreed with the agent about that would be wrong for one day a month — which is exactly the day a limit tends to be hit. `internal/cycle`'s tests assert the same cases the agent's do, so the two can be compared rather than only trusted.

The location matters as much as the day: the agent resets on its *local* midnight, so a cycle computed in UTC for a node in Asia/Tokyo would be off by nine hours, and a forecast of "how much of the cycle has elapsed" would be wrong by a third of a day every day.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Location

func Location(name string) *time.Location

Location resolves a node's timezone name.

An unknown or empty name falls back to UTC rather than failing: a node whose timezone the panel cannot resolve is still a node whose traffic should be watched, and a projection computed in the wrong zone is more useful than no projection — provided the caller can see which zone was used, which is why Cycle carries the name.

Types

type Cycle

type Cycle struct {
	// Start is the most recent reset at or before the reference time.
	Start time.Time `json:"start"`
	// End is the next reset, which is the moment the current cycle's total goes back to zero.
	End time.Time `json:"end"`
	// ResetDay is the configured day of the month, kept so a reader can see what the boundaries came
	// from rather than having to infer it.
	ResetDay int `json:"reset_day"`
	// Location is the timezone the boundaries were computed in: the node's own, which is where its
	// agent resets. Reported as a name so a mismatch is visible.
	Location string `json:"location"`
}

Cycle is one billing period, half-open: `[Start, End)`.

Half-open because that is what the agent's reset produces: the instant of the reset belongs to the new cycle, which is why a sample landing exactly on it is counted once rather than twice or not at all. The forecast takes the same convention, so the two agree about the boundary.

func Bounds

func Bounds(resetDay int, location *time.Location, reference time.Time) (Cycle, error)

Bounds returns the billing cycle containing `reference`, for a node whose month resets on `resetDay` in `location`.

A reset day outside 1..31 means the node has no monthly cycle configured — the agent treats it the same way, returning the reference date itself — and is reported as an error rather than as a one-instant cycle, because a caller that got a zero-length cycle would go on to divide by it.

func (Cycle) Contains

func (c Cycle) Contains(at time.Time) bool

Contains reports whether an instant falls in the cycle.

func (Cycle) Duration

func (c Cycle) Duration() time.Duration

Duration is the cycle's length.

func (Cycle) Elapsed

func (c Cycle) Elapsed(at time.Time) time.Duration

Elapsed is how far into the cycle the reference time is, clamped to the cycle.

Clamped rather than computed directly because a caller may hold a report that arrived slightly after the reset: an unclamped elapsed fraction above 1 would make a projection divide by more than the cycle and report a total for a window that has already ended.

func (Cycle) Fraction

func (c Cycle) Fraction(at time.Time) float64

Fraction is the elapsed share of the cycle, in [0, 1].

type InvalidResetDayError

type InvalidResetDayError struct{ ResetDay int }

InvalidResetDayError reports a day the calendar cannot have.

func (*InvalidResetDayError) Error

func (e *InvalidResetDayError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL