-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parse time literals using the time zone in the select statement #8642
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1482,7 +1482,7 @@ func (s *SelectStatement) RewriteTimeCondition(now time.Time) error { | |
if err != nil { | ||
return err | ||
} else if interval > 0 && s.Condition != nil { | ||
_, tmax, err := TimeRange(s.Condition) | ||
_, tmax, err := TimeRange(s.Condition, s.Location) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -3778,19 +3778,23 @@ func (l *StringLiteral) IsTimeLiteral() bool { | |
} | ||
|
||
// ToTimeLiteral returns a time literal if this string can be converted to a time literal. | ||
func (l *StringLiteral) ToTimeLiteral() (*TimeLiteral, error) { | ||
func (l *StringLiteral) ToTimeLiteral(loc *time.Location) (*TimeLiteral, error) { | ||
if loc == nil { | ||
loc = time.UTC | ||
} | ||
|
||
if isDateTimeString(l.Val) { | ||
t, err := time.Parse(DateTimeFormat, l.Val) | ||
t, err := time.ParseInLocation(DateTimeFormat, l.Val, loc) | ||
if err != nil { | ||
// try to parse it as an RFCNano time | ||
t, err = time.Parse(time.RFC3339Nano, l.Val) | ||
t, err = time.ParseInLocation(time.RFC3339Nano, l.Val, loc) | ||
if err != nil { | ||
return nil, ErrInvalidTime | ||
} | ||
} | ||
return &TimeLiteral{Val: t}, nil | ||
} else if isDateString(l.Val) { | ||
t, err := time.Parse(DateFormat, l.Val) | ||
t, err := time.ParseInLocation(DateFormat, l.Val, loc) | ||
if err != nil { | ||
return nil, ErrInvalidTime | ||
} | ||
|
@@ -4040,7 +4044,7 @@ func OnlyTimeExpr(expr Expr) bool { | |
|
||
// TimeRange returns the minimum and maximum times specified by an expression. | ||
// It returns zero times if there is no bound. | ||
func TimeRange(expr Expr) (min, max time.Time, err error) { | ||
func TimeRange(expr Expr, loc *time.Location) (min, max time.Time, err error) { | ||
WalkFunc(expr, func(n Node) { | ||
if err != nil { | ||
return | ||
|
@@ -4052,11 +4056,11 @@ func TimeRange(expr Expr) (min, max time.Time, err error) { | |
// Otherwise check for for the right-hand side and flip the operator. | ||
op := n.Op | ||
var value time.Time | ||
value, err = timeExprValue(n.LHS, n.RHS) | ||
value, err = timeExprValue(n.LHS, n.RHS, loc) | ||
if err != nil { | ||
return | ||
} else if value.IsZero() { | ||
if value, err = timeExprValue(n.RHS, n.LHS); value.IsZero() || err != nil { | ||
if value, err = timeExprValue(n.RHS, n.LHS, loc); value.IsZero() || err != nil { | ||
return | ||
} else if op == LT { | ||
op = GT | ||
|
@@ -4105,7 +4109,7 @@ func TimeRange(expr Expr) (min, max time.Time, err error) { | |
// an expression. If there is no lower bound, the minimum time is returned | ||
// for minimum. If there is no higher bound, the maximum time is returned. | ||
func TimeRangeAsEpochNano(expr Expr) (min, max int64, err error) { | ||
tmin, tmax, err := TimeRange(expr) | ||
tmin, tmax, err := TimeRange(expr, nil) | ||
if err != nil { | ||
return 0, 0, err | ||
} | ||
|
@@ -4125,12 +4129,12 @@ func TimeRangeAsEpochNano(expr Expr) (min, max int64, err error) { | |
|
||
// timeExprValue returns the time literal value of a "time == <TimeLiteral>" expression. | ||
// Returns zero time if the expression is not a time expression. | ||
func timeExprValue(ref Expr, lit Expr) (t time.Time, err error) { | ||
func timeExprValue(ref Expr, lit Expr, loc *time.Location) (t time.Time, err error) { | ||
if ref, ok := ref.(*VarRef); ok && strings.ToLower(ref.Val) == "time" { | ||
// If literal looks like a date time then parse it as a time literal. | ||
if strlit, ok := lit.(*StringLiteral); ok { | ||
if strlit.IsTimeLiteral() { | ||
t, err := strlit.ToTimeLiteral() | ||
t, err := strlit.ToTimeLiteral(loc) | ||
if err != nil { | ||
return time.Time{}, err | ||
} | ||
|
@@ -4797,6 +4801,11 @@ func reduceBinaryExpr(expr *BinaryExpr, valuer Valuer) Expr { | |
lhs := reduce(expr.LHS, valuer) | ||
rhs := reduce(expr.RHS, valuer) | ||
|
||
loc := time.UTC | ||
if v, ok := valuer.(ZoneValuer); ok { | ||
loc = v.Zone() | ||
} | ||
|
||
// Do not evaluate if one side is nil. | ||
if lhs == nil || rhs == nil { | ||
return &BinaryExpr{LHS: lhs, RHS: rhs, Op: expr.Op} | ||
|
@@ -4827,17 +4836,17 @@ func reduceBinaryExpr(expr *BinaryExpr, valuer Valuer) Expr { | |
case *BooleanLiteral: | ||
return reduceBinaryExprBooleanLHS(op, lhs, rhs) | ||
case *DurationLiteral: | ||
return reduceBinaryExprDurationLHS(op, lhs, rhs) | ||
return reduceBinaryExprDurationLHS(op, lhs, rhs, loc) | ||
case *IntegerLiteral: | ||
return reduceBinaryExprIntegerLHS(op, lhs, rhs) | ||
return reduceBinaryExprIntegerLHS(op, lhs, rhs, loc) | ||
case *nilLiteral: | ||
return reduceBinaryExprNilLHS(op, lhs, rhs) | ||
case *NumberLiteral: | ||
return reduceBinaryExprNumberLHS(op, lhs, rhs) | ||
case *StringLiteral: | ||
return reduceBinaryExprStringLHS(op, lhs, rhs) | ||
return reduceBinaryExprStringLHS(op, lhs, rhs, loc) | ||
case *TimeLiteral: | ||
return reduceBinaryExprTimeLHS(op, lhs, rhs) | ||
return reduceBinaryExprTimeLHS(op, lhs, rhs, loc) | ||
default: | ||
return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} | ||
} | ||
|
@@ -4868,7 +4877,7 @@ func reduceBinaryExprBooleanLHS(op Token, lhs *BooleanLiteral, rhs Expr) Expr { | |
return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} | ||
} | ||
|
||
func reduceBinaryExprDurationLHS(op Token, lhs *DurationLiteral, rhs Expr) Expr { | ||
func reduceBinaryExprDurationLHS(op Token, lhs *DurationLiteral, rhs Expr, loc *time.Location) Expr { | ||
switch rhs := rhs.(type) { | ||
case *DurationLiteral: | ||
switch op { | ||
|
@@ -4915,11 +4924,11 @@ func reduceBinaryExprDurationLHS(op Token, lhs *DurationLiteral, rhs Expr) Expr | |
return &TimeLiteral{Val: rhs.Val.Add(lhs.Val)} | ||
} | ||
case *StringLiteral: | ||
t, err := rhs.ToTimeLiteral() | ||
t, err := rhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
expr := reduceBinaryExprDurationLHS(op, lhs, t) | ||
expr := reduceBinaryExprDurationLHS(op, lhs, t, loc) | ||
|
||
// If the returned expression is still a binary expr, that means | ||
// we couldn't reduce it so this wasn't used in a time literal context. | ||
|
@@ -4932,7 +4941,7 @@ func reduceBinaryExprDurationLHS(op Token, lhs *DurationLiteral, rhs Expr) Expr | |
return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} | ||
} | ||
|
||
func reduceBinaryExprIntegerLHS(op Token, lhs *IntegerLiteral, rhs Expr) Expr { | ||
func reduceBinaryExprIntegerLHS(op Token, lhs *IntegerLiteral, rhs Expr, loc *time.Location) Expr { | ||
switch rhs := rhs.(type) { | ||
case *NumberLiteral: | ||
return reduceBinaryExprNumberLHS(op, &NumberLiteral{Val: float64(lhs.Val)}, rhs) | ||
|
@@ -4983,17 +4992,17 @@ func reduceBinaryExprIntegerLHS(op Token, lhs *IntegerLiteral, rhs Expr) Expr { | |
} | ||
case *TimeLiteral: | ||
d := &DurationLiteral{Val: time.Duration(lhs.Val)} | ||
expr := reduceBinaryExprDurationLHS(op, d, rhs) | ||
expr := reduceBinaryExprDurationLHS(op, d, rhs, loc) | ||
if _, ok := expr.(*BinaryExpr); !ok { | ||
return expr | ||
} | ||
case *StringLiteral: | ||
t, err := rhs.ToTimeLiteral() | ||
t, err := rhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
d := &DurationLiteral{Val: time.Duration(lhs.Val)} | ||
expr := reduceBinaryExprDurationLHS(op, d, t) | ||
expr := reduceBinaryExprDurationLHS(op, d, t, loc) | ||
if _, ok := expr.(*BinaryExpr); !ok { | ||
return expr | ||
} | ||
|
@@ -5075,7 +5084,7 @@ func reduceBinaryExprNumberLHS(op Token, lhs *NumberLiteral, rhs Expr) Expr { | |
return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} | ||
} | ||
|
||
func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | ||
func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr, loc *time.Location) Expr { | ||
switch rhs := rhs.(type) { | ||
case *StringLiteral: | ||
switch op { | ||
|
@@ -5086,17 +5095,17 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
// could be a different result if they use different formats | ||
// for the same time. | ||
if lhs.IsTimeLiteral() && rhs.IsTimeLiteral() { | ||
tlhs, err := lhs.ToTimeLiteral() | ||
tlhs, err := lhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
return expr | ||
} | ||
|
||
trhs, err := rhs.ToTimeLiteral() | ||
trhs, err := rhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
return expr | ||
} | ||
|
||
t := reduceBinaryExprTimeLHS(op, tlhs, trhs) | ||
t := reduceBinaryExprTimeLHS(op, tlhs, trhs, loc) | ||
if _, ok := t.(*BinaryExpr); !ok { | ||
expr = t | ||
} | ||
|
@@ -5109,17 +5118,17 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
// could be a different result if they use different formats | ||
// for the same time. | ||
if lhs.IsTimeLiteral() && rhs.IsTimeLiteral() { | ||
tlhs, err := lhs.ToTimeLiteral() | ||
tlhs, err := lhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
return expr | ||
} | ||
|
||
trhs, err := rhs.ToTimeLiteral() | ||
trhs, err := rhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
return expr | ||
} | ||
|
||
t := reduceBinaryExprTimeLHS(op, tlhs, trhs) | ||
t := reduceBinaryExprTimeLHS(op, tlhs, trhs, loc) | ||
if _, ok := t.(*BinaryExpr); !ok { | ||
expr = t | ||
} | ||
|
@@ -5129,11 +5138,11 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
return &StringLiteral{Val: lhs.Val + rhs.Val} | ||
default: | ||
// Attempt to convert the string literal to a time literal. | ||
t, err := lhs.ToTimeLiteral() | ||
t, err := lhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs) | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs, loc) | ||
|
||
// If the returned expression is still a binary expr, that means | ||
// we couldn't reduce it so this wasn't used in a time literal context. | ||
|
@@ -5143,11 +5152,11 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
} | ||
case *DurationLiteral: | ||
// Attempt to convert the string literal to a time literal. | ||
t, err := lhs.ToTimeLiteral() | ||
t, err := lhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs) | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs, loc) | ||
|
||
// If the returned expression is still a binary expr, that means | ||
// we couldn't reduce it so this wasn't used in a time literal context. | ||
|
@@ -5156,11 +5165,11 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
} | ||
case *TimeLiteral: | ||
// Attempt to convert the string literal to a time literal. | ||
t, err := lhs.ToTimeLiteral() | ||
t, err := lhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs) | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs, loc) | ||
|
||
// If the returned expression is still a binary expr, that means | ||
// we couldn't reduce it so this wasn't used in a time literal context. | ||
|
@@ -5169,11 +5178,11 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
} | ||
case *IntegerLiteral: | ||
// Attempt to convert the string literal to a time literal. | ||
t, err := lhs.ToTimeLiteral() | ||
t, err := lhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs) | ||
expr := reduceBinaryExprTimeLHS(op, t, rhs, loc) | ||
|
||
// If the returned expression is still a binary expr, that means | ||
// we couldn't reduce it so this wasn't used in a time literal context. | ||
|
@@ -5189,7 +5198,7 @@ func reduceBinaryExprStringLHS(op Token, lhs *StringLiteral, rhs Expr) Expr { | |
return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} | ||
} | ||
|
||
func reduceBinaryExprTimeLHS(op Token, lhs *TimeLiteral, rhs Expr) Expr { | ||
func reduceBinaryExprTimeLHS(op Token, lhs *TimeLiteral, rhs Expr, loc *time.Location) Expr { | ||
switch rhs := rhs.(type) { | ||
case *DurationLiteral: | ||
switch op { | ||
|
@@ -5200,7 +5209,7 @@ func reduceBinaryExprTimeLHS(op Token, lhs *TimeLiteral, rhs Expr) Expr { | |
} | ||
case *IntegerLiteral: | ||
d := &DurationLiteral{Val: time.Duration(rhs.Val)} | ||
expr := reduceBinaryExprTimeLHS(op, lhs, d) | ||
expr := reduceBinaryExprTimeLHS(op, lhs, d, loc) | ||
if _, ok := expr.(*BinaryExpr); !ok { | ||
return expr | ||
} | ||
|
@@ -5222,11 +5231,11 @@ func reduceBinaryExprTimeLHS(op Token, lhs *TimeLiteral, rhs Expr) Expr { | |
return &BooleanLiteral{Val: lhs.Val.Before(rhs.Val) || lhs.Val.Equal(rhs.Val)} | ||
} | ||
case *StringLiteral: | ||
t, err := rhs.ToTimeLiteral() | ||
t, err := rhs.ToTimeLiteral(loc) | ||
if err != nil { | ||
break | ||
} | ||
expr := reduceBinaryExprTimeLHS(op, lhs, t) | ||
expr := reduceBinaryExprTimeLHS(op, lhs, t, loc) | ||
|
||
// If the returned expression is still a binary expr, that means | ||
// we couldn't reduce it so this wasn't used in a time literal context. | ||
|
@@ -5300,9 +5309,16 @@ type Valuer interface { | |
Value(key string) (interface{}, bool) | ||
} | ||
|
||
// ZoneValuer is the interface that specifies the current time zone. | ||
type ZoneValuer interface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need this interface? Seems like the only place this gets used is
Is there any anticipation that there will be anything else that implements I guess I've never quite understood why people use create an interface (it seems pretty common) for something where a concrete type would suffice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's because this is an extension of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But you could just as easily casted to a |
||
// Zone returns the time zone location. | ||
Zone() *time.Location | ||
} | ||
|
||
// NowValuer returns only the value for "now()". | ||
type NowValuer struct { | ||
Now time.Time | ||
Now time.Time | ||
Location *time.Location | ||
} | ||
|
||
// Value is a method that returns the value and existence flag for a given key. | ||
|
@@ -5313,6 +5329,14 @@ func (v *NowValuer) Value(key string) (interface{}, bool) { | |
return nil, false | ||
} | ||
|
||
// Zone is a method that returns the time.Location. | ||
func (v *NowValuer) Zone() *time.Location { | ||
if v.Location != nil { | ||
return v.Location | ||
} | ||
return time.UTC | ||
} | ||
|
||
// ContainsVarRef returns true if expr is a VarRef or contains one. | ||
func ContainsVarRef(expr Expr) bool { | ||
var v containsVarRefVisitor | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind of sucks that this bleeds into the some of the binary expression reducing. Guess it has to though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I didn't like this. Might be improved in the future with some better parsing of the expression.