package pgtype
import (
"database/sql/driver"
"encoding/binary"
"encoding/json"
"fmt"
"time"
"github.com/jackc/pgio"
)
const pgTimestamptzHourFormat = "2006-01-02 15:04:05.999999999Z07"
const pgTimestamptzMinuteFormat = "2006-01-02 15:04:05.999999999Z07:00"
const pgTimestamptzSecondFormat = "2006-01-02 15:04:05.999999999Z07:00:00"
const microsecFromUnixEpochToY2K = 946684800 * 1000000
const (
negativeInfinityMicrosecondOffset = -9223372036854775808
infinityMicrosecondOffset = 9223372036854775807
)
type Timestamptz struct {
Time time .Time
Status Status
InfinityModifier InfinityModifier
}
func (dst *Timestamptz ) Set (src interface {}) error {
if src == nil {
*dst = Timestamptz {Status : Null }
return nil
}
if value , ok := src .(interface { Get () interface {} }); ok {
value2 := value .Get ()
if value2 != value {
return dst .Set (value2 )
}
}
switch value := src .(type ) {
case time .Time :
*dst = Timestamptz {Time : value , Status : Present }
case *time .Time :
if value == nil {
*dst = Timestamptz {Status : Null }
} else {
return dst .Set (*value )
}
case InfinityModifier :
*dst = Timestamptz {InfinityModifier : value , Status : Present }
default :
if originalSrc , ok := underlyingTimeType (src ); ok {
return dst .Set (originalSrc )
}
return fmt .Errorf ("cannot convert %v to Timestamptz" , value )
}
return nil
}
func (dst Timestamptz ) Get () interface {} {
switch dst .Status {
case Present :
if dst .InfinityModifier != None {
return dst .InfinityModifier
}
return dst .Time
case Null :
return nil
default :
return dst .Status
}
}
func (src *Timestamptz ) AssignTo (dst interface {}) error {
switch src .Status {
case Present :
switch v := dst .(type ) {
case *time .Time :
if src .InfinityModifier != None {
return fmt .Errorf ("cannot assign %v to %T" , src , dst )
}
*v = src .Time
return nil
default :
if nextDst , retry := GetAssignToDstType (dst ); retry {
return src .AssignTo (nextDst )
}
return fmt .Errorf ("unable to assign to %T" , dst )
}
case Null :
return NullAssignTo (dst )
}
return fmt .Errorf ("cannot decode %#v into %T" , src , dst )
}
func (dst *Timestamptz ) DecodeText (ci *ConnInfo , src []byte ) error {
if src == nil {
*dst = Timestamptz {Status : Null }
return nil
}
sbuf := string (src )
switch sbuf {
case "infinity" :
*dst = Timestamptz {Status : Present , InfinityModifier : Infinity }
case "-infinity" :
*dst = Timestamptz {Status : Present , InfinityModifier : -Infinity }
default :
var format string
if len (sbuf ) >= 9 && (sbuf [len (sbuf )-9 ] == '-' || sbuf [len (sbuf )-9 ] == '+' ) {
format = pgTimestamptzSecondFormat
} else if len (sbuf ) >= 6 && (sbuf [len (sbuf )-6 ] == '-' || sbuf [len (sbuf )-6 ] == '+' ) {
format = pgTimestamptzMinuteFormat
} else {
format = pgTimestamptzHourFormat
}
tim , err := time .Parse (format , sbuf )
if err != nil {
return err
}
*dst = Timestamptz {Time : tim , Status : Present }
}
return nil
}
func (dst *Timestamptz ) DecodeBinary (ci *ConnInfo , src []byte ) error {
if src == nil {
*dst = Timestamptz {Status : Null }
return nil
}
if len (src ) != 8 {
return fmt .Errorf ("invalid length for timestamptz: %v" , len (src ))
}
microsecSinceY2K := int64 (binary .BigEndian .Uint64 (src ))
switch microsecSinceY2K {
case infinityMicrosecondOffset :
*dst = Timestamptz {Status : Present , InfinityModifier : Infinity }
case negativeInfinityMicrosecondOffset :
*dst = Timestamptz {Status : Present , InfinityModifier : -Infinity }
default :
microsecSinceUnixEpoch := microsecFromUnixEpochToY2K + microsecSinceY2K
tim := time .Unix (microsecSinceUnixEpoch /1000000 , (microsecSinceUnixEpoch %1000000 )*1000 )
*dst = Timestamptz {Time : tim , Status : Present }
}
return nil
}
func (src Timestamptz ) EncodeText (ci *ConnInfo , buf []byte ) ([]byte , error ) {
switch src .Status {
case Null :
return nil , nil
case Undefined :
return nil , errUndefined
}
var s string
switch src .InfinityModifier {
case None :
s = src .Time .UTC ().Truncate (time .Microsecond ).Format (pgTimestamptzSecondFormat )
case Infinity :
s = "infinity"
case NegativeInfinity :
s = "-infinity"
}
return append (buf , s ...), nil
}
func (src Timestamptz ) EncodeBinary (ci *ConnInfo , buf []byte ) ([]byte , error ) {
switch src .Status {
case Null :
return nil , nil
case Undefined :
return nil , errUndefined
}
var microsecSinceY2K int64
switch src .InfinityModifier {
case None :
microsecSinceUnixEpoch := src .Time .Unix ()*1000000 + int64 (src .Time .Nanosecond ())/1000
microsecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K
case Infinity :
microsecSinceY2K = infinityMicrosecondOffset
case NegativeInfinity :
microsecSinceY2K = negativeInfinityMicrosecondOffset
}
return pgio .AppendInt64 (buf , microsecSinceY2K ), nil
}