From 0f2b2b9a94dd71e39de2e8bbb3d8d42a409e3797 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 15 Sep 2023 11:31:41 +0200 Subject: [PATCH] [full-ci] enhancement: add support for natural language kql date ranges (#7263) * enhancement: add more kql spec tests and simplify ast normalization * enhancement: kql parser error if query starts with AND * enhancement: add kql docs and support for date and time only dateTimeRestriction queries * enhancement: add the ability to decide how kql nodes get connected connecting nodes (with edges) seem straight forward when not using group, the default connection for nodes with the same node is always OR. THis only applies for first level nodes, for grouped nodes it is defined differently. The KQL docs are saying, nodes inside a grouped node, with the same key are connected by a AND edge. * enhancement: explicit error handling for falsy group nodes and queries with leading binary operator * enhancement: use optimized grammar for kql parser and toolify pigeon * enhancement: simplify error handling * fix: kql implicit 'AND' and 'OR' follows the ms html spec instead of the pdf spec * enhancement: add support for natural language kql date queries * enhancement: structure kql parser tests into logical clusters * fix: time-range error naming --- .../unreleased/kql-search-query-language.md | 28 +- go.mod | 2 +- go.sum | 7 +- services/search/pkg/query/kql/cast.go | 68 +- services/search/pkg/query/kql/dictionary.peg | 21 +- .../search/pkg/query/kql/dictionary_gen.go | 952 ++++--- .../search/pkg/query/kql/dictionary_test.go | 911 ++++--- services/search/pkg/query/kql/doc.go | 1 - .../search/pkg/query/kql/engine_suite_test.go | 11 + services/search/pkg/query/kql/error.go | 9 + services/search/pkg/query/kql/factory.go | 33 + services/search/pkg/query/kql/kql.go | 5 + .../github.com/araddon/dateparse/.travis.yml | 13 - vendor/github.com/araddon/dateparse/README.md | 323 --- .../github.com/araddon/dateparse/parseany.go | 2189 ----------------- vendor/github.com/jinzhu/now/Guardfile | 3 + .../dateparse/LICENSE => jinzhu/now/License} | 2 +- vendor/github.com/jinzhu/now/README.md | 137 ++ vendor/github.com/jinzhu/now/main.go | 200 ++ vendor/github.com/jinzhu/now/now.go | 245 ++ vendor/github.com/jinzhu/now/time.go | 9 + vendor/modules.txt | 6 +- 22 files changed, 1928 insertions(+), 3247 deletions(-) create mode 100644 services/search/pkg/query/kql/engine_suite_test.go delete mode 100644 vendor/github.com/araddon/dateparse/.travis.yml delete mode 100644 vendor/github.com/araddon/dateparse/README.md delete mode 100644 vendor/github.com/araddon/dateparse/parseany.go create mode 100644 vendor/github.com/jinzhu/now/Guardfile rename vendor/github.com/{araddon/dateparse/LICENSE => jinzhu/now/License} (95%) create mode 100644 vendor/github.com/jinzhu/now/README.md create mode 100644 vendor/github.com/jinzhu/now/main.go create mode 100644 vendor/github.com/jinzhu/now/now.go create mode 100644 vendor/github.com/jinzhu/now/time.go diff --git a/changelog/unreleased/kql-search-query-language.md b/changelog/unreleased/kql-search-query-language.md index 0749e09df..f1724114c 100644 --- a/changelog/unreleased/kql-search-query-language.md +++ b/changelog/unreleased/kql-search-query-language.md @@ -2,16 +2,31 @@ Enhancement: Keyword Query Language (KQL) search syntax We've introduced support for [KQL](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference) as the default oCIS search query language. -Some examples of a valid KQL query are: +Simple queries: -* `Tag`: `tag:golden tag:"silver"` -* `Filename`: `name:file.txt name:"file.docx"` -* `Content`: `content:ahab content:"captain aha*"` +* `tag:golden tag:"silver"` +* `name:file.txt name:"file.docx"` +* `content:ahab content:"captain aha*"` + +Date/-range queries + +* `Mtime:"2023-09-05T08:42:11.23554+02:00"` +* `Mtime>"2023-09-05T08:42:11.23554+02:00"` +* `Mtime>="2023-09-05T08:42:11.23554+02:00"` +* `Mtime<"2023-09-05T08:42:11.23554+02:00"` +* `Mtime<="2023-09-05T08:42:11.23554+02:00"` +* `Mtime:today` - range: start of today till end of today +* `Mtime:yesterday` - range: start of yesterday till end of yesterday +* `Mtime:"this week"` - range: start of this week till end of this week +* `Mtime:"this month"` - range: start of this month till end of this month +* `Mtime:"last month"` - range: start of last month till end of last month +* `Mtime:"this year"` - range: start of this year till end of this year +* `Mtime:"last year"` - range: start of last year till end of last year Conjunctive normal form queries: -* `Boolean`: `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver` -* `Group`: `(tag:book content:ahab*)`, `tag:(book pdf)` +* `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver` +* `(tag:book content:ahab*)`, `tag:(book pdf)` Complex queries: @@ -22,6 +37,7 @@ https://github.com/owncloud/ocis/pull/7043 https://github.com/owncloud/ocis/pull/7247 https://github.com/owncloud/ocis/pull/7248 https://github.com/owncloud/ocis/pull/7254 +https://github.com/owncloud/ocis/pull/7262 https://github.com/owncloud/web/pull/9653 https://github.com/owncloud/web/pull/9672 https://github.com/owncloud/ocis/issues/7042 diff --git a/go.mod b/go.mod index a64f307dd..20ceae213 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/Masterminds/semver v1.5.0 github.com/MicahParks/keyfunc v1.9.0 github.com/Nerzal/gocloak/v13 v13.8.0 - github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bbalet/stopwords v1.0.0 github.com/blevesearch/bleve/v2 v2.3.9 github.com/coreos/go-oidc v2.2.1+incompatible @@ -50,6 +49,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/jellydator/ttlcache/v3 v3.1.0 + github.com/jinzhu/now v1.1.5 github.com/justinas/alice v1.2.0 github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 diff --git a/go.sum b/go.sum index fbeb6004b..9af77a03b 100644 --- a/go.sum +++ b/go.sum @@ -859,8 +859,6 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= -github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -1525,6 +1523,8 @@ github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVK github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1642,7 +1642,6 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -1866,7 +1865,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/riandyrn/otelchi v0.5.1 h1:0/45omeqpP7f/cvdL16GddQBfAEmZvUyl2QzLSE6uYo= github.com/riandyrn/otelchi v0.5.1/go.mod h1:ZxVxNEl+jQ9uHseRYIxKWRb3OY8YXFEu+EkNiiSNUEA= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1898,7 +1896,6 @@ github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sciencemesh/meshdirectory-web v1.0.4 h1:1YSctF6PAXhoHUYCaeRTj7rHaF7b3rYrZf2R0VXBIbo= github.com/sciencemesh/meshdirectory-web v1.0.4/go.mod h1:fJSThTS3xf+sTdL0iXQoaQJssLI7tn7DetHMHUl4SRk= -github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index d9710d35c..e3b9ab581 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/araddon/dateparse" + "github.com/jinzhu/now" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -21,21 +21,23 @@ func toNode[T ast.Node](in interface{}) (T, error) { func toNodes[T ast.Node](in interface{}) ([]T, error) { switch v := in.(type) { - case []T: - return v, nil case T: return []T{v}, nil + case []T: + return v, nil + case []*ast.OperatorNode, []*ast.DateTimeNode: + return toNodes[T](v) case []interface{}: - var ts []T - for _, inter := range v { - n, err := toNodes[T](inter) + var nodes []T + for _, el := range v { + node, err := toNodes[T](el) if err != nil { return nil, err } - ts = append(ts, n...) + nodes = append(nodes, node...) } - return ts, nil + return nodes, nil case nil: return nil, nil default: @@ -74,5 +76,53 @@ func toTime(in interface{}) (time.Time, error) { return time.Time{}, err } - return dateparse.ParseLocal(ts) + return now.Parse(ts) +} + +func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { + var from, to time.Time + + value, err := toString(in) + if err != nil { + return &from, &to, UnsupportedTimeRangeError{} + } + + c := &now.Config{ + WeekStartDay: time.Monday, + } + + n := c.With(timeNow()) + + switch value { + case "today": + from = n.BeginningOfDay() + to = n.EndOfDay() + case "yesterday": + yesterday := n.With(n.AddDate(0, 0, -1)) + from = yesterday.BeginningOfDay() + to = yesterday.EndOfDay() + case "this week": + from = n.BeginningOfWeek() + to = n.EndOfWeek() + case "this month": + from = n.BeginningOfMonth() + to = n.EndOfMonth() + case "last month": + lastMonth := n.With(n.AddDate(0, -1, 0)) + from = lastMonth.BeginningOfMonth() + to = lastMonth.EndOfMonth() + case "this year": + from = n.BeginningOfYear() + to = n.EndOfYear() + case "last year": + lastYear := n.With(n.AddDate(-1, 0, 0)) + from = lastYear.BeginningOfYear() + to = lastYear.EndOfYear() + } + + if from.IsZero() || to.IsZero() { + return nil, nil, UnsupportedTimeRangeError{} + } + + return &from, &to, nil } diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index ca9658dfa..426e4d509 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -61,6 +61,12 @@ DateTimeRestrictionNode <- FullTime ) '"'? { return buildDateTimeNode(k, o, v, c.text, c.pos) + } / + k:Char+ ( + OperatorEqualNode / + OperatorColonNode + ) '"'? v:NaturalLanguageDateTime '"'? { + return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos) } TextPropertyRestrictionNode <- @@ -185,11 +191,22 @@ FullTime <- return c.text, nil } -DateTime - = FullDate "T" FullTime { +DateTime <- + FullDate "T" FullTime { return c.text, nil } +NaturalLanguageDateTime <- + "today" / + "yesterday" / + "this week" / + "this month" / + "last month" / + "this year" / + "last year" { + return c.text, nil + } + //////////////////////////////////////////////////////// // misc //////////////////////////////////////////////////////// diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index afce6c5c8..4bcd4f83b 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -43,12 +43,12 @@ var g = &grammar{ pos: position{line: 19, col: 6, offset: 351}, exprs: []any{ &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, run: (*parser).callonNodes3, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -86,10 +86,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 46, col: 7, offset: 1044}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode7, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -102,20 +102,20 @@ var g = &grammar{ pos: position{line: 46, col: 14, offset: 1051}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonNode10, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode12, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -159,10 +159,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 51, col: 7, offset: 1205}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode22, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -178,60 +178,60 @@ var g = &grammar{ pos: position{line: 52, col: 9, offset: 1223}, alternatives: []any{ &actionExpr{ - pos: position{line: 139, col: 5, offset: 3168}, + pos: position{line: 145, col: 5, offset: 3358}, run: (*parser).callonNode26, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3168}, + pos: position{line: 145, col: 5, offset: 3358}, val: ">=", ignoreCase: false, want: "\">=\"", }, }, &actionExpr{ - pos: position{line: 129, col: 5, offset: 2984}, + pos: position{line: 135, col: 5, offset: 3174}, run: (*parser).callonNode28, expr: &litMatcher{ - pos: position{line: 129, col: 5, offset: 2984}, + pos: position{line: 135, col: 5, offset: 3174}, val: "<=", ignoreCase: false, want: "\"<=\"", }, }, &actionExpr{ - pos: position{line: 134, col: 5, offset: 3073}, + pos: position{line: 140, col: 5, offset: 3263}, run: (*parser).callonNode30, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3073}, + pos: position{line: 140, col: 5, offset: 3263}, val: ">", ignoreCase: false, want: "\">\"", }, }, &actionExpr{ - pos: position{line: 124, col: 5, offset: 2892}, + pos: position{line: 130, col: 5, offset: 3082}, run: (*parser).callonNode32, expr: &litMatcher{ - pos: position{line: 124, col: 5, offset: 2892}, + pos: position{line: 130, col: 5, offset: 3082}, val: "<", ignoreCase: false, want: "\"<\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode34, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", }, }, &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonNode36, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -256,28 +256,28 @@ var g = &grammar{ pos: position{line: 59, col: 9, offset: 1420}, alternatives: []any{ &actionExpr{ - pos: position{line: 189, col: 5, offset: 4003}, + pos: position{line: 195, col: 5, offset: 4197}, run: (*parser).callonNode42, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4003}, + pos: position{line: 195, col: 5, offset: 4197}, exprs: []any{ &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, run: (*parser).callonNode44, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, exprs: []any{ &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, run: (*parser).callonNode46, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode48, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -285,10 +285,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode50, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -296,10 +296,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode52, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -307,10 +307,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode54, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -321,22 +321,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, + pos: position{line: 185, col: 14, offset: 3969}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, run: (*parser).callonNode57, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode59, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -344,10 +344,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode61, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -358,22 +358,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, + pos: position{line: 185, col: 28, offset: 3983}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, run: (*parser).callonNode64, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode66, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -381,10 +381,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode68, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -398,28 +398,28 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 189, col: 14, offset: 4012}, + pos: position{line: 195, col: 14, offset: 4206}, val: "T", ignoreCase: false, want: "\"T\"", }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, run: (*parser).callonNode71, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode73, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode75, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -427,10 +427,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode77, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -441,22 +441,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, + pos: position{line: 190, col: 14, offset: 4056}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode80, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode82, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -464,10 +464,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode84, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -478,22 +478,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, + pos: position{line: 190, col: 29, offset: 4071}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, run: (*parser).callonNode87, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode89, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -501,10 +501,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode91, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -515,23 +515,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, + pos: position{line: 190, col: 44, offset: 4086}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, + pos: position{line: 190, col: 49, offset: 4091}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode97, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -543,35 +543,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, + pos: position{line: 190, col: 65, offset: 4107}, exprs: []any{ &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, + pos: position{line: 190, col: 66, offset: 4108}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode103, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode105, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -579,10 +579,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode107, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -593,22 +593,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, + pos: position{line: 190, col: 86, offset: 4128}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode110, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode112, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -616,10 +616,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode114, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -640,22 +640,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, run: (*parser).callonNode116, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, exprs: []any{ &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, run: (*parser).callonNode118, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode120, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -663,10 +663,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode122, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -674,10 +674,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode124, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -685,10 +685,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode126, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -699,22 +699,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, + pos: position{line: 185, col: 14, offset: 3969}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, run: (*parser).callonNode129, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode131, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -722,10 +722,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode133, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -736,22 +736,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, + pos: position{line: 185, col: 28, offset: 3983}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, run: (*parser).callonNode136, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode138, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -759,10 +759,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode140, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -776,22 +776,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, run: (*parser).callonNode142, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode144, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode146, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -799,10 +799,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode148, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -813,22 +813,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, + pos: position{line: 190, col: 14, offset: 4056}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode151, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode153, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -836,10 +836,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode155, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -850,22 +850,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, + pos: position{line: 190, col: 29, offset: 4071}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, run: (*parser).callonNode158, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode160, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -873,10 +873,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode162, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -887,23 +887,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, + pos: position{line: 190, col: 44, offset: 4086}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, + pos: position{line: 190, col: 49, offset: 4091}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode168, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -915,35 +915,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, + pos: position{line: 190, col: 65, offset: 4107}, exprs: []any{ &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, + pos: position{line: 190, col: 66, offset: 4108}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode174, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode176, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -951,10 +951,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode178, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -965,22 +965,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, + pos: position{line: 190, col: 86, offset: 4128}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode181, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode183, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -988,10 +988,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode185, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1024,21 +1024,21 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, run: (*parser).callonNode189, expr: &seqExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, exprs: []any{ &labeledExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 67, col: 7, offset: 1581}, + pos: position{line: 65, col: 7, offset: 1551}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode193, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1048,23 +1048,148 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 67, col: 14, offset: 1588}, + pos: position{line: 66, col: 9, offset: 1567}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode196, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 125, col: 5, offset: 2997}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, + &actionExpr{ + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode198, + expr: &litMatcher{ + pos: position{line: 120, col: 5, offset: 2911}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 68, col: 7, offset: 1619}, + expr: &litMatcher{ + pos: position{line: 68, col: 7, offset: 1619}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + &labeledExpr{ + pos: position{line: 68, col: 12, offset: 1624}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 200, col: 5, offset: 4285}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 200, col: 5, offset: 4285}, + val: "today", + ignoreCase: false, + want: "\"today\"", + }, + &litMatcher{ + pos: position{line: 201, col: 5, offset: 4299}, + val: "yesterday", + ignoreCase: false, + want: "\"yesterday\"", + }, + &litMatcher{ + pos: position{line: 202, col: 5, offset: 4317}, + val: "this week", + ignoreCase: false, + want: "\"this week\"", + }, + &litMatcher{ + pos: position{line: 203, col: 5, offset: 4335}, + val: "this month", + ignoreCase: false, + want: "\"this month\"", + }, + &litMatcher{ + pos: position{line: 204, col: 5, offset: 4354}, + val: "last month", + ignoreCase: false, + want: "\"last month\"", + }, + &litMatcher{ + pos: position{line: 205, col: 5, offset: 4373}, + val: "this year", + ignoreCase: false, + want: "\"this year\"", + }, + &actionExpr{ + pos: position{line: 206, col: 5, offset: 4391}, + run: (*parser).callonNode210, + expr: &litMatcher{ + pos: position{line: 206, col: 5, offset: 4391}, + val: "last year", + ignoreCase: false, + want: "\"last year\"", + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 68, col: 38, offset: 1650}, + expr: &litMatcher{ + pos: position{line: 68, col: 38, offset: 1650}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + run: (*parser).callonNode214, + expr: &seqExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 73, col: 7, offset: 1771}, + expr: &actionExpr{ + pos: position{line: 215, col: 5, offset: 4575}, + run: (*parser).callonNode218, + expr: &charClassMatcher{ + pos: position{line: 215, col: 5, offset: 4575}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 73, col: 14, offset: 1778}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode221, + expr: &litMatcher{ + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonNode198, + pos: position{line: 125, col: 5, offset: 2997}, + run: (*parser).callonNode223, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -1073,30 +1198,30 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 67, col: 53, offset: 1627}, + pos: position{line: 73, col: 53, offset: 1817}, label: "v", expr: &choiceExpr{ - pos: position{line: 67, col: 56, offset: 1630}, + pos: position{line: 73, col: 56, offset: 1820}, alternatives: []any{ &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode202, + pos: position{line: 220, col: 5, offset: 4634}, + run: (*parser).callonNode227, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, + pos: position{line: 220, col: 9, offset: 4638}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1105,7 +1230,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, + pos: position{line: 220, col: 17, offset: 4646}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1114,9 +1239,9 @@ var g = &grammar{ }, }, &oneOrMoreExpr{ - pos: position{line: 67, col: 65, offset: 1639}, + pos: position{line: 73, col: 65, offset: 1829}, expr: &charClassMatcher{ - pos: position{line: 67, col: 65, offset: 1639}, + pos: position{line: 73, col: 65, offset: 1829}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -1130,19 +1255,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 99, col: 5, offset: 2431}, - run: (*parser).callonNode211, + pos: position{line: 105, col: 5, offset: 2621}, + run: (*parser).callonNode236, expr: &choiceExpr{ - pos: position{line: 99, col: 6, offset: 2432}, + pos: position{line: 105, col: 6, offset: 2622}, alternatives: []any{ &litMatcher{ - pos: position{line: 99, col: 6, offset: 2432}, + pos: position{line: 105, col: 6, offset: 2622}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 99, col: 14, offset: 2440}, + pos: position{line: 105, col: 14, offset: 2630}, val: "+", ignoreCase: false, want: "\"+\"", @@ -1151,19 +1276,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 104, col: 5, offset: 2532}, - run: (*parser).callonNode215, + pos: position{line: 110, col: 5, offset: 2722}, + run: (*parser).callonNode240, expr: &choiceExpr{ - pos: position{line: 104, col: 6, offset: 2533}, + pos: position{line: 110, col: 6, offset: 2723}, alternatives: []any{ &litMatcher{ - pos: position{line: 104, col: 6, offset: 2533}, + pos: position{line: 110, col: 6, offset: 2723}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 104, col: 14, offset: 2541}, + pos: position{line: 110, col: 14, offset: 2731}, val: "-", ignoreCase: false, want: "\"-\"", @@ -1172,28 +1297,28 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 109, col: 5, offset: 2632}, - run: (*parser).callonNode219, + pos: position{line: 115, col: 5, offset: 2822}, + run: (*parser).callonNode244, expr: &litMatcher{ - pos: position{line: 109, col: 6, offset: 2633}, + pos: position{line: 115, col: 6, offset: 2823}, val: "OR", ignoreCase: false, want: "\"OR\"", }, }, &actionExpr{ - pos: position{line: 80, col: 6, offset: 1919}, - run: (*parser).callonNode221, + pos: position{line: 86, col: 6, offset: 2109}, + run: (*parser).callonNode246, expr: &seqExpr{ - pos: position{line: 80, col: 6, offset: 1919}, + pos: position{line: 86, col: 6, offset: 2109}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 80, col: 6, offset: 1919}, + pos: position{line: 86, col: 6, offset: 2109}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode224, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode249, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1201,12 +1326,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode226, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode251, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1215,27 +1340,27 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 80, col: 27, offset: 1940}, + pos: position{line: 86, col: 27, offset: 2130}, label: "v", expr: &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode230, + pos: position{line: 220, col: 5, offset: 4634}, + run: (*parser).callonNode255, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, + pos: position{line: 220, col: 9, offset: 4638}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1244,7 +1369,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, + pos: position{line: 220, col: 17, offset: 4646}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1254,12 +1379,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode237, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode262, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1268,12 +1393,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 80, col: 38, offset: 1951}, + pos: position{line: 86, col: 38, offset: 2141}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode241, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode266, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1284,18 +1409,18 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 85, col: 6, offset: 2049}, - run: (*parser).callonNode243, + pos: position{line: 91, col: 6, offset: 2239}, + run: (*parser).callonNode268, expr: &seqExpr{ - pos: position{line: 85, col: 6, offset: 2049}, + pos: position{line: 91, col: 6, offset: 2239}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 85, col: 6, offset: 2049}, + pos: position{line: 91, col: 6, offset: 2239}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode246, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode271, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1303,12 +1428,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode248, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode273, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1317,12 +1442,12 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 85, col: 27, offset: 2070}, + pos: position{line: 91, col: 27, offset: 2260}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 85, col: 29, offset: 2072}, + pos: position{line: 91, col: 29, offset: 2262}, expr: &charClassMatcher{ - pos: position{line: 85, col: 29, offset: 2072}, + pos: position{line: 91, col: 29, offset: 2262}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -1331,12 +1456,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode254, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode279, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1345,12 +1470,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 85, col: 40, offset: 2083}, + pos: position{line: 91, col: 40, offset: 2273}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode258, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode283, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1380,10 +1505,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 32, col: 8, offset: 615}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonGroupNode6, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1399,20 +1524,20 @@ var g = &grammar{ pos: position{line: 32, col: 17, offset: 624}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonGroupNode10, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonGroupNode12, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -2284,19 +2409,19 @@ func (p *parser) callonNode198() (any, error) { return p.cur.onNode198() } -func (c *current) onNode202(v any) (any, error) { - return v, nil +func (c *current) onNode210() (any, error) { + return c.text, nil } -func (p *parser) callonNode202() (any, error) { +func (p *parser) callonNode210() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode202(stack["v"]) + return p.cur.onNode210() } func (c *current) onNode189(k, v any) (any, error) { - return buildStringNode(k, v, c.text, c.pos) + return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos) } @@ -2306,158 +2431,213 @@ func (p *parser) callonNode189() (any, error) { return p.cur.onNode189(stack["k"], stack["v"]) } -func (c *current) onNode211() (any, error) { +func (c *current) onNode218() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode218() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode218() +} + +func (c *current) onNode221() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode211() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode211() -} - -func (c *current) onNode215() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode215() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode215() -} - -func (c *current) onNode219() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode219() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode219() -} - -func (c *current) onNode224() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode224() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode224() -} - -func (c *current) onNode226() (any, error) { - return nil, nil - -} - -func (p *parser) callonNode226() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode226() -} - -func (c *current) onNode230(v any) (any, error) { - return v, nil - -} - -func (p *parser) callonNode230() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode230(stack["v"]) -} - -func (c *current) onNode237(v any) (any, error) { - return nil, nil - -} - -func (p *parser) callonNode237() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode237(stack["v"]) -} - -func (c *current) onNode241() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode241() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode241() -} - -func (c *current) onNode221(v any) (any, error) { - return buildStringNode("", v, c.text, c.pos) - -} - func (p *parser) callonNode221() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode221(stack["v"]) + return p.cur.onNode221() } -func (c *current) onNode246() (any, error) { +func (c *current) onNode223() (any, error) { return buildOperatorNode(c.text, c.pos) } +func (p *parser) callonNode223() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode223() +} + +func (c *current) onNode227(v any) (any, error) { + return v, nil + +} + +func (p *parser) callonNode227() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode227(stack["v"]) +} + +func (c *current) onNode214(k, v any) (any, error) { + return buildStringNode(k, v, c.text, c.pos) + +} + +func (p *parser) callonNode214() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode214(stack["k"], stack["v"]) +} + +func (c *current) onNode236() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode236() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode236() +} + +func (c *current) onNode240() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode240() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode240() +} + +func (c *current) onNode244() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode244() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode244() +} + +func (c *current) onNode249() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode249() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode249() +} + +func (c *current) onNode251() (any, error) { + return nil, nil + +} + +func (p *parser) callonNode251() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode251() +} + +func (c *current) onNode255(v any) (any, error) { + return v, nil + +} + +func (p *parser) callonNode255() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode255(stack["v"]) +} + +func (c *current) onNode262(v any) (any, error) { + return nil, nil + +} + +func (p *parser) callonNode262() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode262(stack["v"]) +} + +func (c *current) onNode266() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode266() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode266() +} + +func (c *current) onNode246(v any) (any, error) { + return buildStringNode("", v, c.text, c.pos) + +} + func (p *parser) callonNode246() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode246() + return p.cur.onNode246(stack["v"]) } -func (c *current) onNode248() (any, error) { - return nil, nil - -} - -func (p *parser) callonNode248() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode248() -} - -func (c *current) onNode254(v any) (any, error) { - return nil, nil - -} - -func (p *parser) callonNode254() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode254(stack["v"]) -} - -func (c *current) onNode258() (any, error) { +func (c *current) onNode271() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode258() (any, error) { +func (p *parser) callonNode271() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode258() + return p.cur.onNode271() } -func (c *current) onNode243(v any) (any, error) { +func (c *current) onNode273() (any, error) { + return nil, nil + +} + +func (p *parser) callonNode273() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode273() +} + +func (c *current) onNode279(v any) (any, error) { + return nil, nil + +} + +func (p *parser) callonNode279() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode279(stack["v"]) +} + +func (c *current) onNode283() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode283() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode283() +} + +func (c *current) onNode268(v any) (any, error) { return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonNode243() (any, error) { +func (p *parser) callonNode268() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode243(stack["v"]) + return p.cur.onNode268(stack["v"]) } func (c *current) onGroupNode6() (any, error) { diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 7e86456a9..05e3cf561 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/araddon/dateparse" + "github.com/jinzhu/now" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -13,66 +13,18 @@ import ( "github.com/owncloud/ocis/v2/services/search/pkg/query/kql" ) -var mustParseTime = func(t *testing.T, ts string) time.Time { - tp, err := dateparse.ParseLocal(ts) - if err != nil { - t.Fatalf("time.Parse(...) error = %v", err) - } - - return tp -} - -var mustJoin = func(v []string) string { - return strings.Join(v, " ") -} - -var FullDictionary = []string{ - `federated search`, - `federat* search`, - `search fed*`, - `author:"John Smith"`, - `filetype:docx`, - `filename:budget.xlsx`, - `author: "John Smith"`, - `author :"John Smith"`, - `author : "John Smith"`, - `author "John Smith"`, - `author "John Smith"`, - `author:Shakespear`, - `author:Paul`, - `author:Shakesp*`, - `title:"Advanced Search"`, - `title:"Advanced Sear*"`, - `title:"Advan* Search"`, - `title:"*anced Search"`, - `author:"John Smith" OR author:"Jane Smith"`, - `author:"John Smith" AND filetype:docx`, - `author:("John Smith" "Jane Smith")`, - `author:("John Smith" OR "Jane Smith")`, - `(DepartmentId:* OR RelatedHubSites:*) AND contentclass:sts_site NOT IsHubSite:false`, - `author:"John Smith" (filetype:docx title:"Advanced Search")`, -} - -func TestParse(t *testing.T) { - tests := []struct { - name string - skip bool - givenQuery string - expectedAst *ast.Ast - expectedError error - }{ - // SPEC ////////////////////////////////////////////////////////////////////////////// - // - // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf - // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference - // - // ++ +func TestParse_Spec(t *testing.T) { + // SPEC ////////////////////////////////////////////////////////////////////////////// + // + // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf + // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 + // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference + tests := []testCase{ // 2.1.2 AND Operator // 3.1.2 AND Operator { name: `cat AND dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -82,22 +34,21 @@ func TestParse(t *testing.T) { }, { name: `AND`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { name: `AND cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, - // ++ // 2.1.6 NOT Operator // 3.1.6 NOT Operator { name: `cat NOT dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -108,19 +59,18 @@ func TestParse(t *testing.T) { }, { name: `NOT dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Value: "dog"}, }, }, }, - // ++ // 2.1.8 OR Operator // 3.1.8 OR Operator { name: `cat OR dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -130,21 +80,20 @@ func TestParse(t *testing.T) { }, { name: `OR`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, { name: `OR cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, - // ++ // 3.1.11 Implicit Operator { name: `cat dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -154,7 +103,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND (dog OR fox)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -168,7 +117,7 @@ func TestParse(t *testing.T) { }, { name: `cat (dog OR fox)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -180,12 +129,11 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 2.1.12 Parentheses // 3.1.12 Parentheses { name: `(cat OR dog) AND fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, @@ -197,11 +145,10 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 3.2.3 Implicit Operator for Property Restriction { name: `author:"John Smith" filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -211,7 +158,7 @@ func TestParse(t *testing.T) { }, { name: `author:"John Smith" AND filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -221,7 +168,7 @@ func TestParse(t *testing.T) { }, { name: `author:"John Smith" author:"Jane Smith"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -231,7 +178,7 @@ func TestParse(t *testing.T) { }, { name: `author:"John Smith" OR author:"Jane Smith"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -241,7 +188,7 @@ func TestParse(t *testing.T) { }, { name: `cat filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -251,7 +198,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -259,11 +206,10 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 3.3.1.1.1 Implicit AND Operator { name: `cat +dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -273,7 +219,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -283,7 +229,7 @@ func TestParse(t *testing.T) { }, { name: `cat -dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -294,7 +240,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND NOT dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -305,7 +251,7 @@ func TestParse(t *testing.T) { }, { name: `cat +dog -fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -318,7 +264,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND dog AND NOT fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -331,7 +277,7 @@ func TestParse(t *testing.T) { }, { name: `cat dog +fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -343,7 +289,7 @@ func TestParse(t *testing.T) { }, { name: `fox OR (fox AND (cat OR dog))`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "fox"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -361,7 +307,7 @@ func TestParse(t *testing.T) { }, { name: `cat dog -fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -374,7 +320,7 @@ func TestParse(t *testing.T) { }, { name: `(NOT fox) AND (cat OR dog)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, @@ -391,7 +337,7 @@ func TestParse(t *testing.T) { }, { name: `cat +dog -fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -404,7 +350,7 @@ func TestParse(t *testing.T) { }, { name: `(NOT fox) AND (dog OR (dog AND cat))`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, @@ -423,12 +369,11 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 2.3.5 Date Tokens // 3.3.5 Date Tokens { name: `Modified:2023-09-05`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.DateTimeNode{ Key: "Modified", @@ -440,7 +385,7 @@ func TestParse(t *testing.T) { }, { name: `Modified:"2008-01-29"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.DateTimeNode{ Key: "Modified", @@ -451,15 +396,400 @@ func TestParse(t *testing.T) { }, }, { - name: `Modified:today`, - skip: true, + name: `Modified:today`, + patch: patchNow(t, "2023-09-10"), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, }, - ////////////////////////////////////////////////////////////////////////////////////// - // everything else + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_DateTimeRestrictionNode(t *testing.T) { + tests := []testCase{ { - name: "FullDictionary", - givenQuery: mustJoin(FullDictionary), - expectedAst: &ast.Ast{ + name: "format", + query: join([]string{ + `Mtime:"2023-09-05T08:42:11.23554+02:00"`, + `Mtime:2023-09-05T08:42:11.23554+02:00`, + `Mtime="2023-09-05T08:42:11.23554+02:00"`, + `Mtime=2023-09-05T08:42:11.23554+02:00`, + `Mtime<"2023-09-05T08:42:11.23554+02:00"`, + `Mtime<2023-09-05T08:42:11.23554+02:00`, + `Mtime<="2023-09-05T08:42:11.23554+02:00"`, + `Mtime<=2023-09-05T08:42:11.23554+02:00`, + `Mtime>"2023-09-05T08:42:11.23554+02:00"`, + `Mtime>2023-09-05T08:42:11.23554+02:00`, + `Mtime>="2023-09-05T08:42:11.23554+02:00"`, + `Mtime>=2023-09-05T08:42:11.23554+02:00`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - today", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:today`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-09"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", + patch: setWorldClock(t, "2023-09-01"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-31"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this week", + patch: setWorldClock(t, "2023-09-06"), + query: join([]string{ + `Mtime:"this week"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-04"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"this month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-12-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this year", + patch: setWorldClock(t, "2023-06-18"), + query: join([]string{ + `Mtime:"this year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_Errors(t *testing.T) { + tests := []testCase{ + { + query: "animal:(mammal:cat mammal:dog reptile:turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "cat"}, + }, + }, + { + query: "animal:(cat mammal:dog turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + { + query: "animal:(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "animal:(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: "(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: `cat dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_Stress(t *testing.T) { + tests := []testCase{ + { + name: "FullDictionary", + query: join(FullDictionary), + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "federated"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -565,13 +895,13 @@ func TestParse(t *testing.T) { }, }, { - name: "Group", - givenQuery: mustJoin([]string{ + name: "complex", + query: join([]string{ `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, `author:("John Smith" Jane)`, `author:("John Smith" OR Jane)`, }), - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -608,7 +938,7 @@ func TestParse(t *testing.T) { }, { name: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "author", @@ -627,7 +957,7 @@ func TestParse(t *testing.T) { }, { name: `author:("John Smith" OR Jane)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "author", @@ -642,7 +972,7 @@ func TestParse(t *testing.T) { }, { name: `NOT "John Smith" NOT Jane`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Value: "John Smith"}, @@ -654,7 +984,7 @@ func TestParse(t *testing.T) { }, { name: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Key: "author", Value: "John Smith"}, @@ -669,7 +999,7 @@ func TestParse(t *testing.T) { }, { name: `scope:"/new folder/subfolder" file`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{ Key: "scope", @@ -684,7 +1014,7 @@ func TestParse(t *testing.T) { }, { name: ` 😂 "*😀 😁*" name:😂💁👌🎍😍 name:😂💁👌 😍`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{ Value: "😂", @@ -710,121 +1040,9 @@ func TestParse(t *testing.T) { }, }, }, - { - name: "DateTimeRestrictionNode", - givenQuery: mustJoin([]string{ - `Mtime:"2023-09-05T08:42:11.23554+02:00"`, - `Mtime:2023-09-05T08:42:11.23554+02:00`, - `Mtime="2023-09-05T08:42:11.23554+02:00"`, - `Mtime=2023-09-05T08:42:11.23554+02:00`, - `Mtime<"2023-09-05T08:42:11.23554+02:00"`, - `Mtime<2023-09-05T08:42:11.23554+02:00`, - `Mtime<="2023-09-05T08:42:11.23554+02:00"`, - `Mtime<=2023-09-05T08:42:11.23554+02:00`, - `Mtime>"2023-09-05T08:42:11.23554+02:00"`, - `Mtime>2023-09-05T08:42:11.23554+02:00`, - `Mtime>="2023-09-05T08:42:11.23554+02:00"`, - `Mtime>=2023-09-05T08:42:11.23554+02:00`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - }, - }, - }, - { - name: "id", - givenQuery: mustJoin([]string{ - `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, - `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{ - Key: "id", - Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{ - Key: "ID", - Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", - }, - }, - }, - }, { name: "animal:(cat dog turtle)", - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "animal", @@ -847,7 +1065,7 @@ func TestParse(t *testing.T) { }, { name: "(cat dog turtle)", - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -867,55 +1085,9 @@ func TestParse(t *testing.T) { }, }, }, - { - name: "animal:(mammal:cat mammal:dog reptile:turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "cat"}, - }, - }, - { - name: "animal:(cat mammal:dog turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "dog"}, - }, - }, - { - name: "animal:(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "animal:(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: "(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: `cat dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, { name: `cat dog fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -927,7 +1099,7 @@ func TestParse(t *testing.T) { }, { name: `(cat dog) fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -943,7 +1115,7 @@ func TestParse(t *testing.T) { }, { name: `(mammal:cat mammal:dog) fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -959,7 +1131,7 @@ func TestParse(t *testing.T) { }, { name: `mammal:(cat dog) fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "mammal", @@ -976,7 +1148,7 @@ func TestParse(t *testing.T) { }, { name: `mammal:(cat dog) mammal:fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "mammal", @@ -993,7 +1165,7 @@ func TestParse(t *testing.T) { }, { name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "title", @@ -1015,39 +1187,56 @@ func TestParse(t *testing.T) { }, }, }, + { + name: "ids", + query: join([]string{ + `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{ + Key: "id", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{ + Key: "ID", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, + }, + }, + }, + { + name: `"test:test" test:"test:test" "more:*+#!/°^§$%&&/()=?<><<<<="}, + }, + &ast.OperatorNode{Value: BoolAND}, + &ast.DateTimeNode{ + Base: b, + Value: *to, + Key: key, + Operator: &ast.OperatorNode{Value: "<="}, + }, + }, nil + +} func buildBooleanNode(k, v interface{}, text []byte, pos position) (*ast.BooleanNode, error) { b, err := base(text, pos) diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 8719223b8..2cf03a7f0 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -3,6 +3,7 @@ package kql import ( "errors" + "time" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -42,3 +43,7 @@ func (b Builder) Build(q string) (*ast.Ast, error) { return f.(*ast.Ast), nil } + +// timeNow mirrors time.Now by default, the only reason why this exists +// is to monkey patch it from the tests. See PatchTimeNow +var timeNow = time.Now diff --git a/vendor/github.com/araddon/dateparse/.travis.yml b/vendor/github.com/araddon/dateparse/.travis.yml deleted file mode 100644 index 3b4b17777..000000000 --- a/vendor/github.com/araddon/dateparse/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: - - 1.13.x - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/araddon/dateparse/README.md b/vendor/github.com/araddon/dateparse/README.md deleted file mode 100644 index fe682dd56..000000000 --- a/vendor/github.com/araddon/dateparse/README.md +++ /dev/null @@ -1,323 +0,0 @@ -Go Date Parser ---------------------------- - -Parse many date strings without knowing format in advance. Uses a scanner to read bytes and use a state machine to find format. Much faster than shotgun based parse methods. See [bench_test.go](https://github.com/araddon/dateparse/blob/master/bench_test.go) for performance comparison. - - -[![Code Coverage](https://codecov.io/gh/araddon/dateparse/branch/master/graph/badge.svg)](https://codecov.io/gh/araddon/dateparse) -[![GoDoc](https://godoc.org/github.com/araddon/dateparse?status.svg)](http://godoc.org/github.com/araddon/dateparse) -[![Build Status](https://travis-ci.org/araddon/dateparse.svg?branch=master)](https://travis-ci.org/araddon/dateparse) -[![Go ReportCard](https://goreportcard.com/badge/araddon/dateparse)](https://goreportcard.com/report/araddon/dateparse) - -**MM/DD/YYYY VS DD/MM/YYYY** Right now this uses mm/dd/yyyy WHEN ambiguous if this is not desired behavior, use `ParseStrict` which will fail on ambiguous date strings. - -**Timezones** The location your server is configured affects the results! See example or https://play.golang.org/p/IDHRalIyXh and last paragraph here https://golang.org/pkg/time/#Parse. - - -```go - -// Normal parse. Equivalent Timezone rules as time.Parse() -t, err := dateparse.ParseAny("3/1/2014") - -// Parse Strict, error on ambigous mm/dd vs dd/mm dates -t, err := dateparse.ParseStrict("3/1/2014") -> returns error - -// Return a string that represents the layout to parse the given date-time. -layout, err := dateparse.ParseFormat("May 8, 2009 5:57:51 PM") -> "Jan 2, 2006 3:04:05 PM" - -``` - -cli tool for testing dateformats ----------------------------------- - -[Date Parse CLI](https://github.com/araddon/dateparse/blob/master/dateparse) - - -Extended example -------------------- - -https://github.com/araddon/dateparse/blob/master/example/main.go - -```go -package main - -import ( - "flag" - "fmt" - "time" - - "github.com/scylladb/termtables" - "github.com/araddon/dateparse" -) - -var examples = []string{ - "May 8, 2009 5:57:51 PM", - "oct 7, 1970", - "oct 7, '70", - "oct. 7, 1970", - "oct. 7, 70", - "Mon Jan 2 15:04:05 2006", - "Mon Jan 2 15:04:05 MST 2006", - "Mon Jan 02 15:04:05 -0700 2006", - "Monday, 02-Jan-06 15:04:05 MST", - "Mon, 02 Jan 2006 15:04:05 MST", - "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)", - "Mon, 02 Jan 2006 15:04:05 -0700", - "Mon 30 Sep 2018 09:09:09 PM UTC", - "Mon Aug 10 15:44:11 UTC+0100 2015", - "Thu, 4 Jan 2018 17:53:36 +0000", - "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", - "Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00)", - "September 17, 2012 10:09am", - "September 17, 2012 at 10:09am PST-08", - "September 17, 2012, 10:10:09", - "October 7, 1970", - "October 7th, 1970", - "12 Feb 2006, 19:17", - "12 Feb 2006 19:17", - "14 May 2019 19:11:40.164", - "7 oct 70", - "7 oct 1970", - "03 February 2013", - "1 July 2013", - "2013-Feb-03", - // dd/Mon/yyy alpha Months - "06/Jan/2008:15:04:05 -0700", - "06/Jan/2008 15:04:05 -0700", - // mm/dd/yy - "3/31/2014", - "03/31/2014", - "08/21/71", - "8/1/71", - "4/8/2014 22:05", - "04/08/2014 22:05", - "4/8/14 22:05", - "04/2/2014 03:00:51", - "8/8/1965 12:00:00 AM", - "8/8/1965 01:00:01 PM", - "8/8/1965 01:00 PM", - "8/8/1965 1:00 PM", - "8/8/1965 12:00 AM", - "4/02/2014 03:00:51", - "03/19/2012 10:11:59", - "03/19/2012 10:11:59.3186369", - // yyyy/mm/dd - "2014/3/31", - "2014/03/31", - "2014/4/8 22:05", - "2014/04/08 22:05", - "2014/04/2 03:00:51", - "2014/4/02 03:00:51", - "2012/03/19 10:11:59", - "2012/03/19 10:11:59.3186369", - // yyyy:mm:dd - "2014:3:31", - "2014:03:31", - "2014:4:8 22:05", - "2014:04:08 22:05", - "2014:04:2 03:00:51", - "2014:4:02 03:00:51", - "2012:03:19 10:11:59", - "2012:03:19 10:11:59.3186369", - // Chinese - "2014年04月08日", - // yyyy-mm-ddThh - "2006-01-02T15:04:05+0000", - "2009-08-12T22:15:09-07:00", - "2009-08-12T22:15:09", - "2009-08-12T22:15:09.988", - "2009-08-12T22:15:09Z", - "2017-07-19T03:21:51:897+0100", - "2019-05-29T08:41-04", // no seconds, 2 digit TZ offset - // yyyy-mm-dd hh:mm:ss - "2014-04-26 17:24:37.3186369", - "2012-08-03 18:31:59.257000000", - "2014-04-26 17:24:37.123", - "2013-04-01 22:43", - "2013-04-01 22:43:22", - "2014-12-16 06:20:00 UTC", - "2014-12-16 06:20:00 GMT", - "2014-04-26 05:24:37 PM", - "2014-04-26 13:13:43 +0800", - "2014-04-26 13:13:43 +0800 +08", - "2014-04-26 13:13:44 +09:00", - "2012-08-03 18:31:59.257000000 +0000 UTC", - "2015-09-30 18:48:56.35272715 +0000 UTC", - "2015-02-18 00:12:00 +0000 GMT", - "2015-02-18 00:12:00 +0000 UTC", - "2015-02-08 03:02:00 +0300 MSK m=+0.000000001", - "2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001", - "2017-07-19 03:21:51+00:00", - "2014-04-26", - "2014-04", - "2014", - "2014-05-11 08:20:13,787", - // yyyy-mm-dd-07:00 - "2020-07-20+08:00", - // mm.dd.yy - "3.31.2014", - "03.31.2014", - "08.21.71", - "2014.03", - "2014.03.30", - // yyyymmdd and similar - "20140601", - "20140722105203", - // yymmdd hh:mm:yy mysql log - // 080313 05:21:55 mysqld started - "171113 14:14:20", - // unix seconds, ms, micro, nano - "1332151919", - "1384216367189", - "1384216367111222", - "1384216367111222333", -} - -var ( - timezone = "" -) - -func main() { - flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone") - flag.Parse() - - if timezone != "" { - // NOTE: This is very, very important to understand - // time-parsing in go - loc, err := time.LoadLocation(timezone) - if err != nil { - panic(err.Error()) - } - time.Local = loc - } - - table := termtables.CreateTable() - - table.AddHeaders("Input", "Parsed, and Output as %v") - for _, dateExample := range examples { - t, err := dateparse.ParseLocal(dateExample) - if err != nil { - panic(err.Error()) - } - table.AddRow(dateExample, fmt.Sprintf("%v", t)) - } - fmt.Println(table.Render()) -} - -/* -+-------------------------------------------------------+-----------------------------------------+ -| Input | Parsed, and Output as %v | -+-------------------------------------------------------+-----------------------------------------+ -| May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC | -| oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC | -| oct. 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| oct. 7, 70 | 1970-10-07 00:00:00 +0000 UTC | -| Mon Jan 2 15:04:05 2006 | 2006-01-02 15:04:05 +0000 UTC | -| Mon Jan 2 15:04:05 MST 2006 | 2006-01-02 15:04:05 +0000 MST | -| Mon Jan 02 15:04:05 -0700 2006 | 2006-01-02 15:04:05 -0700 -0700 | -| Monday, 02-Jan-06 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST | -| Mon, 02 Jan 2006 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST | -| Tue, 11 Jul 2017 16:28:13 +0200 (CEST) | 2017-07-11 16:28:13 +0200 +0200 | -| Mon, 02 Jan 2006 15:04:05 -0700 | 2006-01-02 15:04:05 -0700 -0700 | -| Mon 30 Sep 2018 09:09:09 PM UTC | 2018-09-30 21:09:09 +0000 UTC | -| Mon Aug 10 15:44:11 UTC+0100 2015 | 2015-08-10 15:44:11 +0000 UTC | -| Thu, 4 Jan 2018 17:53:36 +0000 | 2018-01-04 17:53:36 +0000 UTC | -| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT | -| Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 | -| September 17, 2012 10:09am | 2012-09-17 10:09:00 +0000 UTC | -| September 17, 2012 at 10:09am PST-08 | 2012-09-17 10:09:00 -0800 PST | -| September 17, 2012, 10:10:09 | 2012-09-17 10:10:09 +0000 UTC | -| October 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| October 7th, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| 12 Feb 2006, 19:17 | 2006-02-12 19:17:00 +0000 UTC | -| 12 Feb 2006 19:17 | 2006-02-12 19:17:00 +0000 UTC | -| 14 May 2019 19:11:40.164 | 2019-05-14 19:11:40.164 +0000 UTC | -| 7 oct 70 | 1970-10-07 00:00:00 +0000 UTC | -| 7 oct 1970 | 1970-10-07 00:00:00 +0000 UTC | -| 03 February 2013 | 2013-02-03 00:00:00 +0000 UTC | -| 1 July 2013 | 2013-07-01 00:00:00 +0000 UTC | -| 2013-Feb-03 | 2013-02-03 00:00:00 +0000 UTC | -| 06/Jan/2008:15:04:05 -0700 | 2008-01-06 15:04:05 -0700 -0700 | -| 06/Jan/2008 15:04:05 -0700 | 2008-01-06 15:04:05 -0700 -0700 | -| 3/31/2014 | 2014-03-31 00:00:00 +0000 UTC | -| 03/31/2014 | 2014-03-31 00:00:00 +0000 UTC | -| 08/21/71 | 1971-08-21 00:00:00 +0000 UTC | -| 8/1/71 | 1971-08-01 00:00:00 +0000 UTC | -| 4/8/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 04/08/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 8/8/1965 12:00:00 AM | 1965-08-08 00:00:00 +0000 UTC | -| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC | -| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC | -| 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC | -| 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC | -| 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014/3/31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014/03/31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014/4/8 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014/04/08 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014/04/2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2014/4/02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2012/03/19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 2012/03/19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014:3:31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014:03:31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014:4:8 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014:04:08 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014:04:2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2014:4:02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2012:03:19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 2012:03:19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014年04月08日 | 2014-04-08 00:00:00 +0000 UTC | -| 2006-01-02T15:04:05+0000 | 2006-01-02 15:04:05 +0000 UTC | -| 2009-08-12T22:15:09-07:00 | 2009-08-12 22:15:09 -0700 -0700 | -| 2009-08-12T22:15:09 | 2009-08-12 22:15:09 +0000 UTC | -| 2009-08-12T22:15:09.988 | 2009-08-12 22:15:09.988 +0000 UTC | -| 2009-08-12T22:15:09Z | 2009-08-12 22:15:09 +0000 UTC | -| 2017-07-19T03:21:51:897+0100 | 2017-07-19 03:21:51.897 +0100 +0100 | -| 2019-05-29T08:41-04 | 2019-05-29 08:41:00 -0400 -0400 | -| 2014-04-26 17:24:37.3186369 | 2014-04-26 17:24:37.3186369 +0000 UTC | -| 2012-08-03 18:31:59.257000000 | 2012-08-03 18:31:59.257 +0000 UTC | -| 2014-04-26 17:24:37.123 | 2014-04-26 17:24:37.123 +0000 UTC | -| 2013-04-01 22:43 | 2013-04-01 22:43:00 +0000 UTC | -| 2013-04-01 22:43:22 | 2013-04-01 22:43:22 +0000 UTC | -| 2014-12-16 06:20:00 UTC | 2014-12-16 06:20:00 +0000 UTC | -| 2014-12-16 06:20:00 GMT | 2014-12-16 06:20:00 +0000 UTC | -| 2014-04-26 05:24:37 PM | 2014-04-26 17:24:37 +0000 UTC | -| 2014-04-26 13:13:43 +0800 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:43 +0800 +08 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0900 +0900 | -| 2012-08-03 18:31:59.257000000 +0000 UTC | 2012-08-03 18:31:59.257 +0000 UTC | -| 2015-09-30 18:48:56.35272715 +0000 UTC | 2015-09-30 18:48:56.35272715 +0000 UTC | -| 2015-02-18 00:12:00 +0000 GMT | 2015-02-18 00:12:00 +0000 UTC | -| 2015-02-18 00:12:00 +0000 UTC | 2015-02-18 00:12:00 +0000 UTC | -| 2015-02-08 03:02:00 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00 +0300 +0300 | -| 2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00.001 +0300 +0300 | -| 2017-07-19 03:21:51+00:00 | 2017-07-19 03:21:51 +0000 UTC | -| 2014-04-26 | 2014-04-26 00:00:00 +0000 UTC | -| 2014-04 | 2014-04-01 00:00:00 +0000 UTC | -| 2014 | 2014-01-01 00:00:00 +0000 UTC | -| 2014-05-11 08:20:13,787 | 2014-05-11 08:20:13.787 +0000 UTC | -| 2020-07-20+08:00 | 2020-07-20 00:00:00 +0800 +0800 | -| 3.31.2014 | 2014-03-31 00:00:00 +0000 UTC | -| 03.31.2014 | 2014-03-31 00:00:00 +0000 UTC | -| 08.21.71 | 1971-08-21 00:00:00 +0000 UTC | -| 2014.03 | 2014-03-01 00:00:00 +0000 UTC | -| 2014.03.30 | 2014-03-30 00:00:00 +0000 UTC | -| 20140601 | 2014-06-01 00:00:00 +0000 UTC | -| 20140722105203 | 2014-07-22 10:52:03 +0000 UTC | -| 171113 14:14:20 | 2017-11-13 14:14:20 +0000 UTC | -| 1332151919 | 2012-03-19 10:11:59 +0000 UTC | -| 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC | -| 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC | -| 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC | -+-------------------------------------------------------+-----------------------------------------+ -*/ - -``` diff --git a/vendor/github.com/araddon/dateparse/parseany.go b/vendor/github.com/araddon/dateparse/parseany.go deleted file mode 100644 index b9668b21b..000000000 --- a/vendor/github.com/araddon/dateparse/parseany.go +++ /dev/null @@ -1,2189 +0,0 @@ -// Package dateparse parses date-strings without knowing the format -// in advance, using a fast lex based approach to eliminate shotgun -// attempts. It leans towards US style dates when there is a conflict. -package dateparse - -import ( - "fmt" - "strconv" - "strings" - "time" - "unicode" - "unicode/utf8" -) - -// func init() { -// gou.SetupLogging("debug") -// gou.SetColorOutput() -// } - -var days = []string{ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun", - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", - "sunday", -} - -var months = []string{ - "january", - "february", - "march", - "april", - "may", - "june", - "july", - "august", - "september", - "october", - "november", - "december", -} - -type dateState uint8 -type timeState uint8 - -const ( - dateStart dateState = iota // 0 - dateDigit - dateDigitSt - dateYearDash - dateYearDashAlphaDash - dateYearDashDash - dateYearDashDashWs // 5 - dateYearDashDashT - dateYearDashDashOffset - dateDigitDash - dateDigitDashAlpha - dateDigitDashAlphaDash // 10 - dateDigitDot - dateDigitDotDot - dateDigitSlash - dateDigitYearSlash - dateDigitSlashAlpha // 15 - dateDigitColon - dateDigitChineseYear - dateDigitChineseYearWs - dateDigitWs - dateDigitWsMoYear // 20 - dateDigitWsMolong - dateAlpha - dateAlphaWs - dateAlphaWsDigit - dateAlphaWsDigitMore // 25 - dateAlphaWsDigitMoreWs - dateAlphaWsDigitMoreWsYear - dateAlphaWsMonth - dateAlphaWsDigitYearmaybe - dateAlphaWsMonthMore - dateAlphaWsMonthSuffix - dateAlphaWsMore - dateAlphaWsAtTime - dateAlphaWsAlpha - dateAlphaWsAlphaYearmaybe // 35 - dateAlphaPeriodWsDigit - dateWeekdayComma - dateWeekdayAbbrevComma -) -const ( - // Time state - timeIgnore timeState = iota // 0 - timeStart - timeWs - timeWsAlpha - timeWsAlphaWs - timeWsAlphaZoneOffset // 5 - timeWsAlphaZoneOffsetWs - timeWsAlphaZoneOffsetWsYear - timeWsAlphaZoneOffsetWsExtra - timeWsAMPMMaybe - timeWsAMPM // 10 - timeWsOffset - timeWsOffsetWs // 12 - timeWsOffsetColonAlpha - timeWsOffsetColon - timeWsYear // 15 - timeOffset - timeOffsetColon - timeAlpha - timePeriod - timePeriodOffset // 20 - timePeriodOffsetColon - timePeriodOffsetColonWs - timePeriodWs - timePeriodWsAlpha - timePeriodWsOffset // 25 - timePeriodWsOffsetWs - timePeriodWsOffsetWsAlpha - timePeriodWsOffsetColon - timePeriodWsOffsetColonAlpha - timeZ - timeZDigit -) - -var ( - // ErrAmbiguousMMDD for date formats such as 04/02/2014 the mm/dd vs dd/mm are - // ambiguous, so it is an error for strict parse rules. - ErrAmbiguousMMDD = fmt.Errorf("This date has ambiguous mm/dd vs dd/mm type format") -) - -func unknownErr(datestr string) error { - return fmt.Errorf("Could not find format for %q", datestr) -} - -// ParseAny parse an unknown date format, detect the layout. -// Normal parse. Equivalent Timezone rules as time.Parse(). -// NOTE: please see readme on mmdd vs ddmm ambiguous dates. -func ParseAny(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// ParseIn with Location, equivalent to time.ParseInLocation() timezone/offset -// rules. Using location arg, if timezone/offset info exists in the -// datestring, it uses the given location rules for any zone interpretation. -// That is, MST means one thing when using America/Denver and something else -// in other locations. -func ParseIn(datestr string, loc *time.Location, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, loc, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// ParseLocal Given an unknown date format, detect the layout, -// using time.Local, parse. -// -// Set Location to time.Local. Same as ParseIn Location but lazily uses -// the global time.Local variable for Location argument. -// -// denverLoc, _ := time.LoadLocation("America/Denver") -// time.Local = denverLoc -// -// t, err := dateparse.ParseLocal("3/1/2014") -// -// Equivalent to: -// -// t, err := dateparse.ParseIn("3/1/2014", denverLoc) -// -func ParseLocal(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, time.Local, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// MustParse parse a date, and panic if it can't be parsed. Used for testing. -// Not recommended for most use-cases. -func MustParse(datestr string, opts ...ParserOption) time.Time { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - panic(err.Error()) - } - t, err := p.parse() - if err != nil { - panic(err.Error()) - } - return t -} - -// ParseFormat parse's an unknown date-time string and returns a layout -// string that can parse this (and exact same format) other date-time strings. -// -// layout, err := dateparse.ParseFormat("2013-02-01 00:00:00") -// // layout = "2006-01-02 15:04:05" -// -func ParseFormat(datestr string, opts ...ParserOption) (string, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return "", err - } - _, err = p.parse() - if err != nil { - return "", err - } - return string(p.format), nil -} - -// ParseStrict parse an unknown date format. IF the date is ambigous -// mm/dd vs dd/mm then return an error. These return errors: 3.3.2014 , 8/8/71 etc -func ParseStrict(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return time.Time{}, err - } - if p.ambiguousMD { - return time.Time{}, ErrAmbiguousMMDD - } - return p.parse() -} - -func parseTime(datestr string, loc *time.Location, opts ...ParserOption) (p *parser, err error) { - - p = newParser(datestr, loc, opts...) - if p.retryAmbiguousDateWithSwap { - // month out of range signifies that a day/month swap is the correct solution to an ambiguous date - // this is because it means that a day is being interpreted as a month and overflowing the valid value for that - // by retrying in this case, we can fix a common situation with no assumptions - defer func() { - if p != nil && p.ambiguousMD { - // if it errors out with the following error, swap before we - // get out of this function to reduce scope it needs to be applied on - _, err := p.parse() - if err != nil && strings.Contains(err.Error(), "month out of range") { - // create the option to reverse the preference - preferMonthFirst := PreferMonthFirst(!p.preferMonthFirst) - // turn off the retry to avoid endless recursion - retryAmbiguousDateWithSwap := RetryAmbiguousDateWithSwap(false) - modifiedOpts := append(opts, preferMonthFirst, retryAmbiguousDateWithSwap) - p, err = parseTime(datestr, time.Local, modifiedOpts...) - } - } - - }() - } - - i := 0 - - // General strategy is to read rune by rune through the date looking for - // certain hints of what type of date we are dealing with. - // Hopefully we only need to read about 5 or 6 bytes before - // we figure it out and then attempt a parse -iterRunes: - for ; i < len(datestr); i++ { - //r := rune(datestr[i]) - r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) - if bytesConsumed > 1 { - i += (bytesConsumed - 1) - } - - // gou.Debugf("i=%d r=%s state=%d %s", i, string(r), p.stateDate, datestr) - switch p.stateDate { - case dateStart: - if unicode.IsDigit(r) { - p.stateDate = dateDigit - } else if unicode.IsLetter(r) { - p.stateDate = dateAlpha - } else { - return nil, unknownErr(datestr) - } - case dateDigit: - - switch r { - case '-', '\u2212': - // 2006-01-02 - // 2013-Feb-03 - // 13-Feb-03 - // 29-Jun-2016 - if i == 4 { - p.stateDate = dateYearDash - p.yeari = 0 - p.yearlen = i - p.moi = i + 1 - p.set(0, "2006") - } else { - p.stateDate = dateDigitDash - } - case '/': - // 08/May/2005 - // 03/31/2005 - // 2014/02/24 - p.stateDate = dateDigitSlash - if i == 4 { - // 2014/02/24 - Year first / - p.yearlen = i // since it was start of datestr, i=len - p.moi = i + 1 - p.setYear() - p.stateDate = dateDigitYearSlash - } else { - // Either Ambiguous dd/mm vs mm/dd OR dd/month/yy - // 08/May/2005 - // 03/31/2005 - // 31/03/2005 - if i+2 < len(p.datestr) && unicode.IsLetter(rune(datestr[i+1])) { - // 08/May/2005 - p.stateDate = dateDigitSlashAlpha - p.moi = i + 1 - p.daylen = 2 - p.dayi = 0 - p.setDay() - continue - } - // Ambiguous dd/mm vs mm/dd the bane of date-parsing - // 03/31/2005 - // 31/03/2005 - p.ambiguousMD = true - if p.preferMonthFirst { - if p.molen == 0 { - // 03/31/2005 - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - } else { - if p.daylen == 0 { - p.daylen = i - p.setDay() - p.moi = i + 1 - } - } - - } - - case ':': - // 03/31/2005 - // 2014/02/24 - p.stateDate = dateDigitColon - if i == 4 { - p.yearlen = i - p.moi = i + 1 - p.setYear() - } else { - p.ambiguousMD = true - if p.preferMonthFirst { - if p.molen == 0 { - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - } - } - - case '.': - // 3.31.2014 - // 08.21.71 - // 2014.05 - p.stateDate = dateDigitDot - if i == 4 { - p.yearlen = i - p.moi = i + 1 - p.setYear() - } else { - p.ambiguousMD = true - p.moi = 0 - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - - case ' ': - // 18 January 2018 - // 8 January 2018 - // 8 jan 2018 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - if i == 6 { - p.stateDate = dateDigitSt - } else { - p.stateDate = dateDigitWs - p.dayi = 0 - p.daylen = i - } - case '年': - // Chinese Year - p.stateDate = dateDigitChineseYear - case ',': - return nil, unknownErr(datestr) - default: - continue - } - p.part1Len = i - - case dateDigitSt: - p.set(0, "060102") - i = i - 1 - p.stateTime = timeStart - break iterRunes - case dateYearDash: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // 2020-08-17T17:00:00:000+0100 - // dateYearDashDashWs - // 2013-04-01 22:43:22 - // dateYearDashAlphaDash - // 2013-Feb-03 - switch r { - case '-': - p.molen = i - p.moi - p.dayi = i + 1 - p.stateDate = dateYearDashDash - p.setMonth() - default: - if unicode.IsLetter(r) { - p.stateDate = dateYearDashAlphaDash - } - } - - case dateYearDashDash: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // dateYearDashDashWs - // 2013-04-01 22:43:22 - // dateYearDashDashOffset - // 2020-07-20+00:00 - switch r { - case '+', '-': - p.offseti = i - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashOffset - p.setDay() - case ' ': - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashWs - p.stateTime = timeStart - p.setDay() - break iterRunes - case 'T': - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashT - p.stateTime = timeStart - p.setDay() - break iterRunes - } - - case dateYearDashDashT: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // 2020-08-17T17:00:00:000+0100 - - case dateYearDashDashOffset: - // 2020-07-20+00:00 - switch r { - case ':': - p.set(p.offseti, "-07:00") - // case ' ': - // return nil, unknownErr(datestr) - } - - case dateYearDashAlphaDash: - // 2013-Feb-03 - switch r { - case '-': - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.dayi = i + 1 - } - case dateDigitDash: - // 13-Feb-03 - // 29-Jun-2016 - if unicode.IsLetter(r) { - p.stateDate = dateDigitDashAlpha - p.moi = i - } else { - return nil, unknownErr(datestr) - } - case dateDigitDashAlpha: - // 13-Feb-03 - // 28-Feb-03 - // 29-Jun-2016 - switch r { - case '-': - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.yeari = i + 1 - p.stateDate = dateDigitDashAlphaDash - } - - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 dd-month(alpha)-yyyy - switch r { - case ' ': - // we need to find if this was 4 digits, aka year - // or 2 digits which makes it ambiguous year/day - length := i - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } - p.stateTime = timeStart - break iterRunes - } - - case dateDigitYearSlash: - // 2014/07/10 06:55:38.156283 - // I honestly don't know if this format ever shows up as yyyy/ - - switch r { - case ' ', ':': - p.stateTime = timeStart - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - break iterRunes - case '/': - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.dayi = i + 1 - } - } - - case dateDigitSlashAlpha: - // 06/May/2008 - - switch r { - case '/': - // | - // 06/May/2008 - if p.molen == 0 { - p.set(p.moi, "Jan") - p.yeari = i + 1 - } - // We aren't breaking because we are going to re-use this case - // to find where the date starts, and possible time begins - case ' ', ':': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } - break iterRunes - } - - case dateDigitSlash: - // 03/19/2012 10:11:59 - // 04/2/2014 03:00:37 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 3/1/2014 - // 10/13/2014 - // 01/02/2006 - // 1/2/06 - - switch r { - case '/': - // This is the 2nd / so now we should know start pts of all of the dd, mm, yy - if p.preferMonthFirst { - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - } - } else { - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.yeari = i + 1 - } - } - // Note no break, we are going to pass by and re-enter this dateDigitSlash - // and look for ending (space) or not (just date) - case ' ': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } - break iterRunes - } - - case dateDigitColon: - // 2014:07:10 06:55:38.156283 - // 03:19:2012 10:11:59 - // 04:2:2014 03:00:37 - // 3:1:2012 10:11:59 - // 4:8:2014 22:05 - // 3:1:2014 - // 10:13:2014 - // 01:02:2006 - // 1:2:06 - - switch r { - case ' ': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } else if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - break iterRunes - case ':': - if p.yearlen > 0 { - // 2014:07:10 06:55:38.156283 - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.dayi = i + 1 - } - } else if p.preferMonthFirst { - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - } - } - } - - case dateDigitWs: - // 18 January 2018 - // 8 January 2018 - // 8 jan 2018 - // 1 jan 18 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ' ': - p.yeari = i + 1 - //p.yearlen = 4 - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - p.stateTime = timeStart - if i > p.daylen+len(" Sep") { // November etc - // If len greather than space + 3 it must be full month - p.stateDate = dateDigitWsMolong - } else { - // If len=3, the might be Feb or May? Ie ambigous abbreviated but - // we can parse may with either. BUT, that means the - // format may not be correct? - // mo := strings.ToLower(datestr[p.daylen+1 : i]) - p.moi = p.daylen + 1 - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.stateDate = dateDigitWsMoYear - } - } - - case dateDigitWsMoYear: - // 8 jan 2018 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ',': - p.yearlen = i - p.yeari - p.setYear() - i++ - break iterRunes - case ' ': - p.yearlen = i - p.yeari - p.setYear() - break iterRunes - } - case dateDigitWsMolong: - // 18 January 2018 - // 8 January 2018 - - case dateDigitChineseYear: - // dateDigitChineseYear - // 2014年04月08日 - // weekday %Y年%m月%e日 %A %I:%M %p - // 2013年07月18日 星期四 10:27 上午 - if r == ' ' { - p.stateDate = dateDigitChineseYearWs - break - } - case dateDigitDot: - // This is the 2nd period - // 3.31.2014 - // 08.21.71 - // 2014.05 - // 2018.09.30 - if r == '.' { - if p.moi == 0 { - // 3.31.2014 - p.daylen = i - p.dayi - p.yeari = i + 1 - p.setDay() - p.stateDate = dateDigitDotDot - } else { - // 2018.09.30 - //p.molen = 2 - p.molen = i - p.moi - p.dayi = i + 1 - p.setMonth() - p.stateDate = dateDigitDotDot - } - } - case dateDigitDotDot: - // iterate all the way through - case dateAlpha: - // dateAlphaWS - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // dateAlphaWSDigit - // May 8, 2009 5:57:51 PM - // oct 1, 1970 - // dateAlphaWsMonth - // April 8, 2009 - // dateAlphaWsMore - // dateAlphaWsAtTime - // January 02, 2006 at 3:04pm MST-07 - // - // dateAlphaPeriodWsDigit - // oct. 1, 1970 - // dateWeekdayComma - // Monday, 02 Jan 2006 15:04:05 MST - // Monday, 02-Jan-06 15:04:05 MST - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // dateWeekdayAbbrevComma - // Mon, 02 Jan 2006 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // Mon, 02-Jan-06 15:04:05 MST - switch { - case r == ' ': - // X - // April 8, 2009 - if i > 3 { - // Check to see if the alpha is name of month? or Day? - month := strings.ToLower(datestr[0:i]) - if isMonthFull(month) { - p.fullMonth = month - // len(" 31, 2018") = 9 - if len(datestr[i:]) < 10 { - // April 8, 2009 - p.stateDate = dateAlphaWsMonth - } else { - p.stateDate = dateAlphaWsMore - } - p.dayi = i + 1 - break - } - - } else { - // This is possibly ambiguous? May will parse as either though. - // So, it could return in-correct format. - // dateAlphaWs - // May 05, 2005, 05:05:05 - // May 05 2005, 05:05:05 - // Jul 05, 2005, 05:05:05 - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // skip & return to dateStart - // Tue 05 May 2020, 05:05:05 - // Mon Jan 2 15:04:05 2006 - - maybeDay := strings.ToLower(datestr[0:i]) - if isDay(maybeDay) { - // using skip throws off indices used by other code; saner to restart - return parseTime(datestr[i+1:], loc) - } - p.stateDate = dateAlphaWs - } - - case r == ',': - // Mon, 02 Jan 2006 - - if i == 3 { - p.stateDate = dateWeekdayAbbrevComma - p.set(0, "Mon") - } else { - p.stateDate = dateWeekdayComma - p.skip = i + 2 - i++ - // TODO: lets just make this "skip" as we don't need - // the mon, monday, they are all superfelous and not needed - // just lay down the skip, no need to fill and then skip - } - case r == '.': - // sept. 28, 2017 - // jan. 28, 2017 - p.stateDate = dateAlphaPeriodWsDigit - if i == 3 { - p.molen = i - p.set(0, "Jan") - } else if i == 4 { - // gross - datestr = datestr[0:i-1] + datestr[i:] - return parseTime(datestr, loc, opts...) - } else { - return nil, unknownErr(datestr) - } - } - - case dateAlphaWs: - // dateAlphaWsAlpha - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // Mon Aug 10 15:44:11 UTC+0100 2015 - // dateAlphaWsDigit - // May 8, 2009 5:57:51 PM - // May 8 2009 5:57:51 PM - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - // oct 1, 1970 - // oct 7, '70 - switch { - case unicode.IsLetter(r): - p.set(0, "Mon") - p.stateDate = dateAlphaWsAlpha - p.set(i, "Jan") - case unicode.IsDigit(r): - p.set(0, "Jan") - p.stateDate = dateAlphaWsDigit - p.dayi = i - } - - case dateAlphaWsDigit: - // May 8, 2009 5:57:51 PM - // May 8 2009 5:57:51 PM - // oct 1, 1970 - // oct 7, '70 - // oct. 7, 1970 - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - if r == ',' { - p.daylen = i - p.dayi - p.setDay() - p.stateDate = dateAlphaWsDigitMore - } else if r == ' ' { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitYearmaybe - p.stateTime = timeStart - } else if unicode.IsLetter(r) { - p.stateDate = dateAlphaWsMonthSuffix - i-- - } - case dateAlphaWsDigitYearmaybe: - // x - // May 8 2009 5:57:51 PM - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - // Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if r == ':' { - // Guessed wrong; was not a year - i = i - 3 - p.stateDate = dateAlphaWsDigit - p.yeari = 0 - break iterRunes - } else if r == ' ' { - // must be year format, not 15:04 - p.yearlen = i - p.yeari - p.setYear() - break iterRunes - } - case dateAlphaWsDigitMore: - // x - // May 8, 2009 5:57:51 PM - // May 05, 2005, 05:05:05 - // May 05 2005, 05:05:05 - // oct 1, 1970 - // oct 7, '70 - if r == ' ' { - p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitMoreWs - } - case dateAlphaWsDigitMoreWs: - // x - // May 8, 2009 5:57:51 PM - // May 05, 2005, 05:05:05 - // oct 1, 1970 - // oct 7, '70 - switch r { - case '\'': - p.yeari = i + 1 - case ' ', ',': - // x - // May 8, 2009 5:57:51 PM - // x - // May 8, 2009, 5:57:51 PM - p.stateDate = dateAlphaWsDigitMoreWsYear - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - - case dateAlphaWsMonth: - // April 8, 2009 - // April 8 2009 - switch r { - case ' ', ',': - // x - // June 8, 2009 - // x - // June 8 2009 - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - case 's', 'S', 'r', 'R', 't', 'T', 'n', 'N': - // st, rd, nd, st - i-- - p.stateDate = dateAlphaWsMonthSuffix - default: - if p.daylen > 0 && p.yeari == 0 { - p.yeari = i - } - } - case dateAlphaWsMonthMore: - // X - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 02, 2006 15:04:05 - // January 02 2006 15:04:05 - switch r { - case ',': - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - i++ - break iterRunes - case ' ': - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - case dateAlphaWsMonthSuffix: - // x - // April 8th, 2009 - // April 8th 2009 - switch r { - case 't', 'T': - if p.nextIs(i, 'h') || p.nextIs(i, 'H') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 'n', 'N': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 's', 'S': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 'r', 'R': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - } - case dateAlphaWsMore: - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 2nd, 2006, 15:04:05 - // January 2nd 2006, 15:04:05 - // September 17, 2012 at 5:00pm UTC-05 - switch { - case r == ',': - // x - // January 02, 2006, 15:04:05 - if p.nextIs(i, ' ') { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 2 - p.stateDate = dateAlphaWsMonthMore - i++ - } - - case r == ' ': - // x - // January 02 2006, 15:04:05 - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - p.stateDate = dateAlphaWsMonthMore - case unicode.IsDigit(r): - // XX - // January 02, 2006, 15:04:05 - continue - case unicode.IsLetter(r): - // X - // January 2nd, 2006, 15:04:05 - p.daylen = i - p.dayi - p.setDay() - p.stateDate = dateAlphaWsMonthSuffix - i-- - } - - case dateAlphaPeriodWsDigit: - // oct. 7, '70 - switch { - case r == ' ': - // continue - case unicode.IsDigit(r): - p.stateDate = dateAlphaWsDigit - p.dayi = i - default: - return p, unknownErr(datestr) - } - case dateWeekdayComma: - // Monday, 02 Jan 2006 15:04:05 MST - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // Monday, 02-Jan-06 15:04:05 MST - if p.dayi == 0 { - p.dayi = i - } - switch r { - case ' ', '-': - if p.moi == 0 { - p.moi = i + 1 - p.daylen = i - p.dayi - p.setDay() - } else if p.yeari == 0 { - p.yeari = i + 1 - p.molen = i - p.moi - p.set(p.moi, "Jan") - } else { - p.stateTime = timeStart - break iterRunes - } - } - case dateWeekdayAbbrevComma: - // Mon, 02 Jan 2006 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Thu, 4 Jan 2018 17:53:36 +0000 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // Mon, 02-Jan-06 15:04:05 MST - switch r { - case ' ', '-': - if p.dayi == 0 { - p.dayi = i + 1 - } else if p.moi == 0 { - p.daylen = i - p.dayi - p.setDay() - p.moi = i + 1 - } else if p.yeari == 0 { - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.yeari = i + 1 - } else { - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - } - - default: - break iterRunes - } - } - p.coalesceDate(i) - if p.stateTime == timeStart { - // increment first one, since the i++ occurs at end of loop - if i < len(p.datestr) { - i++ - } - // ensure we skip any whitespace prefix - for ; i < len(datestr); i++ { - r := rune(datestr[i]) - if r != ' ' { - break - } - } - - iterTimeRunes: - for ; i < len(datestr); i++ { - r := rune(datestr[i]) - - // gou.Debugf("i=%d r=%s state=%d iterTimeRunes %s %s", i, string(r), p.stateTime, p.ds(), p.ts()) - - switch p.stateTime { - case timeStart: - // 22:43:22 - // 22:43 - // timeComma - // 08:20:13,787 - // timeWs - // 05:24:37 PM - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 00:12:00 +0000 UTC - // 22:18:00 +0000 UTC m=+0.000000001 - // 15:04:05 -0700 - // 15:04:05 -07:00 - // 15:04:05 2008 - // timeOffset - // 03:21:51+00:00 - // 19:55:00+0100 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // 00:00:00.000 +0000 UTC - // timePeriodWsAlpha - // 06:20:00.000 UTC - if p.houri == 0 { - p.houri = i - } - switch r { - case ',': - // hm, lets just swap out comma for period. for some reason go - // won't parse it. - // 2014-05-11 08:20:13,787 - ds := []byte(p.datestr) - ds[i] = '.' - return parseTime(string(ds), loc, opts...) - case '-', '+': - // 03:21:51+00:00 - p.stateTime = timeOffset - if p.seci == 0 { - // 22:18+0530 - p.minlen = i - p.mini - } else { - if p.seclen == 0 { - p.seclen = i - p.seci - } - if p.msi > 0 && p.mslen == 0 { - p.mslen = i - p.msi - } - } - p.offseti = i - case '.': - p.stateTime = timePeriod - p.seclen = i - p.seci - p.msi = i + 1 - case 'Z': - p.stateTime = timeZ - if p.seci == 0 { - p.minlen = i - p.mini - } else { - p.seclen = i - p.seci - } - // (Z)ulu time - p.loc = time.UTC - case 'a', 'A': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - // x - // September 17, 2012 at 5:00pm UTC-05 - i++ // skip t - if p.nextIs(i, ' ') { - // x - // September 17, 2012 at 5:00pm UTC-05 - i++ // skip ' - p.houri = 0 // reset hour - } - } else { - switch { - case r == 'a' && p.nextIs(i, 'm'): - p.coalesceTime(i) - p.set(i, "am") - case r == 'A' && p.nextIs(i, 'M'): - p.coalesceTime(i) - p.set(i, "PM") - } - } - - case 'p', 'P': - // Could be AM/PM - switch { - case r == 'p' && p.nextIs(i, 'm'): - p.coalesceTime(i) - p.set(i, "pm") - case r == 'P' && p.nextIs(i, 'M'): - p.coalesceTime(i) - p.set(i, "PM") - } - case ' ': - p.coalesceTime(i) - p.stateTime = timeWs - case ':': - if p.mini == 0 { - p.mini = i + 1 - p.hourlen = i - p.houri - } else if p.seci == 0 { - p.seci = i + 1 - p.minlen = i - p.mini - } else if p.seci > 0 { - // 18:31:59:257 ms uses colon, wtf - p.seclen = i - p.seci - p.set(p.seci, "05") - p.msi = i + 1 - - // gross, gross, gross. manipulating the datestr is horrible. - // https://github.com/araddon/dateparse/issues/117 - // Could not get the parsing to work using golang time.Parse() without - // replacing that colon with period. - p.set(i, ".") - datestr = datestr[0:i] + "." + datestr[i+1:] - p.datestr = datestr - } - } - case timeOffset: - // 19:55:00+0100 - // timeOffsetColon - // 15:04:05+07:00 - // 15:04:05-07:00 - if r == ':' { - p.stateTime = timeOffsetColon - } - case timeWs: - // timeWsAlpha - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 15:44:11 UTC+0100 2015 - // 18:04:07 GMT+0100 (GMT Daylight Time) - // 17:57:51 MST 2009 - // timeWsAMPMMaybe - // 05:24:37 PM - // timeWsOffset - // 15:04:05 -0700 - // 00:12:00 +0000 UTC - // timeWsOffsetColon - // 15:04:05 -07:00 - // 17:57:51 -0700 2009 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - // timeWsYear - // 00:12:00 2008 - // timeZ - // 15:04:05.99Z - switch r { - case 'A', 'P': - // Could be AM/PM or could be PST or similar - p.tzi = i - p.stateTime = timeWsAMPMMaybe - case '+', '-': - p.offseti = i - p.stateTime = timeWsOffset - default: - if unicode.IsLetter(r) { - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 15:44:11 UTC+0100 2015 - // 17:57:51 MST 2009 - p.tzi = i - p.stateTime = timeWsAlpha - } else if unicode.IsDigit(r) { - // 00:12:00 2008 - p.stateTime = timeWsYear - p.yeari = i - } - } - case timeWsAlpha: - // 06:20:00 UTC - // 06:20:00 UTC-05 - // timeWsAlphaWs - // 17:57:51 MST 2009 - // timeWsAlphaZoneOffset - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - switch r { - case '+', '-': - p.tzlen = i - p.tzi - if p.tzlen == 4 { - p.set(p.tzi, " MST") - } else if p.tzlen == 3 { - p.set(p.tzi, "MST") - } - p.stateTime = timeWsAlphaZoneOffset - p.offseti = i - case ' ': - // 17:57:51 MST 2009 - // 17:57:51 MST - p.tzlen = i - p.tzi - if p.tzlen == 4 { - p.set(p.tzi, " MST") - } else if p.tzlen == 3 { - p.set(p.tzi, "MST") - } - p.stateTime = timeWsAlphaWs - p.yeari = i + 1 - } - case timeWsAlphaWs: - // 17:57:51 MST 2009 - - case timeWsAlphaZoneOffset: - // 06:20:00 UTC-05 - // timeWsAlphaZoneOffset - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - switch r { - case ' ': - p.set(p.offseti, "-0700") - if p.yeari == 0 { - p.yeari = i + 1 - } - p.stateTime = timeWsAlphaZoneOffsetWs - } - case timeWsAlphaZoneOffsetWs: - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - if unicode.IsDigit(r) { - p.stateTime = timeWsAlphaZoneOffsetWsYear - } else { - p.extra = i - 1 - p.stateTime = timeWsAlphaZoneOffsetWsExtra - } - case timeWsAlphaZoneOffsetWsYear: - // 15:44:11 UTC+0100 2015 - if unicode.IsDigit(r) { - p.yearlen = i - p.yeari + 1 - if p.yearlen == 4 { - p.setYear() - } - } - case timeWsAMPMMaybe: - // timeWsAMPMMaybe - // timeWsAMPM - // 05:24:37 PM - // timeWsAlpha - // 00:12:00 PST - // 15:44:11 UTC+0100 2015 - if r == 'M' { - //return parse("2006-01-02 03:04:05 PM", datestr, loc) - p.stateTime = timeWsAMPM - p.set(i-1, "PM") - if p.hourlen == 2 { - p.set(p.houri, "03") - } else if p.hourlen == 1 { - p.set(p.houri, "3") - } - } else { - p.stateTime = timeWsAlpha - } - - case timeWsOffset: - // timeWsOffset - // 15:04:05 -0700 - // timeWsOffsetWsOffset - // 17:57:51 -0700 -07 - // timeWsOffsetWs - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - switch r { - case ':': - p.stateTime = timeWsOffsetColon - case ' ': - p.set(p.offseti, "-0700") - p.yeari = i + 1 - p.stateTime = timeWsOffsetWs - } - case timeWsOffsetWs: - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // w Extra - // 17:57:51 -0700 -07 - switch r { - case '=': - // eff you golang - if datestr[i-1] == 'm' { - p.extra = i - 2 - p.trimExtra() - break - } - case '+', '-', '(': - // This really doesn't seem valid, but for some reason when round-tripping a go date - // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. - // 00:00:00 +0300 +03 - // 00:00:00 +0300 +0300 - p.extra = i - 1 - p.stateTime = timeWsOffset - p.trimExtra() - break - default: - switch { - case unicode.IsDigit(r): - p.yearlen = i - p.yeari + 1 - if p.yearlen == 4 { - p.setYear() - } - case unicode.IsLetter(r): - // 15:04:05 -0700 MST - if p.tzi == 0 { - p.tzi = i - } - } - } - - case timeWsOffsetColon: - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +00:00 UTC - p.stateTime = timeWsOffsetColonAlpha - break iterTimeRunes - } - case timePeriod: - // 15:04:05.999999999+07:00 - // 15:04:05.999999999-07:00 - // 15:04:05.999999+07:00 - // 15:04:05.999999-07:00 - // 15:04:05.999+07:00 - // 15:04:05.999-07:00 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // With Extra - // 00:00:00.000 +0300 +03 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // timePeriodWsAlpha - // 06:20:00.000 UTC - switch r { - case ' ': - p.mslen = i - p.msi - p.stateTime = timePeriodWs - case '+', '-': - // This really shouldn't happen - p.mslen = i - p.msi - p.offseti = i - p.stateTime = timePeriodOffset - default: - if unicode.IsLetter(r) { - // 06:20:00.000 UTC - p.mslen = i - p.msi - p.stateTime = timePeriodWsAlpha - } - } - case timePeriodOffset: - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // 13:31:51.999-07:00 MST - if r == ':' { - p.stateTime = timePeriodOffsetColon - } - case timePeriodOffsetColon: - // timePeriodOffset - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // 13:31:51.999 -07:00 MST - switch r { - case ' ': - p.set(p.offseti, "-07:00") - p.stateTime = timePeriodOffsetColonWs - p.tzi = i + 1 - } - case timePeriodOffsetColonWs: - // continue - case timePeriodWs: - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // timePeriodWsOffsetColon - // 13:31:51.999 -07:00 MST - // timePeriodWsAlpha - // 06:20:00.000 UTC - if p.offseti == 0 { - p.offseti = i - } - switch r { - case '+', '-': - p.mslen = i - p.msi - 1 - p.stateTime = timePeriodWsOffset - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - p.stateTime = timePeriodWsOffsetWsAlpha - break iterTimeRunes - } - } - - case timePeriodWsOffset: - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // With Extra - // 00:00:00.000 +0300 +03 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 03:02:00.001 +0300 MSK m=+0.000000001 - // timePeriodWsOffsetColon - // 13:31:51.999 -07:00 MST - // timePeriodWsAlpha - // 06:20:00.000 UTC - switch r { - case ':': - p.stateTime = timePeriodWsOffsetColon - case ' ': - p.set(p.offseti, "-0700") - case '+', '-': - // This really doesn't seem valid, but for some reason when round-tripping a go date - // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. - // 00:00:00.000 +0300 +03 - // 00:00:00.000 +0300 +0300 - p.extra = i - 1 - p.trimExtra() - break - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 03:02:00.001 +0300 MSK m=+0.000000001 - p.stateTime = timePeriodWsOffsetWsAlpha - } - } - case timePeriodWsOffsetWsAlpha: - // 03:02:00.001 +0300 MSK m=+0.000000001 - // eff you golang - if r == '=' && datestr[i-1] == 'm' { - p.extra = i - 2 - p.trimExtra() - break - } - - case timePeriodWsOffsetColon: - // 13:31:51.999 -07:00 MST - switch r { - case ' ': - p.set(p.offseti, "-07:00") - default: - if unicode.IsLetter(r) { - // 13:31:51.999 -07:00 MST - p.tzi = i - p.stateTime = timePeriodWsOffsetColonAlpha - } - } - case timePeriodWsOffsetColonAlpha: - // continue - case timeZ: - // timeZ - // 15:04:05.99Z - // With a time-zone at end after Z - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // RFC3339 = "2006-01-02T15:04:05Z07:00" - // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - if unicode.IsDigit(r) { - p.stateTime = timeZDigit - } - - } - } - - switch p.stateTime { - case timeWsAlpha: - switch len(p.datestr) - p.tzi { - case 3: - // 13:31:51.999 +01:00 CET - p.set(p.tzi, "MST") - case 4: - p.set(p.tzi, "MST") - p.extra = len(p.datestr) - 1 - p.trimExtra() - } - - case timeWsAlphaWs: - p.yearlen = i - p.yeari - p.setYear() - case timeWsYear: - p.yearlen = i - p.yeari - p.setYear() - case timeWsAlphaZoneOffsetWsExtra: - p.trimExtra() - case timeWsAlphaZoneOffset: - // 06:20:00 UTC-05 - if i-p.offseti < 4 { - p.set(p.offseti, "-07") - } else { - p.set(p.offseti, "-0700") - } - - case timePeriod: - p.mslen = i - p.msi - case timeOffset: - - switch len(p.datestr) - p.offseti { - case 0, 1, 2, 4: - return p, fmt.Errorf("TZ offset not recognized %q near %q (must be 2 or 4 digits optional colon)", datestr, string(datestr[p.offseti:])) - case 3: - // 19:55:00+01 - p.set(p.offseti, "-07") - case 5: - // 19:55:00+0100 - p.set(p.offseti, "-0700") - } - - case timeWsOffset: - p.set(p.offseti, "-0700") - case timeWsOffsetWs: - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - if p.tzi > 0 { - switch len(p.datestr) - p.tzi { - case 3: - // 13:31:51.999 +01:00 CET - p.set(p.tzi, "MST") - case 4: - // 13:31:51.999 +01:00 CEST - p.set(p.tzi, "MST ") - } - - } - case timeWsOffsetColon: - // 17:57:51 -07:00 - p.set(p.offseti, "-07:00") - case timeOffsetColon: - // 15:04:05+07:00 - p.set(p.offseti, "-07:00") - case timePeriodOffset: - // 19:55:00.799+0100 - p.set(p.offseti, "-0700") - case timePeriodOffsetColon: - p.set(p.offseti, "-07:00") - case timePeriodWsOffsetColonAlpha: - p.tzlen = i - p.tzi - switch p.tzlen { - case 3: - p.set(p.tzi, "MST") - case 4: - p.set(p.tzi, "MST ") - } - case timePeriodWsOffset: - p.set(p.offseti, "-0700") - } - p.coalesceTime(i) - } - - switch p.stateDate { - case dateDigit: - // unixy timestamps ish - // example ct type - // 1499979655583057426 19 nanoseconds - // 1499979795437000 16 micro-seconds - // 20180722105203 14 yyyyMMddhhmmss - // 1499979795437 13 milliseconds - // 1332151919 10 seconds - // 20140601 8 yyyymmdd - // 2014 4 yyyy - t := time.Time{} - if len(datestr) == len("1499979655583057426") { // 19 - // nano-seconds - if nanoSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, nanoSecs) - } - } else if len(datestr) == len("1499979795437000") { // 16 - // micro-seconds - if microSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, microSecs*1000) - } - } else if len(datestr) == len("yyyyMMddhhmmss") { // 14 - // yyyyMMddhhmmss - p.format = []byte("20060102150405") - return p, nil - } else if len(datestr) == len("1332151919000") { // 13 - if miliSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, miliSecs*1000*1000) - } - } else if len(datestr) == len("1332151919") { //10 - if secs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(secs, 0) - } - } else if len(datestr) == len("20140601") { - p.format = []byte("20060102") - return p, nil - } else if len(datestr) == len("2014") { - p.format = []byte("2006") - return p, nil - } else if len(datestr) < 4 { - return nil, fmt.Errorf("unrecognized format, too short %v", datestr) - } - if !t.IsZero() { - if loc == nil { - p.t = &t - return p, nil - } - t = t.In(loc) - p.t = &t - return p, nil - } - case dateDigitSt: - // 171113 14:14:20 - return p, nil - - case dateYearDash: - // 2006-01 - return p, nil - - case dateYearDashDash: - // 2006-01-02 - // 2006-1-02 - // 2006-1-2 - // 2006-01-2 - return p, nil - - case dateYearDashDashOffset: - /// 2020-07-20+00:00 - switch len(p.datestr) - p.offseti { - case 5: - p.set(p.offseti, "-0700") - case 6: - p.set(p.offseti, "-07:00") - } - return p, nil - - case dateYearDashAlphaDash: - // 2013-Feb-03 - // 2013-Feb-3 - p.daylen = i - p.dayi - p.setDay() - return p, nil - - case dateYearDashDashWs: - // 2013-04-01 - return p, nil - - case dateYearDashDashT: - return p, nil - - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 - length := len(datestr) - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } - - return p, nil - - case dateDigitDot: - // 2014.05 - p.molen = i - p.moi - p.setMonth() - return p, nil - - case dateDigitDotDot: - // 03.31.1981 - // 3.31.2014 - // 3.2.1981 - // 3.2.81 - // 08.21.71 - // 2018.09.30 - return p, nil - - case dateDigitWsMoYear: - // 2 Jan 2018 - // 2 Jan 18 - // 2 Jan 2018 23:59 - // 02 Jan 2018 23:59 - // 12 Feb 2006, 19:17 - return p, nil - - case dateDigitWsMolong: - // 18 January 2018 - // 8 January 2018 - if p.daylen == 2 { - p.format = []byte("02 January 2006") - return p, nil - } - p.format = []byte("2 January 2006") - return p, nil // parse("2 January 2006", datestr, loc) - - case dateAlphaWsMonth: - p.yearlen = i - p.yeari - p.setYear() - return p, nil - - case dateAlphaWsMonthMore: - return p, nil - - case dateAlphaWsDigitMoreWs: - // oct 1, 1970 - p.yearlen = i - p.yeari - p.setYear() - return p, nil - - case dateAlphaWsDigitMoreWsYear: - // May 8, 2009 5:57:51 PM - // Jun 7, 2005, 05:57:51 - return p, nil - - case dateAlphaWsAlpha: - return p, nil - - case dateAlphaWsDigit: - return p, nil - - case dateAlphaWsDigitYearmaybe: - return p, nil - - case dateDigitSlash: - // 3/1/2014 - // 10/13/2014 - // 01/02/2006 - return p, nil - - case dateDigitSlashAlpha: - // 03/Jun/2014 - return p, nil - - case dateDigitYearSlash: - // 2014/10/13 - return p, nil - - case dateDigitColon: - // 3:1:2014 - // 10:13:2014 - // 01:02:2006 - // 2014:10:13 - return p, nil - - case dateDigitChineseYear: - // dateDigitChineseYear - // 2014年04月08日 - p.format = []byte("2006年01月02日") - return p, nil - - case dateDigitChineseYearWs: - p.format = []byte("2006年01月02日 15:04:05") - return p, nil - - case dateWeekdayComma: - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // Monday, 02-Jan-06 15:04:05 MST - return p, nil - - case dateWeekdayAbbrevComma: - // Mon, 02-Jan-06 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 MST - return p, nil - - } - - return nil, unknownErr(datestr) -} - -type parser struct { - loc *time.Location - preferMonthFirst bool - retryAmbiguousDateWithSwap bool - ambiguousMD bool - stateDate dateState - stateTime timeState - format []byte - datestr string - fullMonth string - skip int - extra int - part1Len int - yeari int - yearlen int - moi int - molen int - dayi int - daylen int - houri int - hourlen int - mini int - minlen int - seci int - seclen int - msi int - mslen int - offseti int - offsetlen int - tzi int - tzlen int - t *time.Time -} - -// ParserOption defines a function signature implemented by options -// Options defined like this accept the parser and operate on the data within -type ParserOption func(*parser) error - -// PreferMonthFirst is an option that allows preferMonthFirst to be changed from its default -func PreferMonthFirst(preferMonthFirst bool) ParserOption { - return func(p *parser) error { - p.preferMonthFirst = preferMonthFirst - return nil - } -} - -// RetryAmbiguousDateWithSwap is an option that allows retryAmbiguousDateWithSwap to be changed from its default -func RetryAmbiguousDateWithSwap(retryAmbiguousDateWithSwap bool) ParserOption { - return func(p *parser) error { - p.retryAmbiguousDateWithSwap = retryAmbiguousDateWithSwap - return nil - } -} - -func newParser(dateStr string, loc *time.Location, opts ...ParserOption) *parser { - p := &parser{ - stateDate: dateStart, - stateTime: timeIgnore, - datestr: dateStr, - loc: loc, - preferMonthFirst: true, - retryAmbiguousDateWithSwap: false, - } - p.format = []byte(dateStr) - - // allow the options to mutate the parser fields from their defaults - for _, option := range opts { - option(p) - } - return p -} - -func (p *parser) nextIs(i int, b byte) bool { - if len(p.datestr) > i+1 && p.datestr[i+1] == b { - return true - } - return false -} - -func (p *parser) set(start int, val string) { - if start < 0 { - return - } - if len(p.format) < start+len(val) { - return - } - for i, r := range val { - p.format[start+i] = byte(r) - } -} -func (p *parser) setMonth() { - if p.molen == 2 { - p.set(p.moi, "01") - } else if p.molen == 1 { - p.set(p.moi, "1") - } -} - -func (p *parser) setDay() { - if p.daylen == 2 { - p.set(p.dayi, "02") - } else if p.daylen == 1 { - p.set(p.dayi, "2") - } -} -func (p *parser) setYear() { - if p.yearlen == 2 { - p.set(p.yeari, "06") - } else if p.yearlen == 4 { - p.set(p.yeari, "2006") - } -} -func (p *parser) coalesceDate(end int) { - if p.yeari > 0 { - if p.yearlen == 0 { - p.yearlen = end - p.yeari - } - p.setYear() - } - if p.moi > 0 && p.molen == 0 { - p.molen = end - p.moi - p.setMonth() - } - if p.dayi > 0 && p.daylen == 0 { - p.daylen = end - p.dayi - p.setDay() - } -} -func (p *parser) ts() string { - return fmt.Sprintf("h:(%d:%d) m:(%d:%d) s:(%d:%d)", p.houri, p.hourlen, p.mini, p.minlen, p.seci, p.seclen) -} -func (p *parser) ds() string { - return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen) -} -func (p *parser) coalesceTime(end int) { - // 03:04:05 - // 15:04:05 - // 3:04:05 - // 3:4:5 - // 15:04:05.00 - if p.houri > 0 { - if p.hourlen == 2 { - p.set(p.houri, "15") - } else if p.hourlen == 1 { - p.set(p.houri, "3") - } - } - if p.mini > 0 { - if p.minlen == 0 { - p.minlen = end - p.mini - } - if p.minlen == 2 { - p.set(p.mini, "04") - } else { - p.set(p.mini, "4") - } - } - if p.seci > 0 { - if p.seclen == 0 { - p.seclen = end - p.seci - } - if p.seclen == 2 { - p.set(p.seci, "05") - } else { - p.set(p.seci, "5") - } - } - - if p.msi > 0 { - for i := 0; i < p.mslen; i++ { - p.format[p.msi+i] = '0' - } - } -} -func (p *parser) setFullMonth(month string) { - if p.moi == 0 { - p.format = []byte(fmt.Sprintf("%s%s", "January", p.format[len(month):])) - } -} - -func (p *parser) trimExtra() { - if p.extra > 0 && len(p.format) > p.extra { - p.format = p.format[0:p.extra] - p.datestr = p.datestr[0:p.extra] - } -} - -// func (p *parser) remove(i, length int) { -// if len(p.format) > i+length { -// //append(a[:i], a[j:]...) -// p.format = append(p.format[0:i], p.format[i+length:]...) -// } -// if len(p.datestr) > i+length { -// //append(a[:i], a[j:]...) -// p.datestr = fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+length:]) -// } -// } - -func (p *parser) parse() (time.Time, error) { - if p.t != nil { - return *p.t, nil - } - if len(p.fullMonth) > 0 { - p.setFullMonth(p.fullMonth) - } - if p.skip > 0 && len(p.format) > p.skip { - p.format = p.format[p.skip:] - p.datestr = p.datestr[p.skip:] - } - - if p.loc == nil { - // gou.Debugf("parse layout=%q input=%q \ntx, err := time.Parse(%q, %q)", string(p.format), p.datestr, string(p.format), p.datestr) - return time.Parse(string(p.format), p.datestr) - } - //gou.Debugf("parse layout=%q input=%q \ntx, err := time.ParseInLocation(%q, %q, %v)", string(p.format), p.datestr, string(p.format), p.datestr, p.loc) - return time.ParseInLocation(string(p.format), p.datestr, p.loc) -} -func isDay(alpha string) bool { - for _, day := range days { - if alpha == day { - return true - } - } - return false -} -func isMonthFull(alpha string) bool { - for _, month := range months { - if alpha == month { - return true - } - } - return false -} diff --git a/vendor/github.com/jinzhu/now/Guardfile b/vendor/github.com/jinzhu/now/Guardfile new file mode 100644 index 000000000..0b860b065 --- /dev/null +++ b/vendor/github.com/jinzhu/now/Guardfile @@ -0,0 +1,3 @@ +guard 'gotest' do + watch(%r{\.go$}) +end diff --git a/vendor/github.com/araddon/dateparse/LICENSE b/vendor/github.com/jinzhu/now/License similarity index 95% rename from vendor/github.com/araddon/dateparse/LICENSE rename to vendor/github.com/jinzhu/now/License index f675ed313..037e1653e 100644 --- a/vendor/github.com/araddon/dateparse/LICENSE +++ b/vendor/github.com/jinzhu/now/License @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2017 Aaron Raddon +Copyright (c) 2013-NOW Jinzhu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/jinzhu/now/README.md b/vendor/github.com/jinzhu/now/README.md new file mode 100644 index 000000000..e81d31de6 --- /dev/null +++ b/vendor/github.com/jinzhu/now/README.md @@ -0,0 +1,137 @@ +## Now + +Now is a time toolkit for golang + +[![go report card](https://goreportcard.com/badge/github.com/jinzhu/now "go report card")](https://goreportcard.com/report/github.com/jinzhu/now) +[![test status](https://github.com/jinzhu/now/workflows/tests/badge.svg?branch=master "test status")](https://github.com/jinzhu/now/actions) +[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) + +## Install + +``` +go get -u github.com/jinzhu/now +``` + +## Usage + +Calculating time based on current time + +```go +import "github.com/jinzhu/now" + +time.Now() // 2013-11-18 17:51:49.123456789 Mon + +now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon +now.BeginningOfHour() // 2013-11-18 17:00:00 Mon +now.BeginningOfDay() // 2013-11-18 00:00:00 Mon +now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun +now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri +now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue +now.BeginningOfYear() // 2013-01-01 00:00:00 Tue + +now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon +now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon +now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon +now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat +now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat +now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue +now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue + +now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday +now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun +``` + +Calculating time based on another time + +```go +t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.Now().Location()) +now.With(t).EndOfMonth() // 2013-02-28 23:59:59.999999999 Thu +``` + +Calculating time based on configuration + +```go +location, err := time.LoadLocation("Asia/Shanghai") + +myConfig := &now.Config{ + WeekStartDay: time.Monday, + TimeLocation: location, + TimeFormats: []string{"2006-01-02 15:04:05"}, +} + +t := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.Now().Location()) // // 2013-11-18 17:51:49.123456789 Mon +myConfig.With(t).BeginningOfWeek() // 2013-11-18 00:00:00 Mon + +myConfig.Parse("2002-10-12 22:14:01") // 2002-10-12 22:14:01 +myConfig.Parse("2002-10-12 22:14") // returns error 'can't parse string as time: 2002-10-12 22:14' +``` + +### Monday/Sunday + +Don't be bothered with the `WeekStartDay` setting, you can use `Monday`, `Sunday` + +```go +now.Monday() // 2013-11-18 00:00:00 Mon +now.Monday("17:44") // 2013-11-18 17:44:00 Mon +now.Sunday() // 2013-11-24 00:00:00 Sun (Next Sunday) +now.Sunday("18:19:24") // 2013-11-24 18:19:24 Sun (Next Sunday) +now.EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of next Sunday) + +t := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.Now().Location()) // 2013-11-24 17:51:49.123456789 Sun +now.With(t).Monday() // 2013-11-18 00:00:00 Mon (Last Monday if today is Sunday) +now.With(t).Monday("17:44") // 2013-11-18 17:44:00 Mon (Last Monday if today is Sunday) +now.With(t).Sunday() // 2013-11-24 00:00:00 Sun (Beginning Of Today if today is Sunday) +now.With(t).Sunday("18:19:24") // 2013-11-24 18:19:24 Sun (Beginning Of Today if today is Sunday) +now.With(t).EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of Today if today is Sunday) +``` + +### Parse String to Time + +```go +time.Now() // 2013-11-18 17:51:49.123456789 Mon + +// Parse(string) (time.Time, error) +t, err := now.Parse("2017") // 2017-01-01 00:00:00, nil +t, err := now.Parse("2017-10") // 2017-10-01 00:00:00, nil +t, err := now.Parse("2017-10-13") // 2017-10-13 00:00:00, nil +t, err := now.Parse("1999-12-12 12") // 1999-12-12 12:00:00, nil +t, err := now.Parse("1999-12-12 12:20") // 1999-12-12 12:20:00, nil +t, err := now.Parse("1999-12-12 12:20:21") // 1999-12-12 12:20:21, nil +t, err := now.Parse("10-13") // 2013-10-13 00:00:00, nil +t, err := now.Parse("12:20") // 2013-11-18 12:20:00, nil +t, err := now.Parse("12:20:13") // 2013-11-18 12:20:13, nil +t, err := now.Parse("14") // 2013-11-18 14:00:00, nil +t, err := now.Parse("99:99") // 2013-11-18 12:20:00, Can't parse string as time: 99:99 + +// MustParse must parse string to time or it will panic +now.MustParse("2013-01-13") // 2013-01-13 00:00:00 +now.MustParse("02-17") // 2013-02-17 00:00:00 +now.MustParse("2-17") // 2013-02-17 00:00:00 +now.MustParse("8") // 2013-11-18 08:00:00 +now.MustParse("2002-10-12 22:14") // 2002-10-12 22:14:00 +now.MustParse("99:99") // panic: Can't parse string as time: 99:99 +``` + +Extend `now` to support more formats is quite easy, just update `now.TimeFormats` with other time layouts, e.g: + +```go +now.TimeFormats = append(now.TimeFormats, "02 Jan 2006 15:04") +``` + +Please send me pull requests if you want a format to be supported officially + +## Contributing + +You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do. + +# Author + +**jinzhu** + +* +* +* + +## License + +Released under the [MIT License](http://www.opensource.org/licenses/MIT). diff --git a/vendor/github.com/jinzhu/now/main.go b/vendor/github.com/jinzhu/now/main.go new file mode 100644 index 000000000..8f78bc752 --- /dev/null +++ b/vendor/github.com/jinzhu/now/main.go @@ -0,0 +1,200 @@ +// Package now is a time toolkit for golang. +// +// More details README here: https://github.com/jinzhu/now +// +// import "github.com/jinzhu/now" +// +// now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon +// now.BeginningOfDay() // 2013-11-18 00:00:00 Mon +// now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon +package now + +import "time" + +// WeekStartDay set week start day, default is sunday +var WeekStartDay = time.Sunday + +// TimeFormats default time formats will be parsed as +var TimeFormats = []string{ + "2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2", + "15:4:5", "15:4", "15", + "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST", "2006-01-02T15:04:05Z0700", "2006-01-02T15:04:05Z07", + "2006.1.2", "2006.1.2 15:04:05", "2006.01.02", "2006.01.02 15:04:05", "2006.01.02 15:04:05.999999999", + "1/2/2006", "1/2/2006 15:4:5", "2006/01/02", "20060102", "2006/01/02 15:04:05", + time.ANSIC, time.UnixDate, time.RubyDate, time.RFC822, time.RFC822Z, time.RFC850, + time.RFC1123, time.RFC1123Z, time.RFC3339, time.RFC3339Nano, + time.Kitchen, time.Stamp, time.StampMilli, time.StampMicro, time.StampNano, +} + +// Config configuration for now package +type Config struct { + WeekStartDay time.Weekday + TimeLocation *time.Location + TimeFormats []string +} + +// DefaultConfig default config +var DefaultConfig *Config + +// New initialize Now based on configuration +func (config *Config) With(t time.Time) *Now { + return &Now{Time: t, Config: config} +} + +// Parse parse string to time based on configuration +func (config *Config) Parse(strs ...string) (time.Time, error) { + if config.TimeLocation == nil { + return config.With(time.Now()).Parse(strs...) + } else { + return config.With(time.Now().In(config.TimeLocation)).Parse(strs...) + } +} + +// MustParse must parse string to time or will panic +func (config *Config) MustParse(strs ...string) time.Time { + if config.TimeLocation == nil { + return config.With(time.Now()).MustParse(strs...) + } else { + return config.With(time.Now().In(config.TimeLocation)).MustParse(strs...) + } +} + +// Now now struct +type Now struct { + time.Time + *Config +} + +// With initialize Now with time +func With(t time.Time) *Now { + config := DefaultConfig + if config == nil { + config = &Config{ + WeekStartDay: WeekStartDay, + TimeFormats: TimeFormats, + } + } + + return &Now{Time: t, Config: config} +} + +// New initialize Now with time +func New(t time.Time) *Now { + return With(t) +} + +// BeginningOfMinute beginning of minute +func BeginningOfMinute() time.Time { + return With(time.Now()).BeginningOfMinute() +} + +// BeginningOfHour beginning of hour +func BeginningOfHour() time.Time { + return With(time.Now()).BeginningOfHour() +} + +// BeginningOfDay beginning of day +func BeginningOfDay() time.Time { + return With(time.Now()).BeginningOfDay() +} + +// BeginningOfWeek beginning of week +func BeginningOfWeek() time.Time { + return With(time.Now()).BeginningOfWeek() +} + +// BeginningOfMonth beginning of month +func BeginningOfMonth() time.Time { + return With(time.Now()).BeginningOfMonth() +} + +// BeginningOfQuarter beginning of quarter +func BeginningOfQuarter() time.Time { + return With(time.Now()).BeginningOfQuarter() +} + +// BeginningOfYear beginning of year +func BeginningOfYear() time.Time { + return With(time.Now()).BeginningOfYear() +} + +// EndOfMinute end of minute +func EndOfMinute() time.Time { + return With(time.Now()).EndOfMinute() +} + +// EndOfHour end of hour +func EndOfHour() time.Time { + return With(time.Now()).EndOfHour() +} + +// EndOfDay end of day +func EndOfDay() time.Time { + return With(time.Now()).EndOfDay() +} + +// EndOfWeek end of week +func EndOfWeek() time.Time { + return With(time.Now()).EndOfWeek() +} + +// EndOfMonth end of month +func EndOfMonth() time.Time { + return With(time.Now()).EndOfMonth() +} + +// EndOfQuarter end of quarter +func EndOfQuarter() time.Time { + return With(time.Now()).EndOfQuarter() +} + +// EndOfYear end of year +func EndOfYear() time.Time { + return With(time.Now()).EndOfYear() +} + +// Monday monday + +func Monday(strs ...string) time.Time { + return With(time.Now()).Monday(strs...) +} + +// Sunday sunday +func Sunday(strs ...string) time.Time { + return With(time.Now()).Sunday(strs...) +} + +// EndOfSunday end of sunday +func EndOfSunday() time.Time { + return With(time.Now()).EndOfSunday() +} + +// Quarter returns the yearly quarter +func Quarter() uint { + return With(time.Now()).Quarter() +} + +// Parse parse string to time +func Parse(strs ...string) (time.Time, error) { + return With(time.Now()).Parse(strs...) +} + +// ParseInLocation parse string to time in location +func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) { + return With(time.Now().In(loc)).Parse(strs...) +} + +// MustParse must parse string to time or will panic +func MustParse(strs ...string) time.Time { + return With(time.Now()).MustParse(strs...) +} + +// MustParseInLocation must parse string to time in location or will panic +func MustParseInLocation(loc *time.Location, strs ...string) time.Time { + return With(time.Now().In(loc)).MustParse(strs...) +} + +// Between check now between the begin, end time or not +func Between(time1, time2 string) bool { + return With(time.Now()).Between(time1, time2) +} diff --git a/vendor/github.com/jinzhu/now/now.go b/vendor/github.com/jinzhu/now/now.go new file mode 100644 index 000000000..2f524cc8d --- /dev/null +++ b/vendor/github.com/jinzhu/now/now.go @@ -0,0 +1,245 @@ +package now + +import ( + "errors" + "regexp" + "time" +) + +// BeginningOfMinute beginning of minute +func (now *Now) BeginningOfMinute() time.Time { + return now.Truncate(time.Minute) +} + +// BeginningOfHour beginning of hour +func (now *Now) BeginningOfHour() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location()) +} + +// BeginningOfDay beginning of day +func (now *Now) BeginningOfDay() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location()) +} + +// BeginningOfWeek beginning of week +func (now *Now) BeginningOfWeek() time.Time { + t := now.BeginningOfDay() + weekday := int(t.Weekday()) + + if now.WeekStartDay != time.Sunday { + weekStartDayInt := int(now.WeekStartDay) + + if weekday < weekStartDayInt { + weekday = weekday + 7 - weekStartDayInt + } else { + weekday = weekday - weekStartDayInt + } + } + return t.AddDate(0, 0, -weekday) +} + +// BeginningOfMonth beginning of month +func (now *Now) BeginningOfMonth() time.Time { + y, m, _ := now.Date() + return time.Date(y, m, 1, 0, 0, 0, 0, now.Location()) +} + +// BeginningOfQuarter beginning of quarter +func (now *Now) BeginningOfQuarter() time.Time { + month := now.BeginningOfMonth() + offset := (int(month.Month()) - 1) % 3 + return month.AddDate(0, -offset, 0) +} + +// BeginningOfHalf beginning of half year +func (now *Now) BeginningOfHalf() time.Time { + month := now.BeginningOfMonth() + offset := (int(month.Month()) - 1) % 6 + return month.AddDate(0, -offset, 0) +} + +// BeginningOfYear BeginningOfYear beginning of year +func (now *Now) BeginningOfYear() time.Time { + y, _, _ := now.Date() + return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location()) +} + +// EndOfMinute end of minute +func (now *Now) EndOfMinute() time.Time { + return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond) +} + +// EndOfHour end of hour +func (now *Now) EndOfHour() time.Time { + return now.BeginningOfHour().Add(time.Hour - time.Nanosecond) +} + +// EndOfDay end of day +func (now *Now) EndOfDay() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location()) +} + +// EndOfWeek end of week +func (now *Now) EndOfWeek() time.Time { + return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) +} + +// EndOfMonth end of month +func (now *Now) EndOfMonth() time.Time { + return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) +} + +// EndOfQuarter end of quarter +func (now *Now) EndOfQuarter() time.Time { + return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) +} + +// EndOfHalf end of half year +func (now *Now) EndOfHalf() time.Time { + return now.BeginningOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond) +} + +// EndOfYear end of year +func (now *Now) EndOfYear() time.Time { + return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) +} + +// Monday monday +/* +func (now *Now) Monday() time.Time { + t := now.BeginningOfDay() + weekday := int(t.Weekday()) + if weekday == 0 { + weekday = 7 + } + return t.AddDate(0, 0, -weekday+1) +} +*/ + +func (now *Now) Monday(strs ...string) time.Time { + var parseTime time.Time + var err error + if len(strs) > 0 { + parseTime, err = now.Parse(strs...) + if err != nil { + panic(err) + } + } else { + parseTime = now.BeginningOfDay() + } + weekday := int(parseTime.Weekday()) + if weekday == 0 { + weekday = 7 + } + return parseTime.AddDate(0, 0, -weekday+1) +} + +func (now *Now) Sunday(strs ...string) time.Time { + var parseTime time.Time + var err error + if len(strs) > 0 { + parseTime, err = now.Parse(strs...) + if err != nil { + panic(err) + } + } else { + parseTime = now.BeginningOfDay() + } + weekday := int(parseTime.Weekday()) + if weekday == 0 { + weekday = 7 + } + return parseTime.AddDate(0, 0, (7 - weekday)) +} + +// EndOfSunday end of sunday +func (now *Now) EndOfSunday() time.Time { + return New(now.Sunday()).EndOfDay() +} + +// Quarter returns the yearly quarter +func (now *Now) Quarter() uint { + return (uint(now.Month())-1)/3 + 1 +} + +func (now *Now) parseWithFormat(str string, location *time.Location) (t time.Time, err error) { + for _, format := range now.TimeFormats { + t, err = time.ParseInLocation(format, str, location) + + if err == nil { + return + } + } + err = errors.New("Can't parse string as time: " + str) + return +} + +var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*|T)\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))(\s*$|[Z+-])`) // match 15:04:05, 15:04:05.000, 15:04:05.000000 15, 2017-01-01 15:04, 2021-07-20T00:59:10Z, 2021-07-20T00:59:10+08:00, 2021-07-20T00:00:10-07:00 etc +var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`) // match 15:04:05, 15, 15:04:05.000, 15:04:05.000000, etc + +// Parse parse string to time +func (now *Now) Parse(strs ...string) (t time.Time, err error) { + var ( + setCurrentTime bool + parseTime []int + currentLocation = now.Location() + onlyTimeInStr = true + currentTime = formatTimeToList(now.Time) + ) + + for _, str := range strs { + hasTimeInStr := hasTimeRegexp.MatchString(str) // match 15:04:05, 15 + onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str) + if t, err = now.parseWithFormat(str, currentLocation); err == nil { + location := t.Location() + parseTime = formatTimeToList(t) + + for i, v := range parseTime { + // Don't reset hour, minute, second if current time str including time + if hasTimeInStr && i <= 3 { + continue + } + + // If value is zero, replace it with current time + if v == 0 { + if setCurrentTime { + parseTime[i] = currentTime[i] + } + } else { + setCurrentTime = true + } + + // if current time only includes time, should change day, month to current time + if onlyTimeInStr { + if i == 4 || i == 5 { + parseTime[i] = currentTime[i] + continue + } + } + } + + t = time.Date(parseTime[6], time.Month(parseTime[5]), parseTime[4], parseTime[3], parseTime[2], parseTime[1], parseTime[0], location) + currentTime = formatTimeToList(t) + } + } + return +} + +// MustParse must parse string to time or it will panic +func (now *Now) MustParse(strs ...string) (t time.Time) { + t, err := now.Parse(strs...) + if err != nil { + panic(err) + } + return t +} + +// Between check time between the begin, end time or not +func (now *Now) Between(begin, end string) bool { + beginTime := now.MustParse(begin) + endTime := now.MustParse(end) + return now.After(beginTime) && now.Before(endTime) +} diff --git a/vendor/github.com/jinzhu/now/time.go b/vendor/github.com/jinzhu/now/time.go new file mode 100644 index 000000000..52dd8b2a0 --- /dev/null +++ b/vendor/github.com/jinzhu/now/time.go @@ -0,0 +1,9 @@ +package now + +import "time" + +func formatTimeToList(t time.Time) []int { + hour, min, sec := t.Clock() + year, month, day := t.Date() + return []int{t.Nanosecond(), sec, min, hour, day, int(month), year} +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 254a1cd48..bcb4678af 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -78,9 +78,6 @@ github.com/alexedwards/argon2id # github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 ## explicit github.com/amoghe/go-crypt -# github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de -## explicit; go 1.12 -github.com/araddon/dateparse # github.com/armon/go-metrics v0.4.1 ## explicit; go 1.12 github.com/armon/go-metrics @@ -1172,6 +1169,9 @@ github.com/jellydator/ttlcache/v2 # github.com/jellydator/ttlcache/v3 v3.1.0 ## explicit; go 1.18 github.com/jellydator/ttlcache/v3 +# github.com/jinzhu/now v1.1.5 +## explicit; go 1.12 +github.com/jinzhu/now # github.com/jmespath/go-jmespath v0.4.0 ## explicit; go 1.14 github.com/jmespath/go-jmespath