...

Source file src/github.com/jackc/pgx/v5/pgtype/time.go

Documentation: github.com/jackc/pgx/v5/pgtype

     1  package pgtype
     2  
     3  import (
     4  	"database/sql/driver"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"strconv"
     8  
     9  	"github.com/jackc/pgx/v5/internal/pgio"
    10  )
    11  
    12  type TimeScanner interface {
    13  	ScanTime(v Time) error
    14  }
    15  
    16  type TimeValuer interface {
    17  	TimeValue() (Time, error)
    18  }
    19  
    20  // Time represents the PostgreSQL time type. The PostgreSQL time is a time of day without time zone.
    21  //
    22  // Time is represented as the number of microseconds since midnight in the same way that PostgreSQL does. Other time
    23  // and date types in pgtype can use time.Time as the underlying representation. However, pgtype.Time type cannot due
    24  // to needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day.
    25  type Time struct {
    26  	Microseconds int64 // Number of microseconds since midnight
    27  	Valid        bool
    28  }
    29  
    30  func (t *Time) ScanTime(v Time) error {
    31  	*t = v
    32  	return nil
    33  }
    34  
    35  func (t Time) TimeValue() (Time, error) {
    36  	return t, nil
    37  }
    38  
    39  // Scan implements the database/sql Scanner interface.
    40  func (t *Time) Scan(src any) error {
    41  	if src == nil {
    42  		*t = Time{}
    43  		return nil
    44  	}
    45  
    46  	switch src := src.(type) {
    47  	case string:
    48  		return scanPlanTextAnyToTimeScanner{}.Scan([]byte(src), t)
    49  	}
    50  
    51  	return fmt.Errorf("cannot scan %T", src)
    52  }
    53  
    54  // Value implements the database/sql/driver Valuer interface.
    55  func (t Time) Value() (driver.Value, error) {
    56  	if !t.Valid {
    57  		return nil, nil
    58  	}
    59  
    60  	buf, err := TimeCodec{}.PlanEncode(nil, 0, TextFormatCode, t).Encode(t, nil)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return string(buf), err
    65  }
    66  
    67  type TimeCodec struct{}
    68  
    69  func (TimeCodec) FormatSupported(format int16) bool {
    70  	return format == TextFormatCode || format == BinaryFormatCode
    71  }
    72  
    73  func (TimeCodec) PreferredFormat() int16 {
    74  	return BinaryFormatCode
    75  }
    76  
    77  func (TimeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
    78  	if _, ok := value.(TimeValuer); !ok {
    79  		return nil
    80  	}
    81  
    82  	switch format {
    83  	case BinaryFormatCode:
    84  		return encodePlanTimeCodecBinary{}
    85  	case TextFormatCode:
    86  		return encodePlanTimeCodecText{}
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  type encodePlanTimeCodecBinary struct{}
    93  
    94  func (encodePlanTimeCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
    95  	t, err := value.(TimeValuer).TimeValue()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	if !t.Valid {
   101  		return nil, nil
   102  	}
   103  
   104  	return pgio.AppendInt64(buf, t.Microseconds), nil
   105  }
   106  
   107  type encodePlanTimeCodecText struct{}
   108  
   109  func (encodePlanTimeCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
   110  	t, err := value.(TimeValuer).TimeValue()
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	if !t.Valid {
   116  		return nil, nil
   117  	}
   118  
   119  	usec := t.Microseconds
   120  	hours := usec / microsecondsPerHour
   121  	usec -= hours * microsecondsPerHour
   122  	minutes := usec / microsecondsPerMinute
   123  	usec -= minutes * microsecondsPerMinute
   124  	seconds := usec / microsecondsPerSecond
   125  	usec -= seconds * microsecondsPerSecond
   126  
   127  	s := fmt.Sprintf("%02d:%02d:%02d.%06d", hours, minutes, seconds, usec)
   128  
   129  	return append(buf, s...), nil
   130  }
   131  
   132  func (TimeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
   133  
   134  	switch format {
   135  	case BinaryFormatCode:
   136  		switch target.(type) {
   137  		case TimeScanner:
   138  			return scanPlanBinaryTimeToTimeScanner{}
   139  		}
   140  	case TextFormatCode:
   141  		switch target.(type) {
   142  		case TimeScanner:
   143  			return scanPlanTextAnyToTimeScanner{}
   144  		}
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  type scanPlanBinaryTimeToTimeScanner struct{}
   151  
   152  func (scanPlanBinaryTimeToTimeScanner) Scan(src []byte, dst any) error {
   153  	scanner := (dst).(TimeScanner)
   154  
   155  	if src == nil {
   156  		return scanner.ScanTime(Time{})
   157  	}
   158  
   159  	if len(src) != 8 {
   160  		return fmt.Errorf("invalid length for time: %v", len(src))
   161  	}
   162  
   163  	usec := int64(binary.BigEndian.Uint64(src))
   164  
   165  	return scanner.ScanTime(Time{Microseconds: usec, Valid: true})
   166  }
   167  
   168  type scanPlanTextAnyToTimeScanner struct{}
   169  
   170  func (scanPlanTextAnyToTimeScanner) Scan(src []byte, dst any) error {
   171  	scanner := (dst).(TimeScanner)
   172  
   173  	if src == nil {
   174  		return scanner.ScanTime(Time{})
   175  	}
   176  
   177  	s := string(src)
   178  
   179  	if len(s) < 8 {
   180  		return fmt.Errorf("cannot decode %v into Time", s)
   181  	}
   182  
   183  	hours, err := strconv.ParseInt(s[0:2], 10, 64)
   184  	if err != nil {
   185  		return fmt.Errorf("cannot decode %v into Time", s)
   186  	}
   187  	usec := hours * microsecondsPerHour
   188  
   189  	minutes, err := strconv.ParseInt(s[3:5], 10, 64)
   190  	if err != nil {
   191  		return fmt.Errorf("cannot decode %v into Time", s)
   192  	}
   193  	usec += minutes * microsecondsPerMinute
   194  
   195  	seconds, err := strconv.ParseInt(s[6:8], 10, 64)
   196  	if err != nil {
   197  		return fmt.Errorf("cannot decode %v into Time", s)
   198  	}
   199  	usec += seconds * microsecondsPerSecond
   200  
   201  	if len(s) > 9 {
   202  		fraction := s[9:]
   203  		n, err := strconv.ParseInt(fraction, 10, 64)
   204  		if err != nil {
   205  			return fmt.Errorf("cannot decode %v into Time", s)
   206  		}
   207  
   208  		for i := len(fraction); i < 6; i++ {
   209  			n *= 10
   210  		}
   211  
   212  		usec += n
   213  	}
   214  
   215  	return scanner.ScanTime(Time{Microseconds: usec, Valid: true})
   216  }
   217  
   218  func (c TimeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
   219  	return codecDecodeToTextFormat(c, m, oid, format, src)
   220  }
   221  
   222  func (c TimeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
   223  	if src == nil {
   224  		return nil, nil
   225  	}
   226  
   227  	var t Time
   228  	err := codecScan(c, m, oid, format, src, &t)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	return t, nil
   233  }
   234  

View as plain text