Skip to content

Commit

Permalink
Merge pull request #11 from Code-Hex/add/change-advance
Browse files Browse the repository at this point in the history
added Advance and Change
  • Loading branch information
Code-Hex committed Sep 12, 2023
2 parents 05331da + 920f46f commit fdddae0
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ If you have a feature request, please open an issue. It would be great if you co
- [IsBetween](https://pkg.go.dev/github.com/Code-Hex/synchro#Time.IsBetween)
- [IsLeapYear](https://pkg.go.dev/github.com/Code-Hex/synchro#Time.IsLeapYear)
- [DiffInCalendarDays](https://pkg.go.dev/github.com/Code-Hex/synchro#Time.DiffInCalendarDays)
- [Change](https://pkg.go.dev/github.com/Code-Hex/synchro#Time.Change)
- `Change` allows you to specify the date and time components you want to change and make modifications.
- [Advance](https://pkg.go.dev/github.com/Code-Hex/synchro#Time.Advance)
- `Advance` allows you to specify the date and time components you want to increment and make modifications.


## TODO
Expand Down
221 changes: 221 additions & 0 deletions builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package synchro

import (
"time"

"github.com/Code-Hex/synchro/tz"
)

func toPtr[T any](x T) *T {
return &x
}

// TimeBuilder defines an interface for constructing a Time[T] value
// with specific date and time components using method chaining.
//
// Implementations should allow for setting each component step by step,
// and then finalizing the construction with the Do() method.
type TimeBuilder[T TimeZone] interface {
// Year sets the year component of the time being built.
Year(int) TimeBuilder[T]

// Month sets the month component of the time being built.
Month(time.Month) TimeBuilder[T]

// Day sets the day component of the time being built.
Day(int) TimeBuilder[T]

// Hour sets the hour component of the time being built.
Hour(int) TimeBuilder[T]

// Minute sets the minute component of the time being built.
Minute(int) TimeBuilder[T]

// Second sets the second component of the time being built.
Second(int) TimeBuilder[T]

// Nanosecond sets the nanosecond component of the time being built.
Nanosecond(int) TimeBuilder[T]

// Do finalizes and returns the constructed Time value
// based on the components set in the chain.
Do() Time[T]
}

type changeBuilder[T TimeZone] struct {
year *int
month *time.Month
day *int
hour *int
minute *int
second *int
nsec *int

t Time[T]
}

var _ TimeBuilder[tz.UTC] = changeBuilder[tz.UTC]{}

// Year implements the TimeBuilder interface to change years.
func (c changeBuilder[T]) Year(year int) TimeBuilder[T] {
c.year = toPtr(year)
return c
}

// Month implements the TimeBuilder interface to change months.
func (c changeBuilder[T]) Month(month time.Month) TimeBuilder[T] {
c.month = toPtr(month)
return c
}

// Day implements the TimeBuilder interface to change days.
func (c changeBuilder[T]) Day(day int) TimeBuilder[T] {
c.day = toPtr(day)
return c
}

// Hour implements the TimeBuilder interface to change hours.
func (c changeBuilder[T]) Hour(hour int) TimeBuilder[T] {
c.hour = toPtr(hour)
return c
}

// Minute implements the TimeBuilder interface to change minutes.
func (c changeBuilder[T]) Minute(minute int) TimeBuilder[T] {
c.minute = toPtr(minute)
return c
}

// Second implements the TimeBuilder interface to change seconds.
func (c changeBuilder[T]) Second(second int) TimeBuilder[T] {
c.second = toPtr(second)
return c
}

// Nanosecond implements the TimeBuilder interface to change nanoseconds.
func (c changeBuilder[T]) Nanosecond(nsec int) TimeBuilder[T] {
c.nsec = toPtr(nsec)
return c
}

// Do implements the TimeBuilder interface to change any date and time components.
func (c changeBuilder[T]) Do() Time[T] {
year, month, day := c.t.Date()
hour, min, sec := c.t.Clock()
nsec := c.t.Nanosecond()
if c.year != nil {
year = *c.year
}
if c.month != nil {
month = *c.month
}
if c.day != nil {
day = *c.day
}
if c.hour != nil {
hour = *c.hour
}
if c.minute != nil {
min = *c.minute
}
if c.second != nil {
sec = *c.second
}
if c.nsec != nil {
nsec = *c.nsec
}
return New[T](year, month, day, hour, min, sec, nsec)
}

// Change initializes and returns a TimeBuilder for the current Time value.
//
// This provides a method chain approach for specifying which parts of the time
// you want to change, allowing for the creation of a new Time[T] instance
// with the specified modifications.
func (t Time[T]) Change() TimeBuilder[T] {
return changeBuilder[T]{t: t}
}

type advanceBuilder[T TimeZone] struct {
year int
month time.Month
day int
hour int
minute int
second int
nsec int

t Time[T]
}

var _ TimeBuilder[tz.UTC] = advanceBuilder[tz.UTC]{}

// Advance initializes and returns a TimeBuilder for the current Time value.
//
// This provides a method chain approach for specifying which parts of the time
// you want to increment, allowing for the creation of a new Time[T] instance
// with the specified modifications.
func (t Time[T]) Advance() TimeBuilder[T] {
return advanceBuilder[T]{t: t}
}

// Year implements the TimeBuilder interface to increment years.
func (a advanceBuilder[T]) Year(year int) TimeBuilder[T] {
a.year += year
return a
}

// Month implements the TimeBuilder interface to increment months.
func (a advanceBuilder[T]) Month(month time.Month) TimeBuilder[T] {
a.month += month
return a
}

// Day implements the TimeBuilder interface to increment days.
func (a advanceBuilder[T]) Day(day int) TimeBuilder[T] {
a.day += day
return a
}

// Hour implements the TimeBuilder interface to increment hours.
func (a advanceBuilder[T]) Hour(hour int) TimeBuilder[T] {
a.hour += hour
return a
}

// Minute implements the TimeBuilder interface to increment minutes.
func (a advanceBuilder[T]) Minute(minute int) TimeBuilder[T] {
a.minute += minute
return a
}

// Second implements the TimeBuilder interface to increment seconds.
func (a advanceBuilder[T]) Second(second int) TimeBuilder[T] {
a.second += second
return a
}

// Nanosecond implements the TimeBuilder interface to increment nanoseconds.
func (a advanceBuilder[T]) Nanosecond(nsec int) TimeBuilder[T] {
a.nsec += nsec
return a
}

// Do implements the TimeBuilder interface to increment any date and time components.
func (a advanceBuilder[T]) Do() Time[T] {
t := a.t.AddDate(a.year, int(a.month), a.day)

if a.hour != 0 {
t = t.Add(time.Hour * time.Duration(a.hour))
}
if a.minute != 0 {
t = t.Add(time.Minute * time.Duration(a.minute))
}
if a.second != 0 {
t = t.Add(time.Second * time.Duration(a.second))
}
if a.nsec != 0 {
t = t.Add(time.Duration(a.nsec))
}
return t
}
120 changes: 120 additions & 0 deletions builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package synchro_test

import (
"fmt"
"testing"
"time"

"github.com/Code-Hex/synchro"
"github.com/Code-Hex/synchro/tz"
)

func ExampleTime_Change() {
utc := synchro.New[tz.UTC](2009, time.November, 10, 23, 0, 0, 0)
c1 := utc.Change().Year(2010).Do()
c2 := utc.Change().Year(2010).Month(time.December).Do()
c3 := utc.Change().Year(2010).Month(time.December).Day(1).Do()
c4 := c3.Change().Hour(1).Do()
c5 := c3.Change().Hour(1).Minute(1).Do()
c6 := c3.Change().Hour(1).Minute(1).Second(1).Do()
c7 := c3.Change().Hour(1).Minute(1).Second(1).Nanosecond(123456789).Do()
fmt.Printf("Go launched at %s\n", utc)
fmt.Println(c1)
fmt.Println(c2)
fmt.Println(c3)
fmt.Println(c4)
fmt.Println(c5)
fmt.Println(c6)
fmt.Println(c7)
// Output:
// Go launched at 2009-11-10 23:00:00 +0000 UTC
// 2010-11-10 23:00:00 +0000 UTC
// 2010-12-10 23:00:00 +0000 UTC
// 2010-12-01 23:00:00 +0000 UTC
// 2010-12-01 01:00:00 +0000 UTC
// 2010-12-01 01:01:00 +0000 UTC
// 2010-12-01 01:01:01 +0000 UTC
// 2010-12-01 01:01:01.123456789 +0000 UTC
}

func ExampleTime_Advance() {
utc := synchro.New[tz.UTC](2009, time.November, 10, 23, 0, 0, 0)
c1 := utc.Advance().Year(1).Do()
c11 := utc.Advance().Year(1).Year(1).Do() // +2 years

c2 := utc.Advance().Year(1).Month(1).Do()
c3 := utc.Advance().Year(1).Month(1).Day(1).Do()
c4 := c3.Advance().Hour(1).Do()
c5 := c3.Advance().Hour(1).Minute(1).Do()
c6 := c3.Advance().Hour(1).Minute(1).Second(1).Do()
c7 := c3.Advance().Hour(1).Minute(1).Second(1).Nanosecond(123456789).Do()

fmt.Printf("Go launched at %s\n", utc)
fmt.Println(c1)
fmt.Println(c11)
fmt.Println()
fmt.Println(c2)
fmt.Println(c3)
fmt.Println(c4)
fmt.Println(c5)
fmt.Println(c6)
fmt.Println(c7)
// Output:
// Go launched at 2009-11-10 23:00:00 +0000 UTC
// 2010-11-10 23:00:00 +0000 UTC
// 2011-11-10 23:00:00 +0000 UTC
//
// 2010-12-10 23:00:00 +0000 UTC
// 2010-12-11 23:00:00 +0000 UTC
// 2010-12-12 00:00:00 +0000 UTC
// 2010-12-12 00:01:00 +0000 UTC
// 2010-12-12 00:01:01 +0000 UTC
// 2010-12-12 00:01:01.123456789 +0000 UTC
}

func TestAdvance(t *testing.T) {
utc := synchro.New[tz.UTC](2009, time.November, 10, 23, 0, 0, 0)

t.Run("month twice", func(t *testing.T) {
got := utc.Advance().Month(1).Month(2).Do() // +3 months
want := synchro.New[tz.UTC](2010, time.February, 10, 23, 0, 0, 0)
if want != got {
t.Fatalf("- %s\n+ %s", want, got)
}
})
t.Run("day twice", func(t *testing.T) {
got := utc.Advance().Day(1).Day(2).Do() // +3 days
want := synchro.New[tz.UTC](2009, time.November, 13, 23, 0, 0, 0)
if want != got {
t.Fatalf("- %s\n+ %s", want, got)
}
})
t.Run("hour twice", func(t *testing.T) {
got := utc.Advance().Hour(1).Hour(2).Do() // +3 hours
want := synchro.New[tz.UTC](2009, time.November, 11, 2, 0, 0, 0)
if want != got {
t.Fatalf("- %s\n+ %s", want, got)
}
})
t.Run("minute twice", func(t *testing.T) {
got := utc.Advance().Minute(5).Minute(60).Do() // +65 minutes
want := synchro.New[tz.UTC](2009, time.November, 11, 0, 5, 0, 0)
if want != got {
t.Fatalf("- %s\n+ %s", want, got)
}
})
t.Run("second twice", func(t *testing.T) {
got := utc.Advance().Second(5).Second(60).Do() // +65 seconds
want := synchro.New[tz.UTC](2009, time.November, 10, 23, 1, 5, 0)
if want != got {
t.Fatalf("- %s\n+ %s", want, got)
}
})
t.Run("nanosec twice", func(t *testing.T) {
got := utc.Advance().Nanosecond(5).Nanosecond(60).Do() // +65 nanosec
want := synchro.New[tz.UTC](2009, time.November, 10, 23, 0, 0, 65)
if want != got {
t.Fatalf("- %s\n+ %s", want, got)
}
})
}
2 changes: 1 addition & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func ExampleNowContext() {
// 0001-01-01 00:00:00 +0000 UTC
}

func ExampleDate() {
func ExampleNew() {
utc := synchro.New[tz.UTC](2009, time.November, 10, 23, 0, 0, 0)
fmt.Printf("Go launched at %s\n", utc)
// Output:
Expand Down

0 comments on commit fdddae0

Please sign in to comment.