func (c *ConvertTz) FunctionName() string {
	return "convert_tz"
}
FunctionName implement the sql.FunctionExpression interface.
package function
import (
	"errors"
	"fmt"
	"regexp"
	"time"
	"github.com/dolthub/go-mysql-server/sql"
)
var offsetRegex = regexp.MustCompile(`(?m)^(\+|\-)(\d{2}):(\d{2})$`)  
type ConvertTz struct {
	dt     sql.Expression
	fromTz sql.Expression
	toTz   sql.Expression
}
var _ sql.FunctionExpression = (*ConvertTz)(nil)
func NewConvertTz(dt, fromTz, toTz sql.Expression) sql.Expression {
	return &ConvertTz{
		dt:     dt,
		fromTz: fromTz,
		toTz:   toTz,
	}
}
func (c *ConvertTz) Resolved() bool {
	return c.dt.Resolved() && c.fromTz.Resolved() && c.toTz.Resolved()
}
func (c *ConvertTz) String() string {
	return fmt.Sprintf("CONVERT_TZ(%s, %s, %s)", c.dt, c.fromTz, c.toTz)
}
func (c *ConvertTz) Type() sql.Type {
	return sql.Datetime
}
func (c *ConvertTz) IsNullable() bool {
	return true
}
func (c *ConvertTz) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
	from, err := c.fromTz.Eval(ctx, row)
	if err != nil {
		return nil, err
	}
	to, err := c.toTz.Eval(ctx, row)
	if err != nil {
		return nil, err
	}
	dt, err := c.dt.Eval(ctx, row)
	if err != nil {
		return nil, err
	}
	datetime, err := sql.Datetime.ConvertWithoutRangeCheck(dt)
	if err != nil {
		return nil, nil
	}
	fromStr, ok := from.(string)
	if !ok {
		return nil, nil
	}
	toStr, ok := to.(string)
	if !ok {
		return nil, nil
	}
	converted, success := convertTimeZone(datetime, fromStr, toStr)
	if success {
		return sql.Datetime.ConvertWithoutRangeCheck(converted)
	}
	converted, success = convertOffsets(datetime, fromStr, toStr)
	if !success {
		return nil, nil
	}
	return sql.Datetime.ConvertWithoutRangeCheck(converted)
}
func convertTimeZone(datetime time.Time, fromLocation string, toLocation string) (time.Time, bool) {
	fLoc, err := time.LoadLocation(fromLocation)
	if err != nil {
		return time.Time{}, false
	}
	tLoc, err := time.LoadLocation(toLocation)
	if err != nil {
		return time.Time{}, false
	}
	delta := getCopy(datetime, fLoc).Sub(getCopy(datetime, tLoc))
	return datetime.Add(delta), true
}
func getCopy(t time.Time, loc *time.Location) time.Time {
	return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), loc).UTC()
}
func convertOffsets(t time.Time, startDuration string, endDuration string) (time.Time, bool) {
	fromDuration, err := getDeltaAsDuration(startDuration)
	if err != nil {
		return time.Time{}, false
	}
	toDuration, err := getDeltaAsDuration(endDuration)
	if err != nil {
		return time.Time{}, false
	}
	return t.Add(toDuration - fromDuration), true
}
func getDeltaAsDuration(d string) (time.Duration, error) {
	var hours string
	var mins string
	var symbol string
	matches := offsetRegex.FindStringSubmatch(d)
	if len(matches) == 4 {
		symbol = matches[1]
		hours = matches[2]
		mins = matches[3]
	} else {
		return -1, errors.New("error: unable to process time")
	}
	return time.ParseDuration(symbol + hours + "h" + mins + "m")
}
func (c *ConvertTz) Children() []sql.Expression {
	return []sql.Expression{c.dt, c.fromTz, c.toTz}
}
func (c *ConvertTz) WithChildren(children ...sql.Expression) (sql.Expression, error) {
	if len(children) != 3 {
		return nil, sql.ErrInvalidChildrenNumber.New(c, len(children), 3)
	}
	return NewConvertTz(children[0], children[1], children[2]), nil
}