extclock

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 7 Imported by: 0

README

go-extclock

CI Go Reference Go Version License

A Go library for extended-hour clocks and business-day date/time handling.

What is go-extclock?

An extended-hour clock can represent values beyond 24:00, such as 26:30 or 30:00. This is useful when a business day does not end at midnight.

With the default cutoff hour of 6, wall-clock 2026-07-05 02:30 is represented as business 2026-07-04 26:30. With the default system, the valid clock range is from 00:00:00 through 30:00:00.

When to use this library

Use this package when your domain represents a business day with extended-hour values such as 26:30 or 30:00.

Do not use it as a replacement for time.Time when you only need ordinary wall-clock timestamps.

Installation

go get github.com/Chiyoshic/go-extclock

Requirements

go-extclock requires Go 1.20 or later. CI tests Go 1.20.x through the latest stable Go release.

Full API documentation is available on pkg.go.dev.

Quick start

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	clock, err := extclock.ParseClock("26:30")
	if err != nil {
		log.Fatal(err)
	}

	dt, err := extclock.ParseDateTime("2026-07-04 26:30")
	if err != nil {
		log.Fatal(err)
	}

	wall := time.Date(2026, time.July, 5, 2, 30, 0, 0, time.UTC)
	business, err := extclock.FromTime(wall)
	if err != nil {
		log.Fatal(err)
	}

	back, err := extclock.DefaultSystem().ToTime(dt, time.UTC)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(clock.StringHM())
	fmt.Println(business.StringHM())
	fmt.Println(back.Format(time.RFC3339))
}

Output:

26:30
2026-07-04 26:30
2026-07-05T02:30:00Z

Core concepts

Clock

Clock is a date-less extended-hour clock. It has no date and no timezone. Its validity depends on a System: the default system accepts values up to 30:00:00, while custom systems can use other maximum hours. Package-level constructors such as NewClock use DefaultSystem().

Date

Date is a calendar date only. It has no clock and no timezone. Dates are validated against the library's supported year range, and invalid calendar dates such as 2026-02-31 are rejected.

DateTime

DateTime is a business date paired with an extended-hour clock. Its clock validity depends on a System. It represents business-day time, not necessarily a normalized wall-clock instant.

System

System owns cutoff-hour-specific rules. The default system uses cutoff hour 6, so MaxHour() is 30. In general, MaxHour() is 24 + CutoffHour(). Use System methods when working with custom cutoff rules.

Default system

Package-level functions use DefaultSystem(). Use them when the default cutoff hour of 6 matches your domain.

package main

import (
	"log"
	"time"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	clock, err := extclock.NewClock(26, 30, 0, 0)
	if err != nil {
		log.Fatal(err)
	}

	parsedClock, err := extclock.ParseClock("26:30")
	if err != nil {
		log.Fatal(err)
	}

	parsedDateTime, err := extclock.ParseDateTime("2026-07-04 26:30")
	if err != nil {
		log.Fatal(err)
	}

	business, err := extclock.FromTime(time.Date(2026, time.July, 5, 2, 30, 0, 0, time.UTC))
	if err != nil {
		log.Fatal(err)
	}

	_, _, _, _ = clock, parsedClock, parsedDateTime, business
}

Custom systems

Create a Config and then a System when your business day uses a different cutoff hour. The maximum clock is derived from the cutoff hour.

package main

import (
	"log"
	"time"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	cfg, err := extclock.NewConfig(10)
	if err != nil {
		log.Fatal(err)
	}

	s, err := extclock.NewSystem(cfg)
	if err != nil {
		log.Fatal(err)
	}

	clock, err := s.ParseClock("33:30")
	if err != nil {
		log.Fatal(err)
	}

	dt, err := s.NewDateTime(extclock.MustDate(2026, time.July, 4), clock)
	if err != nil {
		log.Fatal(err)
	}

	if err := s.ValidateDateTime(dt); err != nil {
		log.Fatal(err)
	}
}

With cutoff hour 10, the maximum clock is 34:00:00.

Parsing and formatting

Most parse functions have an Auto layout variant. Auto layout detects the input shape. For formatting, Auto uses the package-defined default behavior for that value type.

Clock inputs:

Input Meaning
26:30 hour and minute
26:30:00 hour, minute, second
26:30:00.123456789 nanosecond precision

Date inputs:

Input Meaning
2026-07-04 ISO date
20260704 compact date

DateTime inputs:

Input Meaning
2026-07-04 26:30 space separator
2026-07-04T26:30 T separator
2026-07-04 26:30:00.123456789 nanosecond precision

Formatting methods include Clock.Format, Date.Format, DateTime.Format, and their package-level or System variants. System formatting methods validate under that system before formatting.

JSON and text encoding

Clock, Date, and DateTime implement text and JSON encoding. JSON values are strings:

{
  "starts_at": "2026-07-04 26:30:00"
}

Text and JSON unmarshaling for Clock and DateTime is not tied to a specific System. When system-specific validation matters, call System.ValidateClock or System.ValidateDateTime after decoding.

Business-day conversion

System.FromTime converts wall-clock time to business DateTime. System.ToTime converts business DateTime back to wall-clock time. System.DayRange and System.DayRangeDate return a half-open business-day range [start, end).

Default cutoff examples:

2026-07-05 02:30 -> 2026-07-04 26:30
2026-07-05 06:30 -> 2026-07-05 06:30

Duration arithmetic

Clock duration operations come in two styles:

  • Bounded clock operations do not cross the business-day boundary.
  • Shift clock operations may cross the boundary and return a dayOffset.

DateTime duration operations may cross business days directly and return a new DateTime. Bounded DateTime duration operations reject crossing.

29:30 + 30m -> 30:00
29:30 + 1h  -> error for bounded clock add
29:30 + 1h  -> 06:30, dayOffset 1 for clock shift

2026-07-04 29:30 + 1h -> 2026-07-05 06:30

Relevant APIs include System.AddClockDurationBounded, System.SubClockDurationBounded, System.ShiftClockDuration, System.UnshiftClockDuration, System.AddDateTimeDuration, and System.AddDateTimeDurationBounded.

Comparison semantics

DateTime.Compare is a pure value comparison: it compares the stored business date and clock lexicographically. System.CompareDateTime is system-aware: it compares normalized wall-clock instants.

Under the default system, these two values may represent the same wall-clock instant:

2026-07-04 30:00
2026-07-05 06:00

That means pure value comparison and system-aware comparison can differ.

package main

import (
	"fmt"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	s := extclock.DefaultSystem()
	a := s.MustDateTime(extclock.MustDate(2026, 7, 4), s.MustClock(30, 0, 0, 0))
	b := s.MustDateTime(extclock.MustDate(2026, 7, 5), s.MustClock(6, 0, 0, 0))

	fmt.Println(a.Compare(b) == 0)

	n, err := s.CompareDateTime(a, b)
	if err != nil {
		panic(err)
	}
	fmt.Println(n == 0)
}

Errors

The package exposes sentinel errors for category checks:

  • ErrInvalidConfig
  • ErrInvalidClock
  • ErrInvalidDate
  • ErrInvalidDateTime
  • ErrInvalidLayout
  • ErrParseClock
  • ErrParseDate
  • ErrParseDateTime
  • ErrOutOfRange

Parsing failures may be returned as *ParseError. Use errors.Is to check error categories and wrapped validation causes. Do not rely on exact error strings.

package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	_, err := extclock.ClockFromDuration(31 * time.Hour)
	if errors.Is(err, extclock.ErrOutOfRange) {
		fmt.Println("range error")
	}
}

Edge cases and caveats

  • With the default system, 30:00:00 is valid and 30:00:00.000000001 is invalid.
  • In any system, MaxHour():00:00 is valid; values after that boundary are invalid.
  • Date supports years 1 through 9999. The zero value Date{} is invalid.
  • Clock and DateTime do not carry a timezone.
  • DateTime values near the maximum supported date may validate but still fail conversion to wall-clock time if extended-hour normalization would move past year 9999.
  • Very large date/time differences can exceed time.Duration range and return ErrOutOfRange.
  • DayRange and DayRangeDate use calendar-day arithmetic in the provided location; daylight-saving transitions can affect elapsed wall-clock duration.

Testing

go test ./...

CI runs formatting, go vet, tests on Go 1.20.x through stable, and race tests on stable Go.

License

go-extclock is licensed under the MIT License. See LICENSE.

Documentation

Overview

Package extclock provides extended-hour clocks and business-day date/time handling.

An extended-hour clock can represent values beyond 24:00, such as 26:30 or 30:00. This is useful when a business day starts at a cutoff hour instead of midnight. With the default cutoff hour of 6, wall-clock time 2026-07-05 02:30 is represented as business date 2026-07-04 with clock 26:30.

Core model

The package has four central value concepts:

  • Clock is a date-less extended-hour clock value. It has no date and no timezone.
  • Date is a calendar date without clock or timezone information.
  • DateTime is a business date paired with an extended-hour clock.
  • System owns cutoff-hour-specific validation and conversion rules.

Clock and DateTime clock validity depends on a System. Date validity is calendar-based and independent of cutoff-hour rules. Package-level constructors, parsers, converters, and validators use DefaultSystem. Use System methods when custom cutoff-hour rules or system-specific validation are required.

Default and custom systems

DefaultSystem uses cutoff hour 6, so its maximum clock value is 30:00:00. For any System, MaxHour is 24 + CutoffHour. A custom system can use another supported cutoff hour, such as 0 for a 24:00 maximum or 10 for a 34:00 maximum.

Parsing, formatting, and encoding

Clock, Date, and DateTime support parsing and formatting through explicit layouts and auto layouts. Auto layouts infer supported input shapes; detailed layout behavior is documented on ClockLayout, DateLayout, and DateTimeLayout.

Text and JSON encoding use string forms. Unmarshaling Clock and DateTime values is not a substitute for system-specific validation when custom system rules matter; call System.ValidateClock or System.ValidateDateTime after decoding when necessary.

Business-day conversion

System.FromTime converts wall-clock time.Time values to business DateTime values. System.ToTime converts business DateTime values back to wall-clock time.Time values. System.DayRange and System.DayRangeDate return the half-open wall-clock range [start, end) for a business date.

Duration arithmetic

Clock bounded operations reject results outside the representable clock range. Clock shift operations may cross that range and return a dayOffset. DateTime duration operations can return a new business DateTime directly, while bounded DateTime duration operations reject crossing the business-day boundary.

Comparison semantics

DateTime.Compare performs lexicographic comparison by stored business date and clock. System.CompareDateTime performs system-aware comparison by normalized wall-clock instant. For example, under the default system, 2026-07-04 30:00 and 2026-07-05 06:00 may represent the same wall-clock instant, so pure value comparison and system-aware comparison can differ.

Errors

The package uses exported sentinel errors for parse, validation, layout, and range failures. Callers should use errors.Is to test error categories and should not treat exact error strings as an API contract.

Caveats

Clock has no date or timezone. DateTime is a business representation and is not always a unique normalized wall-clock instant without a System. Timezone and daylight-saving behavior follows the time.Location passed to conversion methods and Go's time package. Very large date differences may exceed time.Duration when using APIs that return durations.

Example (ComparisonSemantics)
package main

import (
	"fmt"
	"log"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	s := extclock.DefaultSystem()
	a := s.MustDateTime(extclock.MustDate(2026, 7, 4), s.MustClock(30, 0, 0, 0))
	b := s.MustDateTime(extclock.MustDate(2026, 7, 5), s.MustClock(6, 0, 0, 0))

	fmt.Println(a.Compare(b) == 0)

	n, err := s.CompareDateTime(a, b)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(n == 0)
}
Output:
false
true
Example (CustomSystem)
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	cfg, err := extclock.NewConfig(10)
	if err != nil {
		log.Fatal(err)
	}

	s, err := extclock.NewSystem(cfg)
	if err != nil {
		log.Fatal(err)
	}

	clock, err := s.ParseClock("33:30")
	if err != nil {
		log.Fatal(err)
	}

	dt, err := s.NewDateTime(extclock.MustDate(2026, time.July, 4), clock)
	if err != nil {
		log.Fatal(err)
	}

	if err := s.ValidateDateTime(dt); err != nil {
		log.Fatal(err)
	}

	fmt.Println(s.MaxHour())
	fmt.Println(dt.StringHM())
}
Output:
34
2026-07-04 33:30
Example (QuickStart)
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/Chiyoshic/go-extclock"
)

func main() {
	clock, err := extclock.ParseClock("26:30")
	if err != nil {
		log.Fatal(err)
	}

	dt, err := extclock.ParseDateTime("2026-07-04 26:30")
	if err != nil {
		log.Fatal(err)
	}

	wall := time.Date(2026, time.July, 5, 2, 30, 0, 0, time.UTC)
	business, err := extclock.FromTime(wall)
	if err != nil {
		log.Fatal(err)
	}

	back, err := extclock.DefaultSystem().ToTime(dt, time.UTC)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(clock.StringHM())
	fmt.Println(business.StringHM())
	fmt.Println(back.Format(time.RFC3339))
}
Output:
26:30
2026-07-04 26:30
2026-07-05T02:30:00Z

Index

Examples

Constants

View Source
const (
	// DefaultCutoffHour is the cutoff hour used by the default system.
	DefaultCutoffHour = 6

	// MinCutoffHour is the smallest supported cutoff hour.
	MinCutoffHour = 0
	// MaxCutoffHour is the largest supported cutoff hour.
	MaxCutoffHour = 10

	// MinClockHour is the smallest valid hour value for an extended-hour clock.
	MinClockHour = 0
	// DefaultMaxClockHour is the largest clock hour allowed by the default system.
	DefaultMaxClockHour = 24 + DefaultCutoffHour
)
View Source
const (
	// MinDateYear is the smallest supported calendar year.
	MinDateYear = 1

	// MaxDateYear is the largest supported calendar year.
	MaxDateYear = 9999
)

Variables

View Source
var (
	// ErrInvalidConfig reports an unsupported System configuration.
	ErrInvalidConfig = errors.New("extclock: invalid config")
	// ErrInvalidClock reports an invalid Clock for the relevant System.
	ErrInvalidClock = errors.New("extclock: invalid clock")
	// ErrInvalidDate reports an invalid Date.
	ErrInvalidDate = errors.New("extclock: invalid date")
	// ErrInvalidDateTime reports an invalid DateTime or failed DateTime normalization.
	ErrInvalidDateTime = errors.New("extclock: invalid datetime")
	// ErrInvalidLayout reports an unsupported parse or format layout.
	ErrInvalidLayout = errors.New("extclock: invalid layout")
	// ErrParseClock reports a Clock parsing failure.
	ErrParseClock = errors.New("extclock: parse clock")
	// ErrParseDate reports a Date parsing failure.
	ErrParseDate = errors.New("extclock: parse date")
	// ErrParseDateTime reports a DateTime parsing failure.
	ErrParseDateTime = errors.New("extclock: parse datetime")
	// ErrOutOfRange reports a value outside a supported range.
	ErrOutOfRange = errors.New("extclock: out of range")
)

Functions

func DayRange

func DayRange(year int, month time.Month, day int, loc *time.Location) (start, end time.Time, err error)

DayRange returns the half-open wall-clock time range for a business date under the default system.

With the default cutoff hour of 6, business date 2026-07-04 maps to [2026-07-04 06:00, 2026-07-05 06:00). For custom systems, use System.DayRange.

func DayRangeDate

func DayRangeDate(date Date, loc *time.Location) (start, end time.Time, err error)

DayRangeDate returns the half-open wall-clock time range for a business date under the default system.

With the default cutoff hour of 6, business date 2026-07-04 maps to [2026-07-04 06:00, 2026-07-05 06:00). For custom systems, use System.DayRangeDate.

func FormatClock

func FormatClock(c Clock, layout ClockLayout) (string, error)

FormatClock validates c under the default system and formats it with layout.

Unlike Clock.Format, FormatClock checks that c is valid under the default system before formatting. For custom systems, use System.FormatClock.

func FormatDate

func FormatDate(d Date, layout DateLayout) (string, error)

FormatDate returns a string representation of d using layout.

It is equivalent to d.Format(layout).

func FormatDateTime

func FormatDateTime(dt DateTime, layout DateTimeLayout) (string, error)

FormatDateTime validates dt under the default system and formats it with layout.

Unlike DateTime.Format, FormatDateTime checks that dt is valid under the default system before formatting. For custom systems, use System.FormatDateTime.

Types

type Clock

type Clock struct {
	// contains filtered or unexported fields
}

Clock represents a date-less extended-hour clock value.

Clock has no date, timezone, or system configuration. Its validity depends on the System used for system-specific validation.

The default System allows:

00:00:00.000000000 <= Clock <= 30:00:00.000000000

A custom System may allow a different maximum, such as 24:00 or 34:00.

func ClockFromDuration

func ClockFromDuration(d time.Duration) (Clock, error)

ClockFromDuration converts a duration to a Clock using the default system.

For example, 26h30m becomes 26:30:00 and 30h becomes 30:00:00. Durations greater than 30:00:00 return ErrOutOfRange. For custom systems, use System.ClockFromDuration.

func MustClock

func MustClock(hour, minute, second, nanosecond int) Clock

MustClock returns a Clock with the given components using the default system.

It panics if the components are invalid. Intended for tests, constants, and example code. For custom systems, use System.MustClock.

func MustParseClock

func MustParseClock(input string) Clock

MustParseClock parses input as a Clock using the default system.

It panics if parsing fails. Intended for tests, constants, and example code.

func MustParseClockLayout

func MustParseClockLayout(layout ClockLayout, input string) Clock

MustParseClockLayout parses input as a Clock using the default system and layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func NewClock

func NewClock(hour, minute, second, nanosecond int) (Clock, error)

NewClock returns a Clock with the given components using the default system.

The default system allows clocks up to 30:00:00. For custom systems, use System.NewClock.

func NewClockHM

func NewClockHM(hour, minute int) (Clock, error)

NewClockHM returns a Clock with hour and minute precision (HH:MM) using the default system. For custom systems, use System.NewClockHM.

func NewClockHMS

func NewClockHMS(hour, minute, second int) (Clock, error)

NewClockHMS returns a Clock with hour, minute, and second precision (HH:MM:SS) using the default system. For custom systems, use System.NewClockHMS.

func ParseClock

func ParseClock(input string) (Clock, error)

ParseClock parses input as a Clock using the default system and auto-detected layout.

It is equivalent to DefaultSystem().ParseClock(input). For custom systems, use System.ParseClock.

func ParseClockLayout

func ParseClockLayout(layout ClockLayout, input string) (Clock, error)

ParseClockLayout parses input as a Clock using the default system and layout.

It is equivalent to DefaultSystem().ParseClockLayout(layout, input). For custom systems, use System.ParseClockLayout.

func (Clock) After

func (c Clock) After(other Clock) bool

After reports whether c is later than other.

After does not validate either clock against a System.

func (Clock) Before

func (c Clock) Before(other Clock) bool

Before reports whether c is earlier than other.

Before does not validate either clock against a System.

func (Clock) Compare

func (c Clock) Compare(other Clock) int

Compare compares c and other by elapsed time since Clock 00:00.

It returns -1 if c is before other, 1 if c is after other, and 0 if equal. Compare does not validate either clock against a System; it compares the stored field values only.

func (Clock) Components

func (c Clock) Components() (hour, minute, second, nanosecond int)

Components returns the hour, minute, second, and nanosecond of c.

func (Clock) Duration

func (c Clock) Duration() time.Duration

Duration returns the elapsed time since Clock 00:00:00.000000000.

Example:

26:30:00 -> 26h30m0s

func (Clock) Equal

func (c Clock) Equal(other Clock) bool

Equal reports whether c and other represent the same clock value.

Equal does not validate either clock against a System.

func (Clock) Format

func (c Clock) Format(layout ClockLayout) (string, error)

Format returns a string representation of c using layout.

Supported layouts are ClockLayoutAuto, ClockLayoutHM, ClockLayoutHMS, and ClockLayoutNano. ClockLayoutAuto selects HM, HMS, or Nano based on which components are non-zero. An unknown layout returns ErrInvalidLayout.

Format uses fixed-width fields. For example, hour 6 is written as 06, and fractional seconds are written with 9 digits. Format does not validate c against a System; use System.FormatClock when system-specific validation is required.

func (Clock) Hour

func (c Clock) Hour() int

Hour returns the hour component of c.

The hour is a zero-based count from midnight and may exceed 23 on an extended clock. For example, 25:30:00 has hour 25. The returned value is always non-negative for a valid Clock.

func (Clock) IsExtended

func (c Clock) IsExtended() bool

IsExtended reports whether c is at or after 24:00:00.

For example, 25:30:00 and 30:00:00 are extended; 23:59:59 is not. In a 24-hour system, 24:00:00 is also considered extended.

func (Clock) IsWholeHour

func (c Clock) IsWholeHour() bool

IsWholeHour reports whether c is on an exact hour with no sub-hour components.

func (Clock) IsZero

func (c Clock) IsZero() bool

IsZero reports whether c is exactly 00:00:00.000000000.

func (Clock) MarshalJSON

func (c Clock) MarshalJSON() ([]byte, error)

MarshalJSON encodes c as a JSON string, for example "26:30:00".

func (Clock) MarshalText

func (c Clock) MarshalText() ([]byte, error)

MarshalText encodes c as text using String format.

MarshalText does not validate c against a specific System.

func (Clock) Minute

func (c Clock) Minute() int

Minute returns the minute component of c.

The minute is in the range 0–59. It is the minute-of-hour field, not total minutes since midnight.

func (Clock) Nanosecond

func (c Clock) Nanosecond() int

Nanosecond returns the sub-second component of c.

The nanosecond is in the range 0–999_999_999. It is the fractional part of the current second, not total nanoseconds since midnight.

func (Clock) Second

func (c Clock) Second() int

Second returns the second component of c.

The second is in the range 0–59. It is the second-of-minute field, not total seconds since midnight.

func (Clock) String

func (c Clock) String() string

String returns the clock in HH:MM:SS form, for example 26:30:00.

func (Clock) StringHM

func (c Clock) StringHM() string

StringHM returns the clock in HH:MM form.

func (Clock) StringHMS

func (c Clock) StringHMS() string

StringHMS returns the clock in HH:MM:SS form.

func (Clock) StringNano

func (c Clock) StringNano() string

StringNano returns the clock in HH:MM:SS.NNNNNNNNN form with a fixed 9-digit fractional second, for example 26:30:00.120000000.

func (*Clock) UnmarshalJSON

func (c *Clock) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes c from a JSON string.

It delegates to UnmarshalText, so it accepts the same library-supported clock range and does not perform system-specific validation.

func (*Clock) UnmarshalText

func (c *Clock) UnmarshalText(data []byte) error

UnmarshalText decodes c from text within the library-supported clock range.

Parsing uses the same lenient field rules as ParseClockLayout. UnmarshalText does not validate c against a specific System. Use System.ValidateClock after decoding when system-specific validation is required.

type ClockLayout

type ClockLayout string

ClockLayout selects the format used to parse and format Clock values.

Layout names use HH, MM, SS, and NNNNNNNNN as formatting conventions. Parsing is more lenient than formatting:

  • fields are colon-separated decimal integers
  • hour may use one or more digits, such as 6, 06, or 26
  • minute and second accept values in 0..59 without requiring leading zeros
  • fractional seconds accept 1 to 9 digits; shorter fractions are padded with trailing zeros during parsing

Formatting uses fixed-width fields. For example, 6:30 is formatted as 06:30, and 26:30:00.12 is formatted as 26:30:00.120000000.

Supported layouts:

ClockLayoutAuto -> auto-detect from ".", ":" count, and field shape
ClockLayoutHM   -> hour and minute
ClockLayoutHMS  -> hour, minute, and second
ClockLayoutNano -> hour, minute, second, and fractional second
const (
	// ClockLayoutAuto selects the clock format automatically when parsing and
	// selects precision from non-zero components when formatting.
	ClockLayoutAuto ClockLayout = ""

	// ClockLayoutHM formats a clock as HH:MM, for example 06:30.
	// Parsing accepts lenient hour and minute fields, such as 6:30 or 26:5.
	ClockLayoutHM ClockLayout = "HH:MM"

	// ClockLayoutHMS formats a clock as HH:MM:SS, for example 26:30:00.
	// Parsing accepts lenient numeric fields, such as 26:30:0.
	ClockLayoutHMS ClockLayout = "HH:MM:SS"

	// ClockLayoutNano formats a clock as HH:MM:SS.NNNNNNNNN,
	// for example 26:30:00.120000000. Parsing accepts 1 to 9 fractional
	// digits, such as 26:30:00.12.
	ClockLayoutNano ClockLayout = "HH:MM:SS.NNNNNNNNN"
)

type Config

type Config struct {
	// contains filtered or unexported fields
}

Config defines how many hours after midnight still belong to the previous business day.

Examples:

cutoffHour = 0  -> max clock is 24:00
cutoffHour = 6  -> max clock is 30:00
cutoffHour = 10 -> max clock is 34:00

The zero value is valid and means cutoffHour = 0. Use DefaultConfig or NewConfig(-1) to use DefaultCutoffHour.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the configuration used by DefaultSystem.

func MustConfig

func MustConfig(cutoffHour int) Config

MustConfig returns a Config with the given cutoff hour.

It panics if NewConfig would return an error.

func NewConfig

func NewConfig(cutoffHour int) (Config, error)

NewConfig returns a Config with the given cutoff hour.

The cutoff hour must be in [MinCutoffHour, MaxCutoffHour]. Passing -1 is a shorthand for DefaultConfig.

func (Config) CutoffHour

func (c Config) CutoffHour() int

CutoffHour returns the configured cutoff hour.

func (Config) MaxHour

func (c Config) MaxHour() int

MaxHour returns the largest clock hour allowed by c.

It is always 24 + CutoffHour.

func (Config) Validate

func (c Config) Validate() error

Validate reports whether c has a supported cutoff hour.

Invalid cutoff hours return an error wrapping ErrInvalidConfig.

type Date

type Date struct {
	// contains filtered or unexported fields
}

Date represents a calendar date without clock or timezone information.

Date values use the year range supported by this library: MinDateYear through MaxDateYear.

func DateFromTime

func DateFromTime(t time.Time) (Date, error)

DateFromTime returns the calendar date part of t.

It uses the same validation rules as NewDate.

func MustDate

func MustDate(year int, month time.Month, day int) Date

MustDate returns a Date for the given calendar components.

It panics if the date is invalid. Intended for tests, constants, and example code.

func MustDateFromTime

func MustDateFromTime(t time.Time) Date

MustDateFromTime returns the calendar date part of t.

It panics if the date is invalid. Intended for tests, constants, and example code.

func MustParseDate

func MustParseDate(input string) Date

MustParseDate parses input as a Date using auto-detected layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func MustParseDateLayout

func MustParseDateLayout(layout DateLayout, input string) Date

MustParseDateLayout parses input as a Date using layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func NewDate

func NewDate(year int, month time.Month, day int) (Date, error)

NewDate returns a Date for the given calendar components.

The year must be in [MinDateYear, MaxDateYear]. Invalid dates such as February 31 return ErrInvalidDate. Go's time.Date normalizes overflow dates, so NewDate verifies that the resulting year, month, and day match the input.

func ParseDate

func ParseDate(input string) (Date, error)

ParseDate parses input as a Date using auto-detected layout.

It is equivalent to ParseDateLayout(DateLayoutAuto, input).

func ParseDateLayout

func ParseDateLayout(layout DateLayout, input string) (Date, error)

ParseDateLayout parses input as a Date using layout.

Supported layouts are DateLayoutAuto, DateLayoutISO, and DateLayoutCompact. An empty input or unrecognized auto-detected format returns ErrParseDate. An unknown layout returns ErrInvalidLayout.

DateLayoutAuto chooses a layout from the input shape:

  • length 10 with '-' at positions 4 and 7 -> DateLayoutISO (for example 2026-07-04)
  • length 8 and all digits -> DateLayoutCompact (for example 20260704)

After parsing, the date is validated with NewDate. Parse and validation failures are returned as *ParseError values that support errors.Is for both ErrParseDate and the wrapped cause.

func (Date) AddDays

func (d Date) AddDays(days int) (Date, error)

AddDays returns the date that is days after d.

Negative days move backward. The calculation uses UTC calendar arithmetic. AddDays validates both d and the result. If either date is invalid or outside the supported year range, AddDays returns an error.

func (Date) After

func (d Date) After(other Date) bool

After reports whether d is later than other by calendar order.

After uses Compare and does not validate either Date.

func (Date) Before

func (d Date) Before(other Date) bool

Before reports whether d is earlier than other by calendar order.

Before uses Compare and does not validate either Date.

func (Date) Compare

func (d Date) Compare(other Date) int

Compare compares d and other by calendar order.

It returns -1 if d is before other, 1 if d is after other, and 0 if equal. Compare does not validate either Date; it compares the stored year, month, and day fields.

func (Date) Components

func (d Date) Components() (year int, month time.Month, day int)

Components returns the year, month, and day of d.

func (Date) Day

func (d Date) Day() int

Day returns the day of the month for d.

func (Date) Equal

func (d Date) Equal(other Date) bool

Equal reports whether d and other contain the same stored date fields.

Equal does not validate either Date.

func (Date) Format

func (d Date) Format(layout DateLayout) (string, error)

Format returns a string representation of d using layout.

Supported layouts are DateLayoutAuto, DateLayoutISO, and DateLayoutCompact. DateLayoutAuto is treated as DateLayoutISO. An unknown layout returns ErrInvalidLayout.

func (Date) MarshalJSON

func (d Date) MarshalJSON() ([]byte, error)

MarshalJSON encodes d as a JSON string, for example "2026-07-04".

func (Date) MarshalText

func (d Date) MarshalText() ([]byte, error)

MarshalText validates d and encodes it as an ISO date string.

func (Date) Month

func (d Date) Month() time.Month

Month returns the calendar month of d.

func (Date) MustAddDays

func (d Date) MustAddDays(days int) Date

MustAddDays returns the date that is days after d.

It panics if d is invalid or the result falls outside the supported year range. Intended for tests, constants, and example code.

func (Date) String

func (d Date) String() string

String returns the default ISO date string for d, for example 2026-07-04.

func (Date) ToTime

func (d Date) ToTime(loc *time.Location) time.Time

ToTime returns midnight on d in loc.

If loc is nil, time.Local is used. ToTime assumes d is valid; call Validate first if d may contain the zero value or another invalid date.

func (*Date) UnmarshalJSON

func (d *Date) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes d from a JSON string.

It delegates to UnmarshalText, so it accepts ISO and compact date strings and applies the same validation rules.

func (*Date) UnmarshalText

func (d *Date) UnmarshalText(data []byte) error

UnmarshalText decodes d from an ISO or compact date string.

It delegates to ParseDate, so invalid calendar dates and unsupported shapes are rejected with parse errors that wrap the underlying validation cause.

func (Date) Valid

func (d Date) Valid() bool

Valid reports whether d represents a valid calendar date.

It is equivalent to d.Validate() == nil.

func (Date) Validate

func (d Date) Validate() error

Validate reports whether d represents a valid calendar date.

The zero value Date is invalid. Validation uses the same year-range and normalization checks as NewDate.

func (Date) Year

func (d Date) Year() int

Year returns the calendar year of d.

type DateLayout

type DateLayout string

DateLayout selects the format used to parse and format Date values.

Supported layouts:

DateLayoutAuto    -> auto-detect (2026-07-04 or 20260704)
DateLayoutISO     -> 2026-07-04 (default when formatting)
DateLayoutCompact -> 20260704
const (
	// DateLayoutAuto selects the date format automatically when parsing and
	// formats as DateLayoutISO.
	DateLayoutAuto DateLayout = ""

	// DateLayoutISO formats a date as YYYY-MM-DD, for example 2026-07-04.
	// This is the default date format.
	DateLayoutISO DateLayout = "YYYY-MM-DD"

	// DateLayoutCompact formats a date as YYYYMMDD, for example 20260704.
	DateLayoutCompact DateLayout = "YYYYMMDD"
)

type DateTime

type DateTime struct {
	// contains filtered or unexported fields
}

DateTime represents a business date paired with an extended-hour clock.

DateTime has no timezone. Whether its clock is valid depends on the System used to construct or validate it. The default system allows clocks up to 30:00:00.

func FromTime

func FromTime(t time.Time) (DateTime, error)

FromTime converts a wall-clock time to a business DateTime using the default system.

With the default cutoff hour of 6, 2026-07-05 02:30 becomes 2026-07-04 26:30 and 2026-07-05 06:30 stays 2026-07-05 06:30. For custom systems, use System.FromTime.

func MustDateTime

func MustDateTime(date Date, clock Clock) DateTime

MustDateTime returns a DateTime with the given date and clock using the default system.

It panics if date or clock is invalid. Intended for tests, constants, and example code. For custom systems, use System.MustDateTime.

func MustParseDateTime

func MustParseDateTime(input string) DateTime

MustParseDateTime parses input as a DateTime using the default system.

It panics if parsing fails. Intended for tests, constants, and example code. For custom systems, use System.MustParseDateTime.

func MustParseDateTimeLayout

func MustParseDateTimeLayout(layout DateTimeLayout, input string) DateTime

MustParseDateTimeLayout parses input as a DateTime using the default system and layout.

It panics if parsing fails. Intended for tests, constants, and example code. For custom systems, use System.MustParseDateTimeLayout.

func NewDateTime

func NewDateTime(date Date, clock Clock) (DateTime, error)

NewDateTime returns a DateTime with the given date and clock using the default system.

The default system allows clocks up to 30:00:00. For custom systems, use System.NewDateTime.

func ParseDateTime

func ParseDateTime(input string) (DateTime, error)

ParseDateTime parses input as a DateTime using the default system and auto-detected layout.

It is equivalent to DefaultSystem().ParseDateTime(input). For custom systems, use System.ParseDateTime.

func ParseDateTimeLayout

func ParseDateTimeLayout(layout DateTimeLayout, input string) (DateTime, error)

ParseDateTimeLayout parses input as a DateTime using the default system and layout.

It is equivalent to DefaultSystem().ParseDateTimeLayout(layout, input). For custom systems, use System.ParseDateTimeLayout.

func (DateTime) After

func (dt DateTime) After(other DateTime) bool

After reports whether dt is later than other.

After does not validate either DateTime against a System.

func (DateTime) Before

func (dt DateTime) Before(other DateTime) bool

Before reports whether dt is earlier than other.

Before does not validate either DateTime against a System.

func (DateTime) Clock

func (dt DateTime) Clock() Clock

Clock returns the extended clock part of dt.

func (DateTime) Compare

func (dt DateTime) Compare(other DateTime) int

Compare performs a lexicographic comparison by stored business date and clock.

It returns -1 if dt is before other, 1 if dt is after other, and 0 if equal. Compare does not validate either DateTime against a System and does not normalize equivalent wall-clock instants such as 2026-07-04 30:00 and 2026-07-05 06:00. Use System.CompareDateTime for system-aware comparison.

func (DateTime) Components

func (dt DateTime) Components() (date Date, clock Clock)

Components returns the date and clock parts of dt.

func (DateTime) Date

func (dt DateTime) Date() Date

Date returns the business date part of dt.

func (DateTime) Day

func (dt DateTime) Day() int

Day returns the day of the month for the stored business date.

func (DateTime) Equal

func (dt DateTime) Equal(other DateTime) bool

Equal reports whether dt and other represent the same date and clock values.

Equal does not validate either DateTime against a System.

func (DateTime) Format

func (dt DateTime) Format(layout DateTimeLayout) (string, error)

Format returns a string representation of dt using layout.

Format does not validate dt against a System. DateTimeLayoutAuto is treated as DateTimeLayoutSpaceHMS. Space layouts join date and clock with a space; T layouts join with "T". The date is formatted as DateLayoutISO and the clock is formatted according to the layout precision. An unknown layout returns ErrInvalidLayout.

func (DateTime) Hour

func (dt DateTime) Hour() int

Hour returns the hour of the stored extended-hour clock.

The value may exceed 23, such as 26 for 26:30.

func (DateTime) MarshalJSON

func (dt DateTime) MarshalJSON() ([]byte, error)

MarshalJSON encodes dt as a JSON string, for example "2026-07-04 26:30:00".

func (DateTime) MarshalText

func (dt DateTime) MarshalText() ([]byte, error)

MarshalText encodes dt as text using String format.

MarshalText does not validate dt against a specific System.

func (DateTime) Minute

func (dt DateTime) Minute() int

Minute returns the minute component of the stored clock.

func (DateTime) Month

func (dt DateTime) Month() time.Month

Month returns the month of the stored business date.

func (DateTime) Nanosecond

func (dt DateTime) Nanosecond() int

Nanosecond returns the nanosecond component of the stored clock.

func (DateTime) Second

func (dt DateTime) Second() int

Second returns the second component of the stored clock.

func (DateTime) String

func (dt DateTime) String() string

String returns the default DateTime string, for example 2026-07-04 26:30:00.

func (DateTime) StringHM

func (dt DateTime) StringHM() string

StringHM returns dt formatted to minute precision, for example 2026-07-04 26:30.

func (DateTime) StringHMS

func (dt DateTime) StringHMS() string

StringHMS returns dt formatted to second precision, for example 2026-07-04 26:30:00.

func (DateTime) StringNano

func (dt DateTime) StringNano() string

StringNano returns dt formatted with a fixed 9-digit fractional second, for example 2026-07-04 26:30:00.120000000.

func (*DateTime) UnmarshalJSON

func (dt *DateTime) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes dt from a JSON string.

It delegates to UnmarshalText, so it accepts the same library-supported DateTime range and does not perform system-specific validation.

func (*DateTime) UnmarshalText

func (dt *DateTime) UnmarshalText(data []byte) error

UnmarshalText decodes dt from text within the library-supported range.

Parsing uses the same lenient field rules as ParseDateTimeLayout. UnmarshalText does not validate dt against a specific System. Use System.ValidateDateTime after decoding when system-specific validation is required.

func (DateTime) Year

func (dt DateTime) Year() int

Year returns the year of the stored business date.

type DateTimeLayout

type DateTimeLayout string

DateTimeLayout selects the format used to parse and format DateTime values.

Each layout combines an ISO date part and a clock part. Supported layouts:

DateTimeLayoutAuto      -> auto-detect separator and clock precision
DateTimeLayoutSpaceHM   -> 2026-07-04 26:30
DateTimeLayoutSpaceHMS  -> 2026-07-04 26:30:00
DateTimeLayoutSpaceNano -> 2026-07-04 26:30:00.123456789
DateTimeLayoutTHM       -> 2026-07-04T26:30
DateTimeLayoutTHMS      -> 2026-07-04T26:30:00
DateTimeLayoutTNano     -> 2026-07-04T26:30:00.123456789

Timezone offsets are not supported, for example 2026-07-04T26:30:00+09:00.

const (
	// DateTimeLayoutAuto selects the datetime format automatically when parsing
	// and formats as DateTimeLayoutSpaceHMS.
	DateTimeLayoutAuto DateTimeLayout = ""

	// DateTimeLayoutSpaceHM formats a datetime as "YYYY-MM-DD HH:MM",
	// for example 2026-07-04 26:30.
	DateTimeLayoutSpaceHM DateTimeLayout = "YYYY-MM-DD HH:MM"

	// DateTimeLayoutSpaceHMS formats a datetime as "YYYY-MM-DD HH:MM:SS",
	// for example 2026-07-04 26:30:00.
	DateTimeLayoutSpaceHMS DateTimeLayout = "YYYY-MM-DD HH:MM:SS"

	// DateTimeLayoutSpaceNano formats a datetime as
	// "YYYY-MM-DD HH:MM:SS.NNNNNNNNN",
	// for example 2026-07-04 26:30:00.123456789.
	DateTimeLayoutSpaceNano DateTimeLayout = "YYYY-MM-DD HH:MM:SS.NNNNNNNNN"

	// DateTimeLayoutTHM formats a datetime as "YYYY-MM-DDTHH:MM",
	// for example 2026-07-04T26:30.
	DateTimeLayoutTHM DateTimeLayout = "YYYY-MM-DDTHH:MM"

	// DateTimeLayoutTHMS formats a datetime as "YYYY-MM-DDTHH:MM:SS",
	// for example 2026-07-04T26:30:00.
	DateTimeLayoutTHMS DateTimeLayout = "YYYY-MM-DDTHH:MM:SS"

	// DateTimeLayoutTNano formats a datetime as
	// "YYYY-MM-DDTHH:MM:SS.NNNNNNNNN",
	// for example 2026-07-04T26:30:00.123456789.
	DateTimeLayoutTNano DateTimeLayout = "YYYY-MM-DDTHH:MM:SS.NNNNNNNNN"
)

type ParseError

type ParseError struct {
	// Layout is the layout requested by the caller, or empty for auto layout.
	Layout string
	// Input is the original input string after outer whitespace trimming.
	Input string
	// Err is the underlying parse or validation cause.
	Err error
	// contains filtered or unexported fields
}

ParseError reports a failure to parse input with a specific layout.

ParseError unwraps the underlying cause and also matches the parse category with errors.Is, such as ErrParseClock, ErrParseDate, or ErrParseDateTime.

func (*ParseError) Error

func (e *ParseError) Error() string

Error returns the formatted parse error message.

func (*ParseError) Is

func (e *ParseError) Is(target error) bool

Is reports whether target is the parse category for e.

The wrapped cause remains available through Unwrap, so errors.Is can match both the parse category and the underlying validation error.

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Unwrap returns the underlying parse or validation cause.

type System

type System struct {
	// contains filtered or unexported fields
}

System owns cutoff-hour-specific rules for clocks and business dates.

Use System methods when a value must be constructed, formatted, converted, compared, or validated under a custom system instead of the default system.

func DefaultSystem

func DefaultSystem() System

DefaultSystem returns a System that uses the default 30-hour clock rules.

The returned system has cutoff hour 6 and maximum clock hour 30:

DefaultSystem().CutoffHour() == 6
DefaultSystem().MaxHour() == 30

func MustSystem

func MustSystem(config Config) System

MustSystem returns a System backed by the given configuration.

It panics if config is invalid. This function is intended for tests, package-level variable initialization, and example code where a valid configuration is known at compile time or setup time.

func NewSystem

func NewSystem(config Config) (System, error)

NewSystem returns a System for the given configuration.

Config values are typically obtained from DefaultConfig, NewConfig, MustConfig, or Config{} (cutoff hour 0). If config is invalid, NewSystem returns a zero System and the validation error.

func (System) AddClockDurationBounded

func (s System) AddClockDurationBounded(c Clock, d time.Duration) (Clock, error)

AddClockDurationBounded adds d to c without wrapping across a business day.

c must be valid under the system. The sum is computed from Clock 00:00; if the result is negative or exceeds MaxHour():00:00, AddClockDurationBounded returns ErrOutOfRange. There is no wrap-around.

For example, with the default system (max 30:00:00):

29:30 + 30m -> 30:00
29:30 + 1h  -> error

Use ShiftClockDuration when the result should wrap to an adjacent business day.

func (System) AddDateTime

func (s System) AddDateTime(dt DateTime, d time.Duration) (DateTime, error)

AddDateTime adds d to dt in business date and extended-hour clock space.

It is equivalent to AddDateTimeDuration. The result preserves extended-hour representation and may cross to an adjacent business day.

func (System) AddDateTimeDuration

func (s System) AddDateTimeDuration(dt DateTime, d time.Duration) (DateTime, error)

AddDateTimeDuration adds d to dt in business date and extended-hour clock space.

The result may cross to an adjacent business day. For example, with the default system:

2026-07-04 29:30 + 1h  -> 2026-07-05 06:30
2026-07-04 30:00 + 1ns -> 2026-07-05 06:00:00.000000001
2026-07-04 00:30 - 1h  -> 2026-07-03 23:30

Use AddDateTimeDurationBounded when the result must stay on the same business day.

func (System) AddDateTimeDurationBounded

func (s System) AddDateTimeDurationBounded(dt DateTime, d time.Duration) (DateTime, error)

AddDateTimeDurationBounded adds d to dt without shifting to another business day.

If the result would move to an adjacent business day, it returns ErrOutOfRange. For example, with the default system:

2026-07-04 29:30 + 30m -> 2026-07-04 30:00
2026-07-04 29:30 + 1h  -> error

func (System) AfterClock

func (s System) AfterClock(a, b Clock) (bool, error)

AfterClock reports whether a is later than b after validating both under the system.

func (System) AfterDateTime

func (s System) AfterDateTime(a, b DateTime) (bool, error)

AfterDateTime reports whether a is after b as a normalized wall-clock instant.

It uses CompareDateTime, so it performs system-aware comparison rather than lexicographic comparison of stored DateTime fields.

func (System) BeforeClock

func (s System) BeforeClock(a, b Clock) (bool, error)

BeforeClock reports whether a is earlier than b after validating both under the system.

func (System) BeforeDateTime

func (s System) BeforeDateTime(a, b DateTime) (bool, error)

BeforeDateTime reports whether a is before b as a normalized wall-clock instant.

It uses CompareDateTime, so it performs system-aware comparison rather than lexicographic comparison of stored DateTime fields.

func (System) ClockFromDuration

func (s System) ClockFromDuration(d time.Duration) (Clock, error)

ClockFromDuration converts a duration to a Clock validated by the system.

Negative durations and values greater than MaxHour():00:00 return ErrOutOfRange. The maximum duration MaxHour() hours is valid.

func (System) CompareClock

func (s System) CompareClock(a, b Clock) (int, error)

CompareClock compares a and b after validating both under the system.

It returns -1 if a is before b, 1 if a is after b, and 0 if equal.

func (System) CompareDateTime

func (s System) CompareDateTime(a, b DateTime) (int, error)

CompareDateTime performs a system-aware comparison of a and b.

It validates both values and compares normalized wall-clock instants. Equivalent wall-clock instants such as 2026-07-04 30:00 and 2026-07-05 06:00 compare as equal under the default system. It returns -1 if a is before b, 1 if a is after b, and 0 if equal. If normalization would move outside the supported Date range, it returns an error wrapping ErrInvalidDateTime and ErrInvalidDate.

func (System) Config

func (s System) Config() Config

Config returns the configuration used by the system.

func (System) CutoffHour

func (s System) CutoffHour() int

CutoffHour returns the cutoff hour configured for the system.

Local times from 00:00 up to, but not including, cutoffHour belong to the previous business day. For example, when cutoffHour is 6, 02:30 is represented as 26:30 on the previous business date.

func (System) DayRange

func (s System) DayRange(year int, month time.Month, day int, loc *time.Location) (start, end time.Time, err error)

DayRange returns the half-open wall-clock time range for a business date.

It is equivalent to creating the date with NewDate and calling DayRangeDate.

func (System) DayRangeDate

func (s System) DayRangeDate(date Date, loc *time.Location) (start, end time.Time, err error)

DayRangeDate returns the half-open wall-clock time range for a business date.

The range starts at CutoffHour on the calendar date and ends one calendar day later: [start, end). If loc is nil, time.Local is used.

func (System) EqualClock

func (s System) EqualClock(a, b Clock) (bool, error)

EqualClock reports whether a and b are equal after validating both under the system.

func (System) EqualDateTime

func (s System) EqualDateTime(a, b DateTime) (bool, error)

EqualDateTime reports whether a and b are the same normalized wall-clock instant.

It uses CompareDateTime, so it performs system-aware comparison rather than comparing the stored business date and clock fields as DateTime.Equal does.

func (System) FormatClock

func (s System) FormatClock(c Clock, layout ClockLayout) (string, error)

FormatClock validates c under the system and formats it with layout.

Unlike Clock.Format, FormatClock checks that c is valid under the system before formatting.

func (System) FormatDateTime

func (s System) FormatDateTime(dt DateTime, layout DateTimeLayout) (string, error)

FormatDateTime validates dt under the system and formats it with layout.

Unlike DateTime.Format, FormatDateTime checks that dt is valid under the system before formatting.

func (System) FromTime

func (s System) FromTime(t time.Time) (DateTime, error)

FromTime converts a wall-clock time to a business DateTime under the system.

The natural calendar date and clock are taken from t in t's location. If the hour is before CutoffHour, the business date is the previous calendar day and the business hour is increased by 24.

func (System) IsMaxClock

func (s System) IsMaxClock(c Clock) bool

IsMaxClock reports whether c is the maximum clock value allowed by the system.

The maximum clock is MaxHour():00:00.000000000. For example:

cutoffHour = 0  -> 24:00:00 is max
cutoffHour = 6  -> 30:00:00 is max
cutoffHour = 10 -> 34:00:00 is max

IsMaxClock compares stored fields only and does not validate c.

func (System) MaxHour

func (s System) MaxHour() int

MaxHour returns the highest hour value allowed by the system.

It equals 24 plus the cutoff hour. Examples:

cutoffHour = 0  -> maxHour = 24
cutoffHour = 6  -> maxHour = 30
cutoffHour = 10 -> maxHour = 34

func (System) MustClock

func (s System) MustClock(hour, minute, second, nanosecond int) Clock

MustClock returns a Clock with the given components, validated by the system.

It panics if the components are invalid. Intended for tests, constants, and example code.

func (System) MustDateTime

func (s System) MustDateTime(date Date, clock Clock) DateTime

MustDateTime returns a DateTime with the given date and clock, validated by the system.

It panics if date or clock is invalid. Intended for tests, constants, and example code.

func (System) MustParseClock

func (s System) MustParseClock(input string) Clock

MustParseClock parses input as a Clock using auto-detected layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func (System) MustParseClockLayout

func (s System) MustParseClockLayout(layout ClockLayout, input string) Clock

MustParseClockLayout parses input as a Clock using layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func (System) MustParseDateTime

func (s System) MustParseDateTime(input string) DateTime

MustParseDateTime parses input as a DateTime using auto-detected layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func (System) MustParseDateTimeLayout

func (s System) MustParseDateTimeLayout(layout DateTimeLayout, input string) DateTime

MustParseDateTimeLayout parses input as a DateTime using layout.

It panics if parsing fails. Intended for tests, constants, and example code.

func (System) NewClock

func (s System) NewClock(hour, minute, second, nanosecond int) (Clock, error)

NewClock returns a Clock with the given components, validated by the system.

On success it returns the constructed Clock. On failure it returns a zero Clock and an error from ValidateClock.

func (System) NewClockHM

func (s System) NewClockHM(hour, minute int) (Clock, error)

NewClockHM returns a Clock with hour and minute precision (HH:MM).

Second and nanosecond are set to zero. See NewClock for validation rules.

func (System) NewClockHMS

func (s System) NewClockHMS(hour, minute, second int) (Clock, error)

NewClockHMS returns a Clock with hour, minute, and second precision (HH:MM:SS).

Nanosecond is set to zero. See NewClock for validation rules.

func (System) NewDateTime

func (s System) NewDateTime(date Date, clock Clock) (DateTime, error)

NewDateTime returns a DateTime with the given date and clock, validated by the system.

On success it returns the constructed DateTime. On failure it returns a zero DateTime and an error wrapping ErrInvalidDateTime. The wrapped error still supports errors.Is for ErrInvalidDate or ErrInvalidClock.

func (System) ParseClock

func (s System) ParseClock(input string) (Clock, error)

ParseClock parses input as a Clock using auto-detected layout.

It is equivalent to ParseClockLayout(ClockLayoutAuto, input) on the same system.

func (System) ParseClockLayout

func (s System) ParseClockLayout(layout ClockLayout, input string) (Clock, error)

ParseClockLayout parses input as a Clock using layout.

Supported layouts are ClockLayoutAuto, ClockLayoutHM, ClockLayoutHMS, and ClockLayoutNano. An empty input or unrecognized auto-detected format returns ErrParseClock. An unknown layout returns ErrInvalidLayout.

Parsing is lenient. Each field is a decimal integer and leading zeros are not required. For example, ClockLayoutHM accepts "6:30" and "26:5" as well as "06:30". ClockLayoutHMS accepts "26:30:0". ClockLayoutNano accepts fractional seconds with 1 to 9 digits. The input must still match the layout structure: the correct number of colon-separated fields, and a dot before the fractional second in ClockLayoutNano.

ClockLayoutAuto chooses a layout from the input shape:

  • contains "." -> ClockLayoutNano
  • one ":" -> ClockLayoutHM
  • two ":" -> ClockLayoutHMS

After parsing, the clock is validated under the system. Parse and validation failures are returned as *ParseError values that support errors.Is for both ErrParseClock and the wrapped cause.

func (System) ParseDateTime

func (s System) ParseDateTime(input string) (DateTime, error)

ParseDateTime parses input as a DateTime using auto-detected layout.

It is equivalent to ParseDateTimeLayout(DateTimeLayoutAuto, input) on the same system.

func (System) ParseDateTimeLayout

func (s System) ParseDateTimeLayout(layout DateTimeLayout, input string) (DateTime, error)

ParseDateTimeLayout parses input as a DateTime using layout.

Supported layouts are DateTimeLayoutAuto and the Space/T variants with HM, HMS, or Nano clock precision. An empty input or unrecognized auto-detected format returns ErrParseDateTime. An unknown layout returns ErrInvalidLayout.

DateTimeLayoutAuto chooses a layout from the input shape:

  • contains "T" -> T series; otherwise contains " " -> Space series
  • clock contains "." -> Nano
  • clock has one ":" -> HM
  • clock has two ":" -> HMS

The date part is parsed as DateLayoutISO. The clock part is parsed under the system. After parsing, the DateTime is validated with NewDateTime. Parse and validation failures are returned as *ParseError values that support errors.Is for both ErrParseDateTime and the wrapped cause.

func (System) ShiftClockDuration

func (s System) ShiftClockDuration(c Clock, d time.Duration) (clock Clock, dayOffset int, err error)

ShiftClockDuration adds d to c and may shift across business days.

c must be valid under the system. The sum is computed from Clock 00:00, not from the real business-day boundary. When the result falls outside the representable range [00:00:00, MaxHour():00:00], it wraps into an adjacent business day by adding or subtracting 24 hours per day crossed.

dayOffset is 0 when the result stays on the same business day, positive when it advances to a later day, and negative when it moves to an earlier day.

For example, with the default system (max 30:00:00):

29:30 + 30m -> 30:00, dayOffset 0
29:30 + 1h  -> 06:30, dayOffset 1
30:00 + 0   -> 30:00, dayOffset 0
30:00 + 1ns -> 06:00:00.000000001, dayOffset 1

func (System) SubClock

func (s System) SubClock(a, b Clock) (time.Duration, error)

SubClock returns the duration from b to a (a - b).

The result is a time.Duration and may be positive, zero, or negative. It does not wrap or return a Clock.

For example:

27:00 - 26:30 = 30m
26:30 - 27:00 = -30m

Both clocks must be valid under the system.

func (System) SubClockDurationBounded

func (s System) SubClockDurationBounded(c Clock, d time.Duration) (Clock, error)

SubClockDurationBounded subtracts d from c without wrapping across a business day.

c must be valid under the system. The difference is computed from Clock 00:00; if the result is negative or exceeds MaxHour():00:00, SubClockDurationBounded returns ErrOutOfRange. There is no wrap-around.

For example, with the default system (max 30:00:00):

00:30 - 1h -> error

Use UnshiftClockDuration when the result should wrap to an adjacent business day.

func (System) SubDateTime

func (s System) SubDateTime(a, b DateTime) (time.Duration, error)

SubDateTime returns the elapsed business time from b to a (a - b).

The result is computed from stored business date fields plus extended-hour clock fields; it does not convert values with ToTime. This matches normalized wall-clock equivalence for adjacent extended-hour representations: 2026-07-04 30:00 and 2026-07-05 06:00 differ by zero. If the difference cannot be represented as a time.Duration, SubDateTime returns ErrOutOfRange.

func (System) SubDateTimeDuration

func (s System) SubDateTimeDuration(dt DateTime, d time.Duration) (DateTime, error)

SubDateTimeDuration subtracts d from dt in business date and extended-hour clock space.

The result may cross to an adjacent business day. Use SubDateTimeDurationBounded when the result must stay on the same business day.

func (System) SubDateTimeDurationBounded

func (s System) SubDateTimeDurationBounded(dt DateTime, d time.Duration) (DateTime, error)

SubDateTimeDurationBounded subtracts d from dt without shifting to another business day.

If the result would move to an adjacent business day, it returns ErrOutOfRange. For example, with the default system:

2026-07-04 00:30 - 1h -> error

func (System) ToTime

func (s System) ToTime(dt DateTime, loc *time.Location) (time.Time, error)

ToTime converts a business DateTime to a wall-clock time in loc.

If loc is nil, time.Local is used. When the business hour is 24 or greater, the real calendar date is the next day and the real hour is reduced by 24. If that normalization would move outside the supported Date range, ToTime returns an error wrapping ErrInvalidDateTime and ErrInvalidDate.

func (System) UnshiftClockDuration

func (s System) UnshiftClockDuration(c Clock, d time.Duration) (clock Clock, dayOffset int, err error)

UnshiftClockDuration subtracts d from c and may shift across business days.

c must be valid under the system. When the result falls outside the representable range, it wraps into an adjacent business day by adding or subtracting 24 hours per day crossed.

For example, with the default system (max 30:00:00):

00:30 - 1h -> 23:30, dayOffset -1

func (System) ValidClock

func (s System) ValidClock(c Clock) bool

ValidClock reports whether c is a valid clock value under the system.

It is equivalent to s.ValidateClock(c) == nil.

func (System) ValidDateTime

func (s System) ValidDateTime(dt DateTime) bool

ValidDateTime reports whether dt is valid under the system.

It is equivalent to s.ValidateDateTime(dt) == nil.

func (System) ValidateClock

func (s System) ValidateClock(c Clock) error

ValidateClock reports whether c is a valid clock value under the system.

A valid clock satisfies:

  • 0 <= hour <= MaxHour()
  • 0 <= minute, second <= 59
  • 0 <= nanosecond <= 999_999_999
  • when hour equals MaxHour(), minute, second, and nanosecond are 0

For example, when MaxHour() is 30, 30:00:00 is valid but 30:00:01 is not.

Returns nil if c is valid. Otherwise returns ErrInvalidConfig, ErrOutOfRange, or ErrInvalidClock.

func (System) ValidateDateTime

func (s System) ValidateDateTime(dt DateTime) error

ValidateDateTime reports whether dt is valid under the system.

It validates the date and clock components. On failure it returns an error wrapping ErrInvalidDateTime. The wrapped error still supports errors.Is for ErrInvalidDate or ErrInvalidClock. A DateTime can be valid here but still fail system-aware normalization, such as converting an extended-hour value on MaxDateYear-12-31 to wall-clock time.

Jump to

Keyboard shortcuts

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