Skip to content
26 changes: 26 additions & 0 deletions rules/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@ package common

import "github.com/olebedev/when/rules"

var MONTHS_DAYS = []int{
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
}

func GetDays(year, month int) int {
if month > 12 || month < 0 {
return 0
}

if month != 2 {
return MONTHS_DAYS[month]
}

if year%4 == 0 {
if year%400 == 0 {
return 29
}
if year%100 == 0 {
return 28
}
return 29
}

return 28
}

var All = []rules.Rule{
SlashDMY(rules.Override),
}
11 changes: 9 additions & 2 deletions rules/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Fixture struct {
Text string
Index int
Phrase string
Diff time.Duration
Want time.Time
}

func ApplyFixtures(t *testing.T, name string, w *when.Parser, fixt []Fixture) {
Expand All @@ -28,7 +28,7 @@ func ApplyFixtures(t *testing.T, name string, w *when.Parser, fixt []Fixture) {
require.NotNil(t, res, "[%s] res #%d", name, i)
require.Equal(t, f.Index, res.Index, "[%s] index #%d", name, i)
require.Equal(t, f.Phrase, res.Text, "[%s] text #%d", name, i)
require.Equal(t, f.Diff, res.Time.Sub(null), "[%s] diff #%d", name, i)
require.Equal(t, f.Want, res.Time, "[%s] %s diff #%d", name, f.Phrase, i)
}
}

Expand Down Expand Up @@ -56,3 +56,10 @@ func TestAll(t *testing.T) {
fixt := []Fixture{}
ApplyFixtures(t, "common.All...", w, fixt)
}

func TestLeapYear(t *testing.T) {
require.Equal(t, common.GetDays(1999, 2), 28, "Normal year")
require.Equal(t, common.GetDays(2004, 2), 29, "Leap year")
require.Equal(t, common.GetDays(3000, 2), 28, "Century")
require.Equal(t, common.GetDays(2000, 2), 29, "Century divisible by 400")
}
64 changes: 32 additions & 32 deletions rules/common/slash_dmy.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ also with "\", gift for windows' users
https://play.golang.org/p/29LkTfe1Xr
*/

var MONTHS_DAYS = []int{
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
}

func getDays(year, month int) int {
// naive leap year check
if (year-2000)%4 == 0 && month == 2 {
return 29
}
return MONTHS_DAYS[month]
}

func SlashDMY(s rules.Strategy) rules.Rule {

return &rules.F{
Expand All @@ -58,39 +46,51 @@ func SlashDMY(s rules.Strategy) rules.Rule {
return false, nil
}

if month > 12 {
return false, nil
}

if day > GetDays(ref.Year(), month) {
// invalid date: day is after last day of the month
return false, nil
}

WithYear:
if year != -1 {
if getDays(year, month) >= day {
c.Year = &year
c.Month = &month
c.Day = &day
} else {
return false, nil
}
c.Year = &year
c.Month = &month
c.Day = &day
return true, nil
}

if int(ref.Month()) > month {
year = ref.Year() + 1
goto WithYear
}
if o.WantPast {
if month > int(ref.Month()) {
year = ref.Year() - 1
} else if month == int(ref.Month()) {
if day <= ref.Day() {
year = ref.Year()
} else {
year = ref.Year() - 1
}
} else {
year = ref.Year()
}

if int(ref.Month()) == month {
if getDays(ref.Year(), month) >= day {
if day > ref.Day() {
} else {
if month < int(ref.Month()) {
year = ref.Year() + 1
} else if month == int(ref.Month()) {
if day >= ref.Day() {
year = ref.Year()
} else if day < ref.Day() {
year = ref.Year() + 1
} else {
return false, nil
year = ref.Year() + 1
}
goto WithYear
} else {
return false, nil
year = ref.Year()
}
}

return true, nil
goto WithYear
},
}
}
48 changes: 40 additions & 8 deletions rules/common/slash_dmy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,26 @@ import (

func TestSlashDMY(t *testing.T) {
fixt := []Fixture{
{"The Deadline is 10/10/2016", 16, "10/10/2016", (284 - OFFSET) * 24 * time.Hour},
{"The Deadline is 1/2/2016", 16, "1/2/2016", (32 - OFFSET) * 24 * time.Hour},
{"The Deadline is 29/2/2016", 16, "29/2/2016", (60 - OFFSET) * 24 * time.Hour},
{"The Deadline is 10/10/2016", 16, "10/10/2016", time.Date(2016, 10, 10, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 1/2/2016", 16, "1/2/2016", time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 29/2/2016", 16, "29/2/2016", time.Date(2016, 2, 29, 0, 0, 0, 0, time.UTC)},

// next year
{"The Deadline is 28/2", 16, "28/2", (59 + 366 - OFFSET) * 24 * time.Hour},
{"The Deadline is 28/02/2017", 16, "28/02/2017", (59 + 366 - OFFSET) * 24 * time.Hour},
{"The Deadline is 28/2", 16, "28/2", time.Date(2017, 2, 28, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 28/02/2017", 16, "28/02/2017", time.Date(2017, 2, 28, 0, 0, 0, 0, time.UTC)},

// right after w/o a year
{"The Deadline is 28/07", 16, "28/07", (210 - OFFSET) * 24 * time.Hour},
{"The Deadline is 28/07", 16, "28/07", time.Date(2016, 7, 28, 0, 0, 0, 0, time.UTC)},

// before w/o a year
{"The Deadline is 30/06", 16, "30/06", (181 + 366 - OFFSET) * 24 * time.Hour},
{"The Deadline is 30/06", 16, "30/06", time.Date(2017, 6, 30, 0, 0, 0, 0, time.UTC)},

// prev day will be added to the future
{"The Deadline is 14/07", 16, "14/07", (195 + 366 - OFFSET) * 24 * time.Hour},
{"The Deadline is 14/07", 16, "14/07", time.Date(2017, 7, 14, 0, 0, 0, 0, time.UTC)},

// Existing doesn't work for a month in the future
{"The Deadline is 14/08", 16, "14/08", time.Date(2016, 8, 14, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 15/07", 16, "15/07", time.Date(2016, 7, 15, 0, 0, 0, 0, time.UTC)},
}

w := when.New(nil)
Expand All @@ -35,3 +39,31 @@ func TestSlashDMY(t *testing.T) {
ApplyFixtures(t, "common.SlashDMY", w, fixt)

}

func TestSlashDMYPast(t *testing.T) {
fixt := []Fixture{
{"The Deadline is 10/10/2016", 16, "10/10/2016", time.Date(2016, 10, 10, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 1/2/2016", 16, "1/2/2016", time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 29/2/2016", 16, "29/2/2016", time.Date(2016, 2, 29, 0, 0, 0, 0, time.UTC)},

// before w/o a year says same year
{"The Deadline is 30/06", 16, "30/06", time.Date(2016, 6, 30, 0, 0, 0, 0, time.UTC)},

// prev day will still be this year
{"The Deadline is 14/07", 16, "14/07", time.Date(2016, 7, 14, 0, 0, 0, 0, time.UTC)},

// after w/o a year is prior year
{"The Deadline is 28/07", 16, "28/07", time.Date(2015, 7, 28, 0, 0, 0, 0, time.UTC)},

// Regression tests: current date and furture month
{"The Deadline is 15/07", 16, "15/07", time.Date(2016, 7, 15, 0, 0, 0, 0, time.UTC)},
}

w := when.New(&rules.Options{
Distance: 5,
MatchByOrder: true,
WantPast: true})
w.Add(common.SlashDMY(rules.Skip))

ApplyFixtures(t, "common.SlashDMY", w, fixt)
}
94 changes: 94 additions & 0 deletions rules/common/slash_mdy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package common

import (
"regexp"
"strconv"
"time"

"github.com/olebedev/when/rules"
)

/*

- MM/DD/YYYY
- 3/14/2015
- 03/14/2015
- 3/14

also with "\", gift for windows' users
*/

func SlashMDY(s rules.Strategy) rules.Rule {

return &rules.F{
RegExp: regexp.MustCompile("(?i)(?:\\W|^)" +
"([0-3]{0,1}[0-9]{1})" +
"[\\/\\\\]" +
"([0-3]{0,1}[0-9]{1})" +
"(?:[\\/\\\\]" +
"((?:1|2)[0-9]{3})\\s*)?" +
"(?:\\W|$)"),
Applier: func(m *rules.Match, c *rules.Context, o *rules.Options, ref time.Time) (bool, error) {
if (c.Day != nil || c.Month != nil || c.Year != nil) && s != rules.Override {
return false, nil
}

month, _ := strconv.Atoi(m.Captures[0])
day, _ := strconv.Atoi(m.Captures[1])
year := -1
if m.Captures[2] != "" {
year, _ = strconv.Atoi(m.Captures[2])
}

if day == 0 {
return false, nil
}

if month > 12 {
return false, nil
}

if day > GetDays(ref.Year(), month) {
// invalid date: day is after last day of the month
return false, nil
}

WithYear:
if year != -1 {
c.Year = &year
c.Month = &month
c.Day = &day
return true, nil
}

if o.WantPast {
if month > int(ref.Month()) {
year = ref.Year() - 1
} else if month == int(ref.Month()) {
if day <= ref.Day() {
year = ref.Year()
} else {
year = ref.Year() - 1
}
} else {
year = ref.Year()
}

} else {
if month < int(ref.Month()) {
year = ref.Year() + 1
} else if month == int(ref.Month()) {
if day >= ref.Day() {
year = ref.Year()
} else {
year = ref.Year() + 1
}
} else {
year = ref.Year()
}
}

goto WithYear
},
}
}
69 changes: 69 additions & 0 deletions rules/common/slash_mdy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package common_test

import (
"testing"
"time"

"github.com/olebedev/when"
"github.com/olebedev/when/rules"
"github.com/olebedev/when/rules/common"
)

func TestSlashMDY(t *testing.T) {
fixt := []Fixture{
{"The Deadline is 10/10/2016", 16, "10/10/2016", time.Date(2016, 10, 10, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 2/1/2016", 16, "2/1/2016", time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 2/29/2016", 16, "2/29/2016", time.Date(2016, 2, 29, 0, 0, 0, 0, time.UTC)},

// next year
{"The Deadline is 2/28", 16, "2/28", time.Date(2017, 2, 28, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 02/28/2017", 16, "02/28/2017", time.Date(2017, 2, 28, 0, 0, 0, 0, time.UTC)},

// right after w/o a year
{"The Deadline is 07/28", 16, "07/28", time.Date(2016, 7, 28, 0, 0, 0, 0, time.UTC)},

// before w/o a year
{"The Deadline is 06/30", 16, "06/30", time.Date(2017, 6, 30, 0, 0, 0, 0, time.UTC)},

// prev day will be added to the future
{"The Deadline is 07/14", 16, "07/14", time.Date(2017, time.July, 14, 0, 0, 0, 0, time.UTC)},

// Current day or future months
{"The Deadline is 8/14", 16, "8/14", time.Date(2016, 8, 14, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 7/15", 16, "7/15", time.Date(2016, 7, 15, 0, 0, 0, 0, time.UTC)},
}

w := when.New(nil)
w.Add(common.SlashMDY(rules.Override))

ApplyFixtures(t, "common.SlashMDY", w, fixt)

}

func TestSlashMDYPast(t *testing.T) {
fixt := []Fixture{
{"The Deadline is 10/10/2016", 16, "10/10/2016", time.Date(2016, 10, 10, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 2/1/2016", 16, "2/1/2016", time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC)},
{"The Deadline is 2/29/2016", 16, "2/29/2016", time.Date(2016, 2, 29, 0, 0, 0, 0, time.UTC)},

// before w/o a year says same year
{"The Deadline is 06/30", 16, "06/30", time.Date(2016, 6, 30, 0, 0, 0, 0, time.UTC)},

// prev day will still be this year
{"The Deadline is 07/14", 16, "07/14", time.Date(2016, 7, 14, 0, 0, 0, 0, time.UTC)},

// after w/o a year is prior year
{"The Deadline is 07/28", 16, "07/28", time.Date(2015, 7, 28, 0, 0, 0, 0, time.UTC)},

// Regression tests: current date and furture month
{"The Deadline is 07/15", 16, "07/15", time.Date(2016, 7, 15, 0, 0, 0, 0, time.UTC)},
}

w := when.New(&rules.Options{
Distance: 5,
MatchByOrder: true,
WantPast: true})
w.Add(common.SlashMDY(rules.Skip))

ApplyFixtures(t, "common.SlashMDY", w, fixt)
}
Loading