civiltime

package module
v0.1.2 Latest Latest
Warning

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

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

README

civiltime

civiltime 提供不带时区的民用时间值:DateTimeDateTime

它适合表示生日、营业时间、预约时间、PostgreSQL timestamp without time zone 等值。这些值本身不代表 UTC 时间,也不应被隐式转换成 UTC;只有在已知业务地点时,才调用 DateTime.In(location) 转成 time.Time

dt, err := civiltime.ParseDateTime("2026-01-31T18:30:00")
if err != nil {
	return err
}

loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
	return err
}
instant := dt.In(loc)

特性:

  • 只依赖 Go 标准库;
  • 严格解析和规范化格式化;
  • 实现 encoding.TextMarshalerencoding/jsondatabase/sql.Scannerdriver.Valuer
  • DateTime.In 显式接收时区;
  • Scan(nil) 和 JSON null 返回 ErrNull,避免把空值悄悄变成零值;需要空值时使用 NullDateNullTimeNullDateTime
  • Date.AddMonthsDateTime.AddMonths 对月底进行安全截断;DateTime.Add / Sub 支持跨日算术和时间差。

当前版本刻意不包含数据库驱动、时区数据库和周期规则。具体数据库驱动可直接使用 Scanner / Valuer,无需成为核心依赖;需要把数据库边界显式隔离时,使用 sqladapter,通过自定义 Codec 接入驱动专属格式。

var when civiltime.DateTime
if err := row.Scan(sqladapter.DateTime(&when)); err != nil {
	return err
}

时间转换与算术边界

Date.In / DateTime.In 与标准库 time.Date 保持一致:夏令时跳变或时区规则调整可能使当地时间不存在(gap)或出现两次(fold), 转换可能改变日期/时钟字段,也不保证在重复时间中选择哪一个 offset。 例如纽约 2024-03-10 02:30 不存在,而 2024-11-03 01:30 出现两次; 某些时区还会跳过午夜或整日。字段往返一致不能证明这个时间唯一。 需要唯一预约时刻的应用必须另行确定歧义处理规则;本库不隐式替用户选择策略。 无效日期/时间或 nil 地点调用 In 会 panic,应先校验输入。

有效年份为 00009999AddDaysAddMonthsDateTime.Add 不把越界结果 截到年份端点;结果可能是无效值,需要再次检查 IsValid()。例如 9999-12-31 加一天会得到无效日期,String 返回无效标记,Value 返回校验错误。 无效输入做上述算术会保持原值;Sub 任一输入无效时返回零。 AddMonths 的月底截断保持不变,例如 2024-01-31 加一个月为 2024-02-29

民用时间的每一天固定为 24 小时,Add / Sub 不计算 DST 后实际经过的时长。 Sub 超过 time.Duration 范围时与标准库一样饱和,因此不能用它除以 24 小时 计算任意年份跨度的精确日差。

设计参考

  • 参考 set:优先标准库,值类型 API 保持小而直接。
  • 参考 dict_trans:把数据库读写放在 Scan / Value 边界,不把具体驱动带进核心包。
  • 参考 distsync:保留明确的错误哨兵,调用者可以用 errors.Is 判断失败类别。

Documentation

Overview

Package civiltime represents calendar values that do not identify a unique instant. Use DateTime.In only at a boundary where a location is known.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidDate reports a date with an impossible calendar value.
	ErrInvalidDate = errors.New("civiltime: invalid date")
	// ErrInvalidTime reports a time with an impossible clock value.
	ErrInvalidTime = errors.New("civiltime: invalid time")
	// ErrInvalidDateTime reports a DateTime containing an invalid Date or Time.
	ErrInvalidDateTime = errors.New("civiltime: invalid datetime")
	// ErrNull reports an attempt to scan or unmarshal null into a non-nullable value.
	ErrNull = errors.New("civiltime: null is not allowed")
)

Functions

This section is empty.

Types

type Date

type Date struct {
	Year  int
	Month stdtime.Month
	Day   int
}

Date is a calendar date without location or clock-time information. Valid years range from 0 to 9999; the zero value is not a valid date.

func DateOf

func DateOf(t stdtime.Time) Date

DateOf extracts the calendar date in t's location.

func ParseDate

func ParseDate(s string) (Date, error)

ParseDate parses YYYY-MM-DD.

func (Date) AddDays

func (d Date) AddDays(n int) Date

AddDays returns d shifted by n calendar days. Invalid dates are returned unchanged. Results outside years 0 through 9999 are invalid; check IsValid before use.

func (Date) AddMonths

func (d Date) AddMonths(n int) Date

AddMonths returns d shifted by n calendar months. If the target month has fewer days, the result is clamped to that month's last day. Invalid inputs are returned unchanged. Results outside years 0 through 9999 are invalid; check IsValid before use.

func (Date) After

func (d Date) After(other Date) bool

After reports whether d comes after other.

func (Date) Before

func (d Date) Before(other Date) bool

Before reports whether d comes before other.

func (Date) Compare

func (d Date) Compare(other Date) int

Compare returns -1, 0, or 1 according to d's order relative to other.

func (Date) In

func (d Date) In(loc *stdtime.Location) stdtime.Time

In returns midnight on d in loc. It panics when d or loc is invalid. Missing or repeated midnights follow time.Date, which may return a different civil date and does not guarantee which offset is chosen at a transition.

func (Date) IsValid

func (d Date) IsValid() bool

IsValid reports whether d is a real date representable by String.

func (Date) IsZero

func (d Date) IsZero() bool

IsZero reports whether d is its zero value. The zero value is not a valid date.

func (Date) MarshalJSON

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

MarshalJSON encodes d as a JSON string.

func (Date) MarshalText

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

MarshalText implements encoding.TextMarshaler.

func (*Date) Scan

func (d *Date) Scan(src any) error

Scan implements database/sql.Scanner. SQL NULL is rejected; use a nullable database field type when NULL is part of the schema.

func (Date) String

func (d Date) String() string

String returns d in canonical YYYY-MM-DD form, or <invalid-date>.

func (*Date) UnmarshalJSON

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

UnmarshalJSON decodes a JSON date string. null is rejected.

func (*Date) UnmarshalText

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

UnmarshalText implements encoding.TextUnmarshaler.

func (Date) Value

func (d Date) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

func (Date) Weekday

func (d Date) Weekday() stdtime.Weekday

Weekday returns the day of the week for d. It returns the zero value for an invalid date.

type DateTime

type DateTime struct {
	Date Date
	Time Time
}

DateTime combines a Date and a Time without location information. It does not identify a unique instant until converted with In.

func DateTimeOf

func DateTimeOf(t stdtime.Time) DateTime

DateTimeOf extracts the civil date and time in t's location.

func ParseDateTime

func ParseDateTime(s string) (DateTime, error)

ParseDateTime parses YYYY-MM-DDTHH:MM:SS[.fraction]. A lower-case t or a space is also accepted for database text formats.

func (DateTime) Add

func (dt DateTime) Add(d stdtime.Duration) DateTime

Add returns dt shifted by d. The arithmetic uses fixed 24-hour civil days; no timezone or daylight-saving rule is involved. Invalid inputs are returned unchanged. Results outside years 0 through 9999 are invalid; check IsValid before use.

func (DateTime) AddDays

func (dt DateTime) AddDays(n int) DateTime

AddDays returns dt shifted by n calendar days. Invalid values are returned unchanged. Results outside years 0 through 9999 are invalid; check IsValid before use.

func (DateTime) AddMonths

func (dt DateTime) AddMonths(n int) DateTime

AddMonths returns dt shifted by n calendar months while preserving its time. Invalid inputs are returned unchanged. Results outside years 0 through 9999 are invalid; check IsValid before use.

func (DateTime) After

func (dt DateTime) After(other DateTime) bool

After reports whether dt comes after other.

func (DateTime) Before

func (dt DateTime) Before(other DateTime) bool

Before reports whether dt comes before other.

func (DateTime) Compare

func (dt DateTime) Compare(other DateTime) int

Compare returns -1, 0, or 1 according to dt's order relative to other.

func (DateTime) In

func (dt DateTime) In(loc *stdtime.Location) stdtime.Time

In converts dt to a time in loc. The conversion is explicit because dt has no timezone of its own; DST rules are therefore applied by time.Date. Missing or repeated local times follow time.Date, which may change the civil fields and does not guarantee which offset is chosen at a transition. A field-preserving round trip does not prove that the local time is unique. In panics if dt is invalid or loc is nil.

func (DateTime) IsValid

func (dt DateTime) IsValid() bool

IsValid reports whether both components are valid.

func (DateTime) IsZero

func (dt DateTime) IsZero() bool

IsZero reports whether both components are zero values.

func (DateTime) MarshalJSON

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

MarshalJSON encodes dt as a JSON string.

func (DateTime) MarshalText

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

MarshalText implements encoding.TextMarshaler.

func (*DateTime) Scan

func (dt *DateTime) Scan(src any) error

Scan implements database/sql.Scanner. A time.Time is read by its calendar fields; its location and offset are deliberately ignored.

func (DateTime) String

func (dt DateTime) String() string

String returns dt in canonical YYYY-MM-DDTHH:MM:SS[.fraction] form, or <invalid-datetime>.

func (DateTime) Sub

func (dt DateTime) Sub(other DateTime) stdtime.Duration

Sub returns the civil-time difference dt-other. Invalid values return zero. Like time.Time.Sub, an unrepresentable result is saturated to the limits of time.Duration.

func (*DateTime) UnmarshalJSON

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

UnmarshalJSON decodes a JSON datetime string. null is rejected.

func (*DateTime) UnmarshalText

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

UnmarshalText implements encoding.TextUnmarshaler.

func (DateTime) Value

func (dt DateTime) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type NullDate

type NullDate struct {
	Date  Date
	Valid bool
}

NullDate represents a Date that may be SQL NULL or JSON null.

func (NullDate) MarshalJSON

func (n NullDate) MarshalJSON() ([]byte, error)

MarshalJSON encodes an invalid NullDate as JSON null.

func (*NullDate) Scan

func (n *NullDate) Scan(src any) error

Scan implements database/sql.Scanner.

func (NullDate) String

func (n NullDate) String() string

String returns the date or an empty string when invalid.

func (*NullDate) UnmarshalJSON

func (n *NullDate) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a date string or JSON null.

func (NullDate) Value

func (n NullDate) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type NullDateTime

type NullDateTime struct {
	DateTime DateTime
	Valid    bool
}

NullDateTime represents a DateTime that may be SQL NULL or JSON null.

func (NullDateTime) MarshalJSON

func (n NullDateTime) MarshalJSON() ([]byte, error)

MarshalJSON encodes an invalid NullDateTime as JSON null.

func (*NullDateTime) Scan

func (n *NullDateTime) Scan(src any) error

Scan implements database/sql.Scanner.

func (NullDateTime) String

func (n NullDateTime) String() string

String returns the datetime or an empty string when invalid.

func (*NullDateTime) UnmarshalJSON

func (n *NullDateTime) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a datetime string or JSON null.

func (NullDateTime) Value

func (n NullDateTime) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type NullTime

type NullTime struct {
	Time  Time
	Valid bool
}

NullTime represents a Time that may be SQL NULL or JSON null.

func (NullTime) MarshalJSON

func (n NullTime) MarshalJSON() ([]byte, error)

MarshalJSON encodes an invalid NullTime as JSON null.

func (*NullTime) Scan

func (n *NullTime) Scan(src any) error

Scan implements database/sql.Scanner.

func (NullTime) String

func (n NullTime) String() string

String returns the time or an empty string when invalid.

func (*NullTime) UnmarshalJSON

func (n *NullTime) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a time string or JSON null.

func (NullTime) Value

func (n NullTime) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

type Time

type Time struct {
	Hour       int
	Minute     int
	Second     int
	Nanosecond int
}

Time is a wall-clock time without location information.

func ParseTime

func ParseTime(s string) (Time, error)

ParseTime parses HH:MM:SS with an optional fractional part of one to nine digits.

func TimeOf

func TimeOf(t stdtime.Time) Time

TimeOf extracts the wall-clock time in t's location.

func (Time) After

func (t Time) After(other Time) bool

After reports whether t comes after other.

func (Time) Before

func (t Time) Before(other Time) bool

Before reports whether t comes before other.

func (Time) Compare

func (t Time) Compare(other Time) int

Compare returns -1, 0, or 1 according to t's order relative to other.

func (Time) IsValid

func (t Time) IsValid() bool

IsValid reports whether t is a valid 24-hour clock time.

func (Time) IsZero

func (t Time) IsZero() bool

IsZero reports whether t is its zero value. Midnight is also the zero value.

func (Time) MarshalJSON

func (t Time) MarshalJSON() ([]byte, error)

MarshalJSON encodes t as a JSON string.

func (Time) MarshalText

func (t Time) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (*Time) Scan

func (t *Time) Scan(src any) error

Scan implements database/sql.Scanner. SQL NULL is rejected.

func (Time) String

func (t Time) String() string

String returns t in canonical HH:MM:SS[.fraction] form, or <invalid-time>.

func (*Time) UnmarshalJSON

func (t *Time) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON time string. null is rejected.

func (*Time) UnmarshalText

func (t *Time) UnmarshalText(data []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (Time) Value

func (t Time) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer.

Directories

Path Synopsis
Package sqladapter adapts domain values to database/sql.
Package sqladapter adapts domain values to database/sql.

Jump to

Keyboard shortcuts

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