Files
dolthub--doltgresql/server/functions/make_timestamp.go
T
2026-07-13 12:32:25 +08:00

115 lines
4.6 KiB
Go

// Copyright 2025 Dolthub, Inc.
//
// 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 functions
import (
"time"
"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/doltgresql/server/functions/framework"
pgtypes "github.com/dolthub/doltgresql/server/types"
)
// initMakeTimestamp registers the functions to the catalog.
func initMakeTimestamp() {
framework.RegisterFunction(make_timestamp)
framework.RegisterFunction(make_timestamptz_int32_int32_int32_int32_int32_float64)
framework.RegisterFunction(make_timestamptz_int32_int32_int32_int32_int32_float64_text)
}
var errDateFieldOutOfRange = errors.Errorf(`date field value out of range`)
var errTimeFieldOutOfRange = errors.Errorf(`time field value out of range`)
// make_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
var make_timestamp = framework.Function6{
Name: "make_timestamp",
Return: pgtypes.Timestamp,
Parameters: [6]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Float64},
IsNonDeterministic: true,
Strict: true,
Callable: func(ctx *sql.Context, _ [7]*pgtypes.DoltgresType, val1, val2, val3, val4, val5, val6 any) (any, error) {
return getTimestampInServerLocation(val1.(int32), val2.(int32), val3.(int32), val4.(int32), val5.(int32), val6.(float64), time.UTC)
},
}
// make_timestamptz_int32_int32_int32_int32_int32_float64 represents the PostgreSQL function of the same name, taking the same parameters.
var make_timestamptz_int32_int32_int32_int32_int32_float64 = framework.Function6{
Name: "make_timestamptz",
Return: pgtypes.TimestampTZ,
Parameters: [6]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Float64},
IsNonDeterministic: true,
Strict: true,
Callable: func(ctx *sql.Context, _ [7]*pgtypes.DoltgresType, val1, val2, val3, val4, val5, val6 any) (any, error) {
loc, err := GetServerLocation(ctx)
if err != nil {
return time.Time{}, err
}
return getTimestampInServerLocation(val1.(int32), val2.(int32), val3.(int32), val4.(int32), val5.(int32), val6.(float64), loc)
},
}
// make_timestamptz_int32_int32_int32_int32_int32_float64_text represents the PostgreSQL function of the same name, taking the same parameters.
var make_timestamptz_int32_int32_int32_int32_int32_float64_text = framework.Function7{
Name: "make_timestamptz",
Return: pgtypes.TimestampTZ,
Parameters: [7]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Int32, pgtypes.Float64, pgtypes.Text},
IsNonDeterministic: true,
Strict: true,
Callable: func(ctx *sql.Context, _ [8]*pgtypes.DoltgresType, val1, val2, val3, val4, val5, val6, val7 any) (any, error) {
tz := val7.(string)
loc, _, _, err := convertTzToOffsetSecs(time.Now().UTC(), tz)
if err != nil {
return nil, errors.Errorf(`time zone "%s" not recognized`, tz)
}
return getTimestampInServerLocation(val1.(int32), val2.(int32), val3.(int32), val4.(int32), val5.(int32), val6.(float64), loc)
},
}
func getTimestampInServerLocation(year, month, day, hour, minute int32, partialSecond float64, loc *time.Location) (time.Time, error) {
if year == 0 {
return time.Time{}, errDateFieldOutOfRange
} else if year < 0 {
// PostgreSQL: year 0 = 1 BC, year -1 = 2 BC, etc.
// which formatting it is handled in FormatDateTimeWithBC using calculation: (1 - year)
year++
}
if month < 1 || month > 12 {
return time.Time{}, errDateFieldOutOfRange
}
if day < 1 || day > 31 {
return time.Time{}, errDateFieldOutOfRange
}
if hour < 0 || hour > 23 {
return time.Time{}, errTimeFieldOutOfRange
}
if minute < 0 || minute > 59 {
return time.Time{}, errTimeFieldOutOfRange
}
second := int(partialSecond)
if second < 0 || second >= 60 {
return time.Time{}, errTimeFieldOutOfRange
}
nsec := int64(partialSecond*float64(time.Second)) % int64(time.Second)
return time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), second, int(nsec), loc), nil
}