...

Source file src/github.com/jackc/pgtype/timestamp.go

Documentation: github.com/jackc/pgtype

     1  package pgtype
     2  
     3  import (
     4  	"database/sql/driver"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/jackc/pgio"
    11  )
    12  
    13  const pgTimestampFormat = "2006-01-02 15:04:05.999999999"
    14  
    15  // Timestamp represents the PostgreSQL timestamp type. The PostgreSQL
    16  // timestamp does not have a time zone. This presents a problem when
    17  // translating to and from time.Time which requires a time zone. It is highly
    18  // recommended to use timestamptz whenever possible. Timestamp methods either
    19  // convert to UTC or return an error on non-UTC times.
    20  type Timestamp struct {
    21  	Time             time.Time // Time must always be in UTC.
    22  	Status           Status
    23  	InfinityModifier InfinityModifier
    24  }
    25  
    26  // Set converts src into a Timestamp and stores in dst. If src is a
    27  // time.Time in a non-UTC time zone, the time zone is discarded.
    28  func (dst *Timestamp) Set(src interface{}) error {
    29  	if src == nil {
    30  		*dst = Timestamp{Status: Null}
    31  		return nil
    32  	}
    33  
    34  	if value, ok := src.(interface{ Get() interface{} }); ok {
    35  		value2 := value.Get()
    36  		if value2 != value {
    37  			return dst.Set(value2)
    38  		}
    39  	}
    40  
    41  	switch value := src.(type) {
    42  	case time.Time:
    43  		*dst = Timestamp{Time: time.Date(value.Year(), value.Month(), value.Day(), value.Hour(), value.Minute(), value.Second(), value.Nanosecond(), time.UTC), Status: Present}
    44  	case *time.Time:
    45  		if value == nil {
    46  			*dst = Timestamp{Status: Null}
    47  		} else {
    48  			return dst.Set(*value)
    49  		}
    50  	case string:
    51  		return dst.DecodeText(nil, []byte(value))
    52  	case *string:
    53  		if value == nil {
    54  			*dst = Timestamp{Status: Null}
    55  		} else {
    56  			return dst.Set(*value)
    57  		}
    58  	case InfinityModifier:
    59  		*dst = Timestamp{InfinityModifier: value, Status: Present}
    60  	default:
    61  		if originalSrc, ok := underlyingTimeType(src); ok {
    62  			return dst.Set(originalSrc)
    63  		}
    64  		return fmt.Errorf("cannot convert %v to Timestamp", value)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (dst Timestamp) Get() interface{} {
    71  	switch dst.Status {
    72  	case Present:
    73  		if dst.InfinityModifier != None {
    74  			return dst.InfinityModifier
    75  		}
    76  		return dst.Time
    77  	case Null:
    78  		return nil
    79  	default:
    80  		return dst.Status
    81  	}
    82  }
    83  
    84  func (src *Timestamp) AssignTo(dst interface{}) error {
    85  	switch src.Status {
    86  	case Present:
    87  		switch v := dst.(type) {
    88  		case *time.Time:
    89  			if src.InfinityModifier != None {
    90  				return fmt.Errorf("cannot assign %v to %T", src, dst)
    91  			}
    92  			*v = src.Time
    93  			return nil
    94  		default:
    95  			if nextDst, retry := GetAssignToDstType(dst); retry {
    96  				return src.AssignTo(nextDst)
    97  			}
    98  			return fmt.Errorf("unable to assign to %T", dst)
    99  		}
   100  	case Null:
   101  		return NullAssignTo(dst)
   102  	}
   103  
   104  	return fmt.Errorf("cannot decode %#v into %T", src, dst)
   105  }
   106  
   107  // DecodeText decodes from src into dst. The decoded time is considered to
   108  // be in UTC.
   109  func (dst *Timestamp) DecodeText(ci *ConnInfo, src []byte) error {
   110  	if src == nil {
   111  		*dst = Timestamp{Status: Null}
   112  		return nil
   113  	}
   114  
   115  	sbuf := string(src)
   116  	switch sbuf {
   117  	case "infinity":
   118  		*dst = Timestamp{Status: Present, InfinityModifier: Infinity}
   119  	case "-infinity":
   120  		*dst = Timestamp{Status: Present, InfinityModifier: -Infinity}
   121  	default:
   122  		if strings.HasSuffix(sbuf, " BC") {
   123  			t, err := time.Parse(pgTimestampFormat, strings.TrimRight(sbuf, " BC"))
   124  			t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
   125  			if err != nil {
   126  				return err
   127  			}
   128  			*dst = Timestamp{Time: t2, Status: Present}
   129  			return nil
   130  		}
   131  		tim, err := time.Parse(pgTimestampFormat, sbuf)
   132  		if err != nil {
   133  			return err
   134  		}
   135  
   136  		*dst = Timestamp{Time: tim, Status: Present}
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  // DecodeBinary decodes from src into dst. The decoded time is considered to
   143  // be in UTC.
   144  func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error {
   145  	if src == nil {
   146  		*dst = Timestamp{Status: Null}
   147  		return nil
   148  	}
   149  
   150  	if len(src) != 8 {
   151  		return fmt.Errorf("invalid length for timestamp: %v", len(src))
   152  	}
   153  
   154  	microsecSinceY2K := int64(binary.BigEndian.Uint64(src))
   155  
   156  	switch microsecSinceY2K {
   157  	case infinityMicrosecondOffset:
   158  		*dst = Timestamp{Status: Present, InfinityModifier: Infinity}
   159  	case negativeInfinityMicrosecondOffset:
   160  		*dst = Timestamp{Status: Present, InfinityModifier: -Infinity}
   161  	default:
   162  		tim := time.Unix(
   163  			microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000,
   164  			(microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000),
   165  		).UTC()
   166  		*dst = Timestamp{Time: tim, Status: Present}
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  // EncodeText writes the text encoding of src into w. If src.Time is not in
   173  // the UTC time zone it returns an error.
   174  func (src Timestamp) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
   175  	switch src.Status {
   176  	case Null:
   177  		return nil, nil
   178  	case Undefined:
   179  		return nil, errUndefined
   180  	}
   181  	if src.Time.Location() != time.UTC {
   182  		return nil, fmt.Errorf("cannot encode non-UTC time into timestamp")
   183  	}
   184  
   185  	var s string
   186  
   187  	switch src.InfinityModifier {
   188  	case None:
   189  		s = src.Time.Truncate(time.Microsecond).Format(pgTimestampFormat)
   190  	case Infinity:
   191  		s = "infinity"
   192  	case NegativeInfinity:
   193  		s = "-infinity"
   194  	}
   195  
   196  	return append(buf, s...), nil
   197  }
   198  
   199  // EncodeBinary writes the binary encoding of src into w. If src.Time is not in
   200  // the UTC time zone it returns an error.
   201  func (src Timestamp) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
   202  	switch src.Status {
   203  	case Null:
   204  		return nil, nil
   205  	case Undefined:
   206  		return nil, errUndefined
   207  	}
   208  	if src.Time.Location() != time.UTC {
   209  		return nil, fmt.Errorf("cannot encode non-UTC time into timestamp")
   210  	}
   211  
   212  	var microsecSinceY2K int64
   213  	switch src.InfinityModifier {
   214  	case None:
   215  		microsecSinceUnixEpoch := src.Time.Unix()*1000000 + int64(src.Time.Nanosecond())/1000
   216  		microsecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K
   217  	case Infinity:
   218  		microsecSinceY2K = infinityMicrosecondOffset
   219  	case NegativeInfinity:
   220  		microsecSinceY2K = negativeInfinityMicrosecondOffset
   221  	}
   222  
   223  	return pgio.AppendInt64(buf, microsecSinceY2K), nil
   224  }
   225  
   226  // Scan implements the database/sql Scanner interface.
   227  func (dst *Timestamp) Scan(src interface{}) error {
   228  	if src == nil {
   229  		*dst = Timestamp{Status: Null}
   230  		return nil
   231  	}
   232  
   233  	switch src := src.(type) {
   234  	case string:
   235  		return dst.DecodeText(nil, []byte(src))
   236  	case []byte:
   237  		srcCopy := make([]byte, len(src))
   238  		copy(srcCopy, src)
   239  		return dst.DecodeText(nil, srcCopy)
   240  	case time.Time:
   241  		*dst = Timestamp{Time: src, Status: Present}
   242  		return nil
   243  	}
   244  
   245  	return fmt.Errorf("cannot scan %T", src)
   246  }
   247  
   248  // Value implements the database/sql/driver Valuer interface.
   249  func (src Timestamp) Value() (driver.Value, error) {
   250  	switch src.Status {
   251  	case Present:
   252  		if src.InfinityModifier != None {
   253  			return src.InfinityModifier.String(), nil
   254  		}
   255  		return src.Time, nil
   256  	case Null:
   257  		return nil, nil
   258  	default:
   259  		return nil, errUndefined
   260  	}
   261  }
   262  

View as plain text