Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor date handling to use CivilDate instead of #21

Merged
merged 1 commit into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions civil.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"github.com/Code-Hex/synchro/tz"
)

// A Date represents a date (year, month, day).
type Date[T TimeZone] struct {
// A CivilDate represents a date (year, month, day).
type CivilDate[T TimeZone] struct {
Year int // Year (e.g., 2014).
Month time.Month // Month of the year (January = 1, ...).
Day int // Day of the month, starting at 1.
Expand All @@ -22,37 +22,37 @@ type Date[T TimeZone] struct {
var _ interface {
encoding.TextMarshaler
encoding.TextUnmarshaler
} = (*Date[tz.UTC])(nil)
} = (*CivilDate[tz.UTC])(nil)

// DateOf returns the Date in which a time occurs in that time's location.
func DateOf[T TimeZone, U Time[T] | time.Time](t U) Date[T] {
var d Date[T]
// CivilDateOf returns the CivilDate in which a time occurs in that time's location.
func CivilDateOf[T TimeZone, U Time[T] | time.Time](t U) CivilDate[T] {
var d CivilDate[T]
st, _ := convertTime[T](t) // No returns error if Time[T] | time.Time
d.Year, d.Month, d.Day = st.Date()
return d
}

// ParseDate parses a string in ISO 8601 full-date format and returns the date value it represents.
func ParseDate[T TimeZone, U constraints.Bytes](s U) (Date[T], error) {
// ParseCivilDate parses a string in ISO 8601 full-date format and returns the date value it represents.
func ParseCivilDate[T TimeZone, U constraints.Bytes](s U) (CivilDate[T], error) {
t, err := iso8601.ParseDate(s)
if err != nil {
return Date[T]{}, err
return CivilDate[T]{}, err
}
return DateOf[T](t.Date().StdTime()), nil
return CivilDateOf[T](t.Date().StdTime()), nil
}

// String returns the date in ISO 8601 full-date format.
func (d Date[T]) String() string {
func (d CivilDate[T]) String() string {
return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
}

// Time converts Date[T] to Time[T].
func (d Date[T]) Time() Time[T] {
func (d CivilDate[T]) Time() Time[T] {
return New[T](d.Year, d.Month, d.Day, 0, 0, 0, 0)
}

// IsValid reports whether the date is valid.
func (d Date[T]) IsValid() bool {
func (d CivilDate[T]) IsValid() bool {
iso := iso8601.Date{
Year: d.Year,
Month: d.Month,
Expand All @@ -63,27 +63,27 @@ func (d Date[T]) IsValid() bool {

// AddDate returns the time corresponding to adding the
// given number of years, months, and days to d.
func (d Date[T]) AddDate(years int, months int, days int) Date[T] {
return DateOf[T](d.Time().AddDate(years, months, days))
func (d CivilDate[T]) AddDate(years int, months int, days int) CivilDate[T] {
return CivilDateOf[T](d.Time().AddDate(years, months, days))
}

// AddDays returns the date that is n days in the future.
// n can also be negative to go into the past.
func (d Date[T]) AddDays(n int) Date[T] {
func (d CivilDate[T]) AddDays(n int) CivilDate[T] {
return d.AddDate(0, 0, n)
}

// DaysSince returns the signed number of days between the date and s, not including the end day.
// This is the inverse operation to AddDays.
func (d Date[T]) DaysSince(s Date[T]) (days int) {
func (d CivilDate[T]) DaysSince(s CivilDate[T]) (days int) {
// We convert to Unix time so we do not have to worry about leap seconds:
// Unix time increases by exactly 86400 seconds per day.
deltaUnix := d.Time().Unix() - s.Time().Unix()
return int(deltaUnix / 86400)
}

// Before reports whether d occurs before d2.
func (d Date[T]) Before(d2 Date[T]) bool {
func (d CivilDate[T]) Before(d2 CivilDate[T]) bool {
if d.Year != d2.Year {
return d.Year < d2.Year
}
Expand All @@ -94,39 +94,39 @@ func (d Date[T]) Before(d2 Date[T]) bool {
}

// After reports whether d occurs after d2.
func (d Date[T]) After(d2 Date[T]) bool {
func (d CivilDate[T]) After(d2 CivilDate[T]) bool {
return d2.Before(d)
}

// IsZero reports whether date fields are set to their default value.
func (d Date[T]) IsZero() bool {
func (d CivilDate[T]) IsZero() bool {
return (d.Year == 0) && (int(d.Month) == 0) && (d.Day == 0)
}

// MarshalText implements the encoding.TextMarshaler interface.
// The output is the result of d.String().
func (d Date[T]) MarshalText() ([]byte, error) {
func (d CivilDate[T]) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}

// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The date is expected to be a string in a format accepted by ParseDate.
func (d *Date[T]) UnmarshalText(data []byte) error {
// The date is expected to be a string in a format accepted by ParseCivilDate.
func (d *CivilDate[T]) UnmarshalText(data []byte) error {
var err error
*d, err = ParseDate[T](string(data))
*d, err = ParseCivilDate[T](string(data))
return err
}

// Change modifies the date based on the provided unit values.
//
// Note: Units related to time are ignored.
func (t Date[T]) Change(u1 Unit, u2 ...Unit) Date[T] {
return DateOf[T](t.Time().Change(u1, u2...))
func (t CivilDate[T]) Change(u1 Unit, u2 ...Unit) CivilDate[T] {
return CivilDateOf[T](t.Time().Change(u1, u2...))
}

// Advance adjusts the date based on the provided unit values, moving it forward in date.
//
// Note: Units related to time are ignored.
func (t Date[T]) Advance(u1 Unit, u2 ...Unit) Date[T] {
return DateOf[T](t.Time().Advance(u1, u2...))
func (t CivilDate[T]) Advance(u1 Unit, u2 ...Unit) CivilDate[T] {
return CivilDateOf[T](t.Time().Advance(u1, u2...))
}
Loading