mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-30 17:00:57 -06:00
[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
This commit is contained in:
@@ -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
|
||||
|
||||
2
go.mod
2
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
|
||||
|
||||
7
go.sum
7
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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@ The following spec parts are supported and tested:
|
||||
- 2.1.8 OR Operator
|
||||
- 2.1.12 Parentheses
|
||||
- 2.3.5 Date Tokens
|
||||
- Human tokens not implemented
|
||||
- 3.1.11 Implicit Operator
|
||||
- 3.1.12 Parentheses
|
||||
- 3.1.2 AND Operator
|
||||
|
||||
11
services/search/pkg/query/kql/engine_suite_test.go
Normal file
11
services/search/pkg/query/kql/engine_suite_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package kql
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// PatchTimeNow is here to path the package time now func,
|
||||
// this only exists for the tests context
|
||||
func PatchTimeNow(t func() time.Time) {
|
||||
timeNow = t
|
||||
}
|
||||
@@ -28,3 +28,12 @@ func (e NamedGroupInvalidNodesError) Error() string {
|
||||
ast.NodeValue(e.Node),
|
||||
).Error()
|
||||
}
|
||||
|
||||
// UnsupportedTimeRangeError records an error and the value that caused it.
|
||||
type UnsupportedTimeRangeError struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (e UnsupportedTimeRangeError) Error() string {
|
||||
return fmt.Sprintf("unable to convert '%v' to a time range", e.Value)
|
||||
}
|
||||
|
||||
@@ -101,6 +101,39 @@ func buildDateTimeNode(k, o, v interface{}, text []byte, pos position) (*ast.Dat
|
||||
Value: value,
|
||||
}, nil
|
||||
}
|
||||
func buildNaturalLanguageDateTimeNodes(k, v interface{}, text []byte, pos position) ([]ast.Node, error) {
|
||||
b, err := base(text, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := toString(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
from, to, err := toTimeRange(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []ast.Node{
|
||||
&ast.DateTimeNode{
|
||||
Base: b,
|
||||
Value: *from,
|
||||
Key: key,
|
||||
Operator: &ast.OperatorNode{Value: ">="},
|
||||
},
|
||||
&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)
|
||||
|
||||
@@ -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
|
||||
|
||||
13
vendor/github.com/araddon/dateparse/.travis.yml
generated
vendored
13
vendor/github.com/araddon/dateparse/.travis.yml
generated
vendored
@@ -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)
|
||||
323
vendor/github.com/araddon/dateparse/README.md
generated
vendored
323
vendor/github.com/araddon/dateparse/README.md
generated
vendored
@@ -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.
|
||||
|
||||
|
||||
[](https://codecov.io/gh/araddon/dateparse)
|
||||
[](http://godoc.org/github.com/araddon/dateparse)
|
||||
[](https://travis-ci.org/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 |
|
||||
+-------------------------------------------------------+-----------------------------------------+
|
||||
*/
|
||||
|
||||
```
|
||||
2189
vendor/github.com/araddon/dateparse/parseany.go
generated
vendored
2189
vendor/github.com/araddon/dateparse/parseany.go
generated
vendored
File diff suppressed because it is too large
Load Diff
3
vendor/github.com/jinzhu/now/Guardfile
generated
vendored
Normal file
3
vendor/github.com/jinzhu/now/Guardfile
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
guard 'gotest' do
|
||||
watch(%r{\.go$})
|
||||
end
|
||||
2
vendor/github.com/araddon/dateparse/LICENSE → vendor/github.com/jinzhu/now/License
generated
vendored
2
vendor/github.com/araddon/dateparse/LICENSE → vendor/github.com/jinzhu/now/License
generated
vendored
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Aaron Raddon
|
||||
Copyright (c) 2013-NOW Jinzhu <wosmvp@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
137
vendor/github.com/jinzhu/now/README.md
generated
vendored
Normal file
137
vendor/github.com/jinzhu/now/README.md
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
## Now
|
||||
|
||||
Now is a time toolkit for golang
|
||||
|
||||
[](https://goreportcard.com/report/github.com/jinzhu/now)
|
||||
[](https://github.com/jinzhu/now/actions)
|
||||
[](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**
|
||||
|
||||
* <http://github.com/jinzhu>
|
||||
* <wosmvp@gmail.com>
|
||||
* <http://twitter.com/zhangjinzhu>
|
||||
|
||||
## License
|
||||
|
||||
Released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
||||
200
vendor/github.com/jinzhu/now/main.go
generated
vendored
Normal file
200
vendor/github.com/jinzhu/now/main.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
||||
245
vendor/github.com/jinzhu/now/now.go
generated
vendored
Normal file
245
vendor/github.com/jinzhu/now/now.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
||||
9
vendor/github.com/jinzhu/now/time.go
generated
vendored
Normal file
9
vendor/github.com/jinzhu/now/time.go
generated
vendored
Normal file
@@ -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}
|
||||
}
|
||||
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user