diff --git a/rules/br/deadline.go b/rules/br/deadline.go new file mode 100644 index 0000000..3d1d639 --- /dev/null +++ b/rules/br/deadline.go @@ -0,0 +1,104 @@ +package br + +import ( + "regexp" + "strconv" + "strings" + "time" + + "github.com/AlekSi/pointer" + "github.com/olebedev/when/rules" + "github.com/pkg/errors" +) + +func Deadline(s rules.Strategy) rules.Rule { + overwrite := s == rules.Override + + return &rules.F{ + RegExp: regexp.MustCompile( + "(?i)(?:\\W|^)(dentro\\sde|em)\\s*" + + "(?:(" + INTEGER_WORDS_PATTERN + "|[0-9]+|(?:\\s*pouc[oa](?:s|)?|algu(?:mas|m|ns)?|mei[oa]?))\\s*" + + "(segundos?|min(?:uto)?s?|horas?|dias?|semanas?|mês|meses|anos?)\\s*)" + + "(?:\\W|$)"), + Applier: func(m *rules.Match, c *rules.Context, o *rules.Options, ref time.Time) (bool, error) { + numStr := strings.TrimSpace(m.Captures[1]) + + var num int + var err error + + if n, ok := INTEGER_WORDS[numStr]; ok { + num = n + } else if strings.Contains(numStr, "pouc") || strings.Contains(numStr, "algu") { + num = 3 + } else if strings.Contains(numStr, "mei") { + // pass + } else { + num, err = strconv.Atoi(numStr) + if err != nil { + return false, errors.Wrapf(err, "convert '%s' to int", numStr) + } + } + + exponent := strings.TrimSpace(m.Captures[2]) + + if !strings.Contains(numStr, "mei") { + switch { + case strings.Contains(exponent, "segundo"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * time.Second + } + case strings.Contains(exponent, "min"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * time.Minute + } + case strings.Contains(exponent, "hora"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * time.Hour + } + case strings.Contains(exponent, "dia"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * 24 * time.Hour + } + case strings.Contains(exponent, "semana"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * 7 * 24 * time.Hour + } + case strings.Contains(exponent, "mês"), strings.Contains(exponent, "meses"): + if c.Month == nil || overwrite { + c.Month = pointer.ToInt((int(ref.Month()) + num) % 12) + } + case strings.Contains(exponent, "ano"): + if c.Year == nil || overwrite { + c.Year = pointer.ToInt(ref.Year() + num) + } + } + } else { + switch { + case strings.Contains(exponent, "hora"): + if c.Duration == 0 || overwrite { + c.Duration = 30 * time.Minute + } + case strings.Contains(exponent, "dia"): + if c.Duration == 0 || overwrite { + c.Duration = 12 * time.Hour + } + case strings.Contains(exponent, "semana"): + if c.Duration == 0 || overwrite { + c.Duration = 7 * 12 * time.Hour + } + case strings.Contains(exponent, "mês"), strings.Contains(exponent, "meses"): + if c.Duration == 0 || overwrite { + // 2 weeks + c.Duration = 14 * 24 * time.Hour + } + case strings.Contains(exponent, "ano"): + if c.Month == nil || overwrite { + c.Month = pointer.ToInt((int(ref.Month()) + 6) % 12) + } + } + } + + return true, nil + }, + } +} diff --git a/rules/br/deadline_test.go b/rules/br/deadline_test.go new file mode 100644 index 0000000..caba3d9 --- /dev/null +++ b/rules/br/deadline_test.go @@ -0,0 +1,34 @@ +package br_test + +import ( + "testing" + "time" + + "github.com/olebedev/when" + "github.com/olebedev/when/rules" + "github.com/olebedev/when/rules/br" +) + +func TestDeadline(t *testing.T) { + fixt := []Fixture{ + {"dentro de meia hora", 0, "dentro de meia hora", time.Hour / 2}, + {"dentro de 1 hora", 0, "dentro de 1 hora", time.Hour}, + {"em 5 minutos", 0, "em 5 minutos", time.Minute * 5}, + {"Em 5 minutos eu irei para casa", 0, "Em 5 minutos", time.Minute * 5}, + {"nós precisamos fazer algo dentro de 10 dias.", 27, "dentro de 10 dias", 10 * 24 * time.Hour}, + {"nós temos que fazer algo em cinco dias.", 26, "em cinco dias", 5 * 24 * time.Hour}, + {"nós temos que fazer algo em 5 dias.", 26, "em 5 dias", 5 * 24 * time.Hour}, + {"Em 5 segundos, um carro precisa se mover", 0, "Em 5 segundos", 5 * time.Second}, + {"dentro de duas semanas", 0, "dentro de duas semanas", 14 * 24 * time.Hour}, + {"dentro de um mês", 0, "dentro de um mês", 31 * 24 * time.Hour}, + {"dentro de alguns meses", 0, "dentro de alguns meses", 91 * 24 * time.Hour}, + {"dentro de poucos meses", 0, "dentro de poucos meses", 91 * 24 * time.Hour}, + {"dentro de um ano", 0, "dentro de um ano", 366 * 24 * time.Hour}, + {"em uma semana", 0, "em uma semana", 7 * 24 * time.Hour}, + } + + w := when.New(nil) + w.Add(br.Deadline(rules.Skip)) + + ApplyFixtures(t, "br.Deadline", w, fixt) +} diff --git a/rules/nl/deadline.go b/rules/nl/deadline.go new file mode 100644 index 0000000..6f2212c --- /dev/null +++ b/rules/nl/deadline.go @@ -0,0 +1,106 @@ +package nl + +import ( + "regexp" + "strconv" + "strings" + "time" + + "github.com/AlekSi/pointer" + "github.com/olebedev/when/rules" + "github.com/pkg/errors" +) + +func Deadline(s rules.Strategy) rules.Rule { + overwrite := s == rules.Override + + return &rules.F{ + RegExp: regexp.MustCompile( + "(?i)(?:\\W|^)(binnen|in|over|na)\\s*" + + "(" + INTEGER_WORDS_PATTERN + "|[0-9]+|een(?:\\s*(paar|half|halve))?)\\s*" + + "(seconden?|minuut|minuten|uur|uren|dag|dagen|week|weken|maand|maanden|jaar|jaren)\\s*" + + "(?:\\W|$)"), + Applier: func(m *rules.Match, c *rules.Context, o *rules.Options, ref time.Time) (bool, error) { + numStr := strings.TrimSpace(m.Captures[1]) + + var num int + var err error + + if n, ok := INTEGER_WORDS[numStr]; ok { + num = n + } else if numStr == "een" { + num = 1 + } else if strings.Contains(numStr, "paar") { + num = 3 + } else if strings.Contains(numStr, "half") || strings.Contains(numStr, "halve") { + // pass + } else { + num, err = strconv.Atoi(numStr) + if err != nil { + return false, errors.Wrapf(err, "convert '%s' to int", numStr) + } + } + + exponent := strings.TrimSpace(m.Captures[3]) + + if !strings.Contains(numStr, "half") && !strings.Contains(numStr, "halve") { + switch { + case strings.Contains(exponent, "second"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * time.Second + } + case strings.Contains(exponent, "min"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * time.Minute + } + case strings.Contains(exponent, "uur"), strings.Contains(exponent, "uren"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * time.Hour + } + case strings.Contains(exponent, "dag"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * 24 * time.Hour + } + case strings.Contains(exponent, "week"), strings.Contains(exponent, "weken"): + if c.Duration == 0 || overwrite { + c.Duration = time.Duration(num) * 7 * 24 * time.Hour + } + case strings.Contains(exponent, "maand"): + if c.Month == nil || overwrite { + c.Month = pointer.ToInt((int(ref.Month()) + num) % 12) + } + case strings.Contains(exponent, "jaar"): + if c.Year == nil || overwrite { + c.Year = pointer.ToInt(ref.Year() + num) + } + } + } else { + switch { + case strings.Contains(exponent, "uur"): + if c.Duration == 0 || overwrite { + c.Duration = 30 * time.Minute + } + case strings.Contains(exponent, "dag"): + if c.Duration == 0 || overwrite { + c.Duration = 12 * time.Hour + } + case strings.Contains(exponent, "week"): + if c.Duration == 0 || overwrite { + c.Duration = 7 * 12 * time.Hour + } + case strings.Contains(exponent, "maand"): + if c.Duration == 0 || overwrite { + // 2 weeks + c.Duration = 14 * 24 * time.Hour + } + case strings.Contains(exponent, "jaar"): + if c.Month == nil || overwrite { + c.Month = pointer.ToInt((int(ref.Month()) + 6) % 12) + } + } + } + + return true, nil + }, + } +} diff --git a/rules/nl/deadline_test.go b/rules/nl/deadline_test.go new file mode 100644 index 0000000..0fc6f94 --- /dev/null +++ b/rules/nl/deadline_test.go @@ -0,0 +1,34 @@ +package nl_test + +import ( + "github.com/olebedev/when/rules/nl" + "testing" + "time" + + "github.com/olebedev/when" + "github.com/olebedev/when/rules" +) + +func TestDeadline(t *testing.T) { + fixt := []Fixture{ + {"binnen een half uur", 0, "binnen een half uur", time.Hour / 2}, + {"binnen 1 uur", 0, "binnen 1 uur", time.Hour}, + {"in 5 minuten", 0, "in 5 minuten", time.Minute * 5}, + {"Binnen 5 minuten ga ik naar huis", 0, "Binnen 5 minuten", time.Minute * 5}, + {"we moeten binnen 10 dagen iets doen", 10, "binnen 10 dagen", 10 * 24 * time.Hour}, + {"we moeten binnen vijf dagen iets doen", 10, "binnen vijf dagen", 5 * 24 * time.Hour}, + {"we moeten over 5 dagen iets doen", 10, "over 5 dagen", 5 * 24 * time.Hour}, + {"In 5 seconde moet een auto verplaatsen", 0, "In 5 seconde", 5 * time.Second}, + {"binnen twee weken", 0, "binnen twee weken", 14 * 24 * time.Hour}, + {"binnen een maand", 0, "binnen een maand", 31 * 24 * time.Hour}, + {"na een maand", 0, "na een maand", 31 * 24 * time.Hour}, + {"binnen een paar maanden", 0, "binnen een paar maanden", 91 * 24 * time.Hour}, + {"binnen een jaar", 0, "binnen een jaar", 366 * 24 * time.Hour}, + {"in een week", 0, "in een week", 7 * 24 * time.Hour}, + } + + w := when.New(nil) + w.Add(nl.Deadline(rules.Skip)) + + ApplyFixtures(t, "nl.Deadline", w, fixt) +}