diff --git a/README.md b/README.md index b802bbb..8589557 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ -Duration [![Build](https://travis-ci.org/senseyeio/duration.svg?branch=master)](https://travis-ci.org/senseyeio/duration) [![Coverage](https://coveralls.io/repos/github/senseyeio/duration/badge.svg?branch=master)](https://coveralls.io/github/senseyeio/duration?branch=master) [![Go Report Card](https://goreportcard.com/badge/senseyeio/duration)](https://goreportcard.com/report/senseyeio/duration) [![GoDoc](https://godoc.org/github.com/senseyeio/duration?status.svg)](https://godoc.org/github.com/senseyeio/duration) -======= -Parse ISO8601 duration strings, and use to shift dates/times. +# Duration [![Build](https://travis-ci.org/senseyeio/duration.svg?branch=master)](https://travis-ci.org/senseyeio/duration) [![Coverage](https://coveralls.io/repos/github/senseyeio/duration/badge.svg?branch=master)](https://coveralls.io/github/senseyeio/duration?branch=master) [![Go Report Card](https://goreportcard.com/badge/senseyeio/duration)](https://goreportcard.com/report/senseyeio/duration) [![GoDoc](https://godoc.org/github.com/senseyeio/duration?status.svg)](https://godoc.org/github.com/senseyeio/duration) -Basic Example -------------- +This is a fork of the original `duration` package with added functionality to support shifting dates/times both forward and backward. + +## Basic Example ```go package main @@ -19,8 +18,10 @@ func main() { d, _ := iso8601.ParseISO8601("P1D") today := time.Now() tomorrow := d.Shift(today) + yesterday := d.Unshift(today) fmt.Println(today.Format("Jan _2")) fmt.Println(tomorrow.Format("Jan _2")) + fmt.Println(yesterday.Format("Jan _2")) } ``` @@ -82,3 +83,31 @@ Nov 2, 2006 Dec 2, 2006 Jan 2, 2007 ``` + + +Additional Functionality +--------------------------- +This fork includes an Unshift method that complements the Shift functionality. It returns a time.Time shifted back by the duration from the given start. + +```go +// UnShift returns a time.Time, shifted back by the duration from the given start. +// +// NB: UnShift uses time.AddDate for years, months, weeks, and days, and so +// shares its limitations. In particular, shifting back by months is not recommended +// unless the start date is before the 28th of the month. Otherwise, dates will +// roll over, e.g. Oct 1 - P1M = Aug 31. +// +// Week and Day values will be combined as W*7 + D. +func (d Duration) Unshift(t time.Time) time.Time { + if d.Y != 0 || d.M != 0 || d.W != 0 || d.D != 0 { + days := d.W*7 + d.D + t = t.AddDate(-d.Y, -d.M, -days) + } + t = t.Add(-d.timeDuration()) + return t +} + +``` + +This method allows for shifting dates and times backward, which can be useful in certain scenarios. + diff --git a/duration.go b/duration.go index 7163e3d..d06d3d2 100644 --- a/duration.go +++ b/duration.go @@ -98,6 +98,23 @@ func (d Duration) Shift(t time.Time) time.Time { return t } +// UnShift returns a time.Time, shifted back by the duration from the given start. +// +// NB: UnShift uses time.AddDate for years, months, weeks, and days, and so +// shares its limitations. In particular, shifting back by months is not recommended +// unless the start date is before the 28th of the month. Otherwise, dates will +// roll over, e.g. Oct 1 - P1M = Aug 31. +// +// Week and Day values will be combined as W*7 + D. +func (d Duration) Unshift(t time.Time) time.Time { + if d.Y != 0 || d.M != 0 || d.W != 0 || d.D != 0 { + days := d.W*7 + d.D + t = t.AddDate(-d.Y, -d.M, -days) + } + t = t.Add(-d.timeDuration()) + return t +} + func (d Duration) timeDuration() time.Duration { var dur time.Duration dur = dur + (time.Duration(d.TH) * time.Hour) diff --git a/duration_test.go b/duration_test.go index bfc773c..063d8fb 100644 --- a/duration_test.go +++ b/duration_test.go @@ -201,3 +201,40 @@ func TestCanRejectBadJSON(t *testing.T) { t.Fatal("expected error, got none") } } + +func TestCanUnshift(t *testing.T) { + cases := []struct { + to string + duration duration.Duration + want string + }{ + {"Jan 1, 2019 at 00:00:00", duration.Duration{Y: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Feb 1, 2018 at 00:00:00", duration.Duration{M: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Mar 1, 2018 at 00:00:00", duration.Duration{M: 2}, "Jan 1, 2018 at 00:00:00"}, + {"Jan 8, 2018 at 00:00:00", duration.Duration{W: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Jan 2, 2018 at 00:00:00", duration.Duration{D: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Jan 1, 2018 at 01:00:00", duration.Duration{TH: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Jan 1, 2018 at 00:01:00", duration.Duration{TM: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Jan 1, 2018 at 00:00:01", duration.Duration{TS: 1}, "Jan 1, 2018 at 00:00:00"}, + {"Jun 9, 2028 at 05:10:06", duration.Duration{ + Y: 10, + M: 5, + D: 8, + TH: 5, + TM: 10, + TS: 6, + }, + "Jan 1, 2018 at 00:00:00", + }, + } + + for k, c := range cases { + to := makeTime(t, c.to) + want := makeTime(t, c.want) + + got := c.duration.Unshift(to) + if !want.Equal(got) { + t.Fatalf("Case %d: want=%s, got=%s", k, want, got) + } + } +}