mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-27 00:30:18 +00:00
ff3e84ad67
updating kubernetes to 1.28.0 in the main repo. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
602 lines
19 KiB
Go
602 lines
19 KiB
Go
// Copyright 2020 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cel
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/cel-go/checker"
|
|
"github.com/google/cel-go/common"
|
|
"github.com/google/cel-go/common/operators"
|
|
"github.com/google/cel-go/common/overloads"
|
|
"github.com/google/cel-go/common/types"
|
|
"github.com/google/cel-go/common/types/ref"
|
|
"github.com/google/cel-go/common/types/traits"
|
|
"github.com/google/cel-go/interpreter"
|
|
"github.com/google/cel-go/interpreter/functions"
|
|
"github.com/google/cel-go/parser"
|
|
|
|
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
|
)
|
|
|
|
const (
|
|
optMapMacro = "optMap"
|
|
hasValueFunc = "hasValue"
|
|
optionalNoneFunc = "optional.none"
|
|
optionalOfFunc = "optional.of"
|
|
optionalOfNonZeroValueFunc = "optional.ofNonZeroValue"
|
|
valueFunc = "value"
|
|
unusedIterVar = "#unused"
|
|
)
|
|
|
|
// Library provides a collection of EnvOption and ProgramOption values used to configure a CEL
|
|
// environment for a particular use case or with a related set of functionality.
|
|
//
|
|
// Note, the ProgramOption values provided by a library are expected to be static and not vary
|
|
// between calls to Env.Program(). If there is a need for such dynamic configuration, prefer to
|
|
// configure these options outside the Library and within the Env.Program() call directly.
|
|
type Library interface {
|
|
// CompileOptions returns a collection of functional options for configuring the Parse / Check
|
|
// environment.
|
|
CompileOptions() []EnvOption
|
|
|
|
// ProgramOptions returns a collection of functional options which should be included in every
|
|
// Program generated from the Env.Program() call.
|
|
ProgramOptions() []ProgramOption
|
|
}
|
|
|
|
// SingletonLibrary refines the Library interface to ensure that libraries in this format are only
|
|
// configured once within the environment.
|
|
type SingletonLibrary interface {
|
|
Library
|
|
|
|
// LibraryName provides a namespaced name which is used to check whether the library has already
|
|
// been configured in the environment.
|
|
LibraryName() string
|
|
}
|
|
|
|
// Lib creates an EnvOption out of a Library, allowing libraries to be provided as functional args,
|
|
// and to be linked to each other.
|
|
func Lib(l Library) EnvOption {
|
|
singleton, isSingleton := l.(SingletonLibrary)
|
|
return func(e *Env) (*Env, error) {
|
|
if isSingleton {
|
|
if e.HasLibrary(singleton.LibraryName()) {
|
|
return e, nil
|
|
}
|
|
e.libraries[singleton.LibraryName()] = true
|
|
}
|
|
var err error
|
|
for _, opt := range l.CompileOptions() {
|
|
e, err = opt(e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
e.progOpts = append(e.progOpts, l.ProgramOptions()...)
|
|
return e, nil
|
|
}
|
|
}
|
|
|
|
// StdLib returns an EnvOption for the standard library of CEL functions and macros.
|
|
func StdLib() EnvOption {
|
|
return Lib(stdLibrary{})
|
|
}
|
|
|
|
// stdLibrary implements the Library interface and provides functional options for the core CEL
|
|
// features documented in the specification.
|
|
type stdLibrary struct{}
|
|
|
|
// LibraryName implements the SingletonLibrary interface method.
|
|
func (stdLibrary) LibraryName() string {
|
|
return "cel.lib.std"
|
|
}
|
|
|
|
// EnvOptions returns options for the standard CEL function declarations and macros.
|
|
func (stdLibrary) CompileOptions() []EnvOption {
|
|
return []EnvOption{
|
|
Declarations(checker.StandardDeclarations()...),
|
|
Macros(StandardMacros...),
|
|
}
|
|
}
|
|
|
|
// ProgramOptions returns function implementations for the standard CEL functions.
|
|
func (stdLibrary) ProgramOptions() []ProgramOption {
|
|
return []ProgramOption{
|
|
Functions(functions.StandardOverloads()...),
|
|
}
|
|
}
|
|
|
|
type optionalLibrary struct{}
|
|
|
|
// LibraryName implements the SingletonLibrary interface method.
|
|
func (optionalLibrary) LibraryName() string {
|
|
return "cel.lib.optional"
|
|
}
|
|
|
|
// CompileOptions implements the Library interface method.
|
|
func (optionalLibrary) CompileOptions() []EnvOption {
|
|
paramTypeK := TypeParamType("K")
|
|
paramTypeV := TypeParamType("V")
|
|
optionalTypeV := OptionalType(paramTypeV)
|
|
listTypeV := ListType(paramTypeV)
|
|
mapTypeKV := MapType(paramTypeK, paramTypeV)
|
|
|
|
return []EnvOption{
|
|
// Enable the optional syntax in the parser.
|
|
enableOptionalSyntax(),
|
|
|
|
// Introduce the optional type.
|
|
Types(types.OptionalType),
|
|
|
|
// Configure the optMap macro.
|
|
Macros(NewReceiverMacro(optMapMacro, 2, optMap)),
|
|
|
|
// Global and member functions for working with optional values.
|
|
Function(optionalOfFunc,
|
|
Overload("optional_of", []*Type{paramTypeV}, optionalTypeV,
|
|
UnaryBinding(func(value ref.Val) ref.Val {
|
|
return types.OptionalOf(value)
|
|
}))),
|
|
Function(optionalOfNonZeroValueFunc,
|
|
Overload("optional_ofNonZeroValue", []*Type{paramTypeV}, optionalTypeV,
|
|
UnaryBinding(func(value ref.Val) ref.Val {
|
|
v, isZeroer := value.(traits.Zeroer)
|
|
if !isZeroer || !v.IsZeroValue() {
|
|
return types.OptionalOf(value)
|
|
}
|
|
return types.OptionalNone
|
|
}))),
|
|
Function(optionalNoneFunc,
|
|
Overload("optional_none", []*Type{}, optionalTypeV,
|
|
FunctionBinding(func(values ...ref.Val) ref.Val {
|
|
return types.OptionalNone
|
|
}))),
|
|
Function(valueFunc,
|
|
MemberOverload("optional_value", []*Type{optionalTypeV}, paramTypeV,
|
|
UnaryBinding(func(value ref.Val) ref.Val {
|
|
opt := value.(*types.Optional)
|
|
return opt.GetValue()
|
|
}))),
|
|
Function(hasValueFunc,
|
|
MemberOverload("optional_hasValue", []*Type{optionalTypeV}, BoolType,
|
|
UnaryBinding(func(value ref.Val) ref.Val {
|
|
opt := value.(*types.Optional)
|
|
return types.Bool(opt.HasValue())
|
|
}))),
|
|
|
|
// Implementation of 'or' and 'orValue' are special-cased to support short-circuiting in the
|
|
// evaluation chain.
|
|
Function("or",
|
|
MemberOverload("optional_or_optional", []*Type{optionalTypeV, optionalTypeV}, optionalTypeV)),
|
|
Function("orValue",
|
|
MemberOverload("optional_orValue_value", []*Type{optionalTypeV, paramTypeV}, paramTypeV)),
|
|
|
|
// OptSelect is handled specially by the type-checker, so the receiver's field type is used to determine the
|
|
// optput type.
|
|
Function(operators.OptSelect,
|
|
Overload("select_optional_field", []*Type{DynType, StringType}, optionalTypeV)),
|
|
|
|
// OptIndex is handled mostly like any other indexing operation on a list or map, so the type-checker can use
|
|
// these signatures to determine type-agreement without any special handling.
|
|
Function(operators.OptIndex,
|
|
Overload("list_optindex_optional_int", []*Type{listTypeV, IntType}, optionalTypeV),
|
|
Overload("optional_list_optindex_optional_int", []*Type{OptionalType(listTypeV), IntType}, optionalTypeV),
|
|
Overload("map_optindex_optional_value", []*Type{mapTypeKV, paramTypeK}, optionalTypeV),
|
|
Overload("optional_map_optindex_optional_value", []*Type{OptionalType(mapTypeKV), paramTypeK}, optionalTypeV)),
|
|
|
|
// Index overloads to accommodate using an optional value as the operand.
|
|
Function(operators.Index,
|
|
Overload("optional_list_index_int", []*Type{OptionalType(listTypeV), IntType}, optionalTypeV),
|
|
Overload("optional_map_index_optional_value", []*Type{OptionalType(mapTypeKV), paramTypeK}, optionalTypeV)),
|
|
}
|
|
}
|
|
|
|
func optMap(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
|
varIdent := args[0]
|
|
varName := ""
|
|
switch varIdent.GetExprKind().(type) {
|
|
case *exprpb.Expr_IdentExpr:
|
|
varName = varIdent.GetIdentExpr().GetName()
|
|
default:
|
|
return nil, &common.Error{
|
|
Message: "optMap() variable name must be a simple identifier",
|
|
Location: meh.OffsetLocation(varIdent.GetId()),
|
|
}
|
|
}
|
|
mapExpr := args[1]
|
|
return meh.GlobalCall(
|
|
operators.Conditional,
|
|
meh.ReceiverCall(hasValueFunc, target),
|
|
meh.GlobalCall(optionalOfFunc,
|
|
meh.Fold(
|
|
unusedIterVar,
|
|
meh.NewList(),
|
|
varName,
|
|
meh.ReceiverCall(valueFunc, target),
|
|
meh.LiteralBool(false),
|
|
meh.Ident(varName),
|
|
mapExpr,
|
|
),
|
|
),
|
|
meh.GlobalCall(optionalNoneFunc),
|
|
), nil
|
|
}
|
|
|
|
// ProgramOptions implements the Library interface method.
|
|
func (optionalLibrary) ProgramOptions() []ProgramOption {
|
|
return []ProgramOption{
|
|
CustomDecorator(decorateOptionalOr),
|
|
}
|
|
}
|
|
|
|
func enableOptionalSyntax() EnvOption {
|
|
return func(e *Env) (*Env, error) {
|
|
e.prsrOpts = append(e.prsrOpts, parser.EnableOptionalSyntax(true))
|
|
return e, nil
|
|
}
|
|
}
|
|
|
|
func decorateOptionalOr(i interpreter.Interpretable) (interpreter.Interpretable, error) {
|
|
call, ok := i.(interpreter.InterpretableCall)
|
|
if !ok {
|
|
return i, nil
|
|
}
|
|
args := call.Args()
|
|
if len(args) != 2 {
|
|
return i, nil
|
|
}
|
|
switch call.Function() {
|
|
case "or":
|
|
if call.OverloadID() != "" && call.OverloadID() != "optional_or_optional" {
|
|
return i, nil
|
|
}
|
|
return &evalOptionalOr{
|
|
id: call.ID(),
|
|
lhs: args[0],
|
|
rhs: args[1],
|
|
}, nil
|
|
case "orValue":
|
|
if call.OverloadID() != "" && call.OverloadID() != "optional_orValue_value" {
|
|
return i, nil
|
|
}
|
|
return &evalOptionalOrValue{
|
|
id: call.ID(),
|
|
lhs: args[0],
|
|
rhs: args[1],
|
|
}, nil
|
|
default:
|
|
return i, nil
|
|
}
|
|
}
|
|
|
|
// evalOptionalOr selects between two optional values, either the first if it has a value, or
|
|
// the second optional expression is evaluated and returned.
|
|
type evalOptionalOr struct {
|
|
id int64
|
|
lhs interpreter.Interpretable
|
|
rhs interpreter.Interpretable
|
|
}
|
|
|
|
// ID implements the Interpretable interface method.
|
|
func (opt *evalOptionalOr) ID() int64 {
|
|
return opt.id
|
|
}
|
|
|
|
// Eval evaluates the left-hand side optional to determine whether it contains a value, else
|
|
// proceeds with the right-hand side evaluation.
|
|
func (opt *evalOptionalOr) Eval(ctx interpreter.Activation) ref.Val {
|
|
// short-circuit lhs.
|
|
optLHS := opt.lhs.Eval(ctx)
|
|
optVal, ok := optLHS.(*types.Optional)
|
|
if !ok {
|
|
return optLHS
|
|
}
|
|
if optVal.HasValue() {
|
|
return optVal
|
|
}
|
|
return opt.rhs.Eval(ctx)
|
|
}
|
|
|
|
// evalOptionalOrValue selects between an optional or a concrete value. If the optional has a value,
|
|
// its value is returned, otherwise the alternative value expression is evaluated and returned.
|
|
type evalOptionalOrValue struct {
|
|
id int64
|
|
lhs interpreter.Interpretable
|
|
rhs interpreter.Interpretable
|
|
}
|
|
|
|
// ID implements the Interpretable interface method.
|
|
func (opt *evalOptionalOrValue) ID() int64 {
|
|
return opt.id
|
|
}
|
|
|
|
// Eval evaluates the left-hand side optional to determine whether it contains a value, else
|
|
// proceeds with the right-hand side evaluation.
|
|
func (opt *evalOptionalOrValue) Eval(ctx interpreter.Activation) ref.Val {
|
|
// short-circuit lhs.
|
|
optLHS := opt.lhs.Eval(ctx)
|
|
optVal, ok := optLHS.(*types.Optional)
|
|
if !ok {
|
|
return optLHS
|
|
}
|
|
if optVal.HasValue() {
|
|
return optVal.GetValue()
|
|
}
|
|
return opt.rhs.Eval(ctx)
|
|
}
|
|
|
|
type timeUTCLibrary struct{}
|
|
|
|
func (timeUTCLibrary) CompileOptions() []EnvOption {
|
|
return timeOverloadDeclarations
|
|
}
|
|
|
|
func (timeUTCLibrary) ProgramOptions() []ProgramOption {
|
|
return []ProgramOption{}
|
|
}
|
|
|
|
// Declarations and functions which enable using UTC on time.Time inputs when the timezone is unspecified
|
|
// in the CEL expression.
|
|
var (
|
|
utcTZ = types.String("UTC")
|
|
|
|
timeOverloadDeclarations = []EnvOption{
|
|
Function(overloads.TimeGetHours,
|
|
MemberOverload(overloads.DurationToHours, []*Type{DurationType}, IntType,
|
|
UnaryBinding(func(dur ref.Val) ref.Val {
|
|
d := dur.(types.Duration)
|
|
return types.Int(d.Hours())
|
|
}))),
|
|
Function(overloads.TimeGetMinutes,
|
|
MemberOverload(overloads.DurationToMinutes, []*Type{DurationType}, IntType,
|
|
UnaryBinding(func(dur ref.Val) ref.Val {
|
|
d := dur.(types.Duration)
|
|
return types.Int(d.Minutes())
|
|
}))),
|
|
Function(overloads.TimeGetSeconds,
|
|
MemberOverload(overloads.DurationToSeconds, []*Type{DurationType}, IntType,
|
|
UnaryBinding(func(dur ref.Val) ref.Val {
|
|
d := dur.(types.Duration)
|
|
return types.Int(d.Seconds())
|
|
}))),
|
|
Function(overloads.TimeGetMilliseconds,
|
|
MemberOverload(overloads.DurationToMilliseconds, []*Type{DurationType}, IntType,
|
|
UnaryBinding(func(dur ref.Val) ref.Val {
|
|
d := dur.(types.Duration)
|
|
return types.Int(d.Milliseconds())
|
|
}))),
|
|
Function(overloads.TimeGetFullYear,
|
|
MemberOverload(overloads.TimestampToYear, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetFullYear(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToYearWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetFullYear),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetMonth,
|
|
MemberOverload(overloads.TimestampToMonth, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetMonth(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToMonthWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetMonth),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetDayOfYear,
|
|
MemberOverload(overloads.TimestampToDayOfYear, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetDayOfYear(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToDayOfYearWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(func(ts, tz ref.Val) ref.Val {
|
|
return timestampGetDayOfYear(ts, tz)
|
|
}),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetDayOfMonth,
|
|
MemberOverload(overloads.TimestampToDayOfMonthZeroBased, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetDayOfMonthZeroBased(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToDayOfMonthZeroBasedWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetDayOfMonthZeroBased),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetDate,
|
|
MemberOverload(overloads.TimestampToDayOfMonthOneBased, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetDayOfMonthOneBased(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToDayOfMonthOneBasedWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetDayOfMonthOneBased),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetDayOfWeek,
|
|
MemberOverload(overloads.TimestampToDayOfWeek, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetDayOfWeek(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToDayOfWeekWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetDayOfWeek),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetHours,
|
|
MemberOverload(overloads.TimestampToHours, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetHours(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToHoursWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetHours),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetMinutes,
|
|
MemberOverload(overloads.TimestampToMinutes, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetMinutes(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToMinutesWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetMinutes),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetSeconds,
|
|
MemberOverload(overloads.TimestampToSeconds, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetSeconds(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToSecondsWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetSeconds),
|
|
),
|
|
),
|
|
Function(overloads.TimeGetMilliseconds,
|
|
MemberOverload(overloads.TimestampToMilliseconds, []*Type{TimestampType}, IntType,
|
|
UnaryBinding(func(ts ref.Val) ref.Val {
|
|
return timestampGetMilliseconds(ts, utcTZ)
|
|
}),
|
|
),
|
|
MemberOverload(overloads.TimestampToMillisecondsWithTz, []*Type{TimestampType, StringType}, IntType,
|
|
BinaryBinding(timestampGetMilliseconds),
|
|
),
|
|
),
|
|
}
|
|
)
|
|
|
|
func timestampGetFullYear(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Year())
|
|
}
|
|
|
|
func timestampGetMonth(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
// CEL spec indicates that the month should be 0-based, but the Time value
|
|
// for Month() is 1-based.
|
|
return types.Int(t.Month() - 1)
|
|
}
|
|
|
|
func timestampGetDayOfYear(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.YearDay() - 1)
|
|
}
|
|
|
|
func timestampGetDayOfMonthZeroBased(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Day() - 1)
|
|
}
|
|
|
|
func timestampGetDayOfMonthOneBased(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Day())
|
|
}
|
|
|
|
func timestampGetDayOfWeek(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Weekday())
|
|
}
|
|
|
|
func timestampGetHours(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Hour())
|
|
}
|
|
|
|
func timestampGetMinutes(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Minute())
|
|
}
|
|
|
|
func timestampGetSeconds(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Second())
|
|
}
|
|
|
|
func timestampGetMilliseconds(ts, tz ref.Val) ref.Val {
|
|
t, err := inTimeZone(ts, tz)
|
|
if err != nil {
|
|
return types.NewErr(err.Error())
|
|
}
|
|
return types.Int(t.Nanosecond() / 1000000)
|
|
}
|
|
|
|
func inTimeZone(ts, tz ref.Val) (time.Time, error) {
|
|
t := ts.(types.Timestamp)
|
|
val := string(tz.(types.String))
|
|
ind := strings.Index(val, ":")
|
|
if ind == -1 {
|
|
loc, err := time.LoadLocation(val)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return t.In(loc), nil
|
|
}
|
|
|
|
// If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC
|
|
// in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes.
|
|
hr, err := strconv.Atoi(string(val[0:ind]))
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
min, err := strconv.Atoi(string(val[ind+1:]))
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
var offset int
|
|
if string(val[0]) == "-" {
|
|
offset = hr*60 - min
|
|
} else {
|
|
offset = hr*60 + min
|
|
}
|
|
secondsEastOfUTC := int((time.Duration(offset) * time.Minute).Seconds())
|
|
timezone := time.FixedZone("", secondsEastOfUTC)
|
|
return t.In(timezone), nil
|
|
}
|