...

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

Documentation: github.com/jackc/pgtype

     1  package pgtype
     2  
     3  import (
     4  	"database/sql"
     5  	"database/sql/driver"
     6  	"encoding/binary"
     7  	"encoding/json"
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/jackc/pgio"
    13  )
    14  
    15  type Date struct {
    16  	Time             time.Time
    17  	Status           Status
    18  	InfinityModifier InfinityModifier
    19  }
    20  
    21  const (
    22  	negativeInfinityDayOffset = -2147483648
    23  	infinityDayOffset         = 2147483647
    24  )
    25  
    26  func (dst *Date) Set(src interface{}) error {
    27  	if src == nil {
    28  		*dst = Date{Status: Null}
    29  		return nil
    30  	}
    31  
    32  	if value, ok := src.(interface{ Get() interface{} }); ok {
    33  		value2 := value.Get()
    34  		if value2 != value {
    35  			return dst.Set(value2)
    36  		}
    37  	}
    38  
    39  	if value, ok := src.(interface{ Value() (driver.Value, error) }); ok {
    40  		v, err := value.Value()
    41  		if err != nil {
    42  			return fmt.Errorf("cannot get value %v for Date: %v", value, err)
    43  		}
    44  		return dst.Set(v)
    45  	}
    46  
    47  	switch value := src.(type) {
    48  	case time.Time:
    49  		*dst = Date{Time: value, Status: Present}
    50  	case *time.Time:
    51  		if value == nil {
    52  			*dst = Date{Status: Null}
    53  		} else {
    54  			return dst.Set(*value)
    55  		}
    56  	case string:
    57  		return dst.DecodeText(nil, []byte(value))
    58  	case *string:
    59  		if value == nil {
    60  			*dst = Date{Status: Null}
    61  		} else {
    62  			return dst.Set(*value)
    63  		}
    64  	default:
    65  		if originalSrc, ok := underlyingTimeType(src); ok {
    66  			return dst.Set(originalSrc)
    67  		}
    68  		return fmt.Errorf("cannot convert %v to Date", value)
    69  	}
    70  
    71  	return nil
    72  }
    73  
    74  func (dst Date) Get() interface{} {
    75  	switch dst.Status {
    76  	case Present:
    77  		if dst.InfinityModifier != None {
    78  			return dst.InfinityModifier
    79  		}
    80  		return dst.Time
    81  	case Null:
    82  		return nil
    83  	default:
    84  		return dst.Status
    85  	}
    86  }
    87  
    88  func (src *Date) AssignTo(dst interface{}) error {
    89  	if scanner, ok := dst.(sql.Scanner); ok {
    90  		var err error
    91  		switch src.Status {
    92  		case Present:
    93  			if src.InfinityModifier != None {
    94  				err = scanner.Scan(src.InfinityModifier.String())
    95  			} else {
    96  				err = scanner.Scan(src.Time)
    97  			}
    98  		case Null:
    99  			err = scanner.Scan(nil)
   100  		}
   101  		if err != nil {
   102  			return fmt.Errorf("unable assign %v to %T: %s", src, dst, err)
   103  		}
   104  		return nil
   105  	}
   106  
   107  	switch src.Status {
   108  	case Present:
   109  		switch v := dst.(type) {
   110  		case *time.Time:
   111  			if src.InfinityModifier != None {
   112  				return fmt.Errorf("cannot assign %v to %T", src, dst)
   113  			}
   114  			*v = src.Time
   115  			return nil
   116  		default:
   117  			if nextDst, retry := GetAssignToDstType(dst); retry {
   118  				return src.AssignTo(nextDst)
   119  			}
   120  			return fmt.Errorf("unable to assign to %T", dst)
   121  		}
   122  	case Null:
   123  		return NullAssignTo(dst)
   124  	}
   125  
   126  	return fmt.Errorf("cannot decode %#v into %T", src, dst)
   127  }
   128  
   129  func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error {
   130  	if src == nil {
   131  		*dst = Date{Status: Null}
   132  		return nil
   133  	}
   134  
   135  	sbuf := string(src)
   136  	switch sbuf {
   137  	case "infinity":
   138  		*dst = Date{Status: Present, InfinityModifier: Infinity}
   139  	case "-infinity":
   140  		*dst = Date{Status: Present, InfinityModifier: -Infinity}
   141  	default:
   142  		if strings.HasSuffix(sbuf, " BC") {
   143  			t, err := time.ParseInLocation("2006-01-02", strings.TrimRight(sbuf, " BC"), time.UTC)
   144  			t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
   145  			if err != nil {
   146  				return err
   147  			}
   148  			*dst = Date{Time: t2, Status: Present}
   149  			return nil
   150  		}
   151  		t, err := time.ParseInLocation("2006-01-02", sbuf, time.UTC)
   152  		if err != nil {
   153  			return err
   154  		}
   155  
   156  		*dst = Date{Time: t, Status: Present}
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  func (dst *Date) DecodeBinary(ci *ConnInfo, src []byte) error {
   163  	if src == nil {
   164  		*dst = Date{Status: Null}
   165  		return nil
   166  	}
   167  
   168  	if len(src) != 4 {
   169  		return fmt.Errorf("invalid length for date: %v", len(src))
   170  	}
   171  
   172  	dayOffset := int32(binary.BigEndian.Uint32(src))
   173  
   174  	switch dayOffset {
   175  	case infinityDayOffset:
   176  		*dst = Date{Status: Present, InfinityModifier: Infinity}
   177  	case negativeInfinityDayOffset:
   178  		*dst = Date{Status: Present, InfinityModifier: -Infinity}
   179  	default:
   180  		t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)
   181  		*dst = Date{Time: t, Status: Present}
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  func (src Date) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
   188  	switch src.Status {
   189  	case Null:
   190  		return nil, nil
   191  	case Undefined:
   192  		return nil, errUndefined
   193  	}
   194  
   195  	var s string
   196  
   197  	switch src.InfinityModifier {
   198  	case None:
   199  		s = src.Time.Format("2006-01-02")
   200  	case Infinity:
   201  		s = "infinity"
   202  	case NegativeInfinity:
   203  		s = "-infinity"
   204  	}
   205  
   206  	return append(buf, s...), nil
   207  }
   208  
   209  func (src Date) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
   210  	switch src.Status {
   211  	case Null:
   212  		return nil, nil
   213  	case Undefined:
   214  		return nil, errUndefined
   215  	}
   216  
   217  	var daysSinceDateEpoch int32
   218  	switch src.InfinityModifier {
   219  	case None:
   220  		tUnix := time.Date(src.Time.Year(), src.Time.Month(), src.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
   221  		dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
   222  
   223  		secSinceDateEpoch := tUnix - dateEpoch
   224  		daysSinceDateEpoch = int32(secSinceDateEpoch / 86400)
   225  	case Infinity:
   226  		daysSinceDateEpoch = infinityDayOffset
   227  	case NegativeInfinity:
   228  		daysSinceDateEpoch = negativeInfinityDayOffset
   229  	}
   230  
   231  	return pgio.AppendInt32(buf, daysSinceDateEpoch), nil
   232  }
   233  
   234  // Scan implements the database/sql Scanner interface.
   235  func (dst *Date) Scan(src interface{}) error {
   236  	if src == nil {
   237  		*dst = Date{Status: Null}
   238  		return nil
   239  	}
   240  
   241  	switch src := src.(type) {
   242  	case string:
   243  		return dst.DecodeText(nil, []byte(src))
   244  	case []byte:
   245  		srcCopy := make([]byte, len(src))
   246  		copy(srcCopy, src)
   247  		return dst.DecodeText(nil, srcCopy)
   248  	case time.Time:
   249  		*dst = Date{Time: src, Status: Present}
   250  		return nil
   251  	}
   252  
   253  	return fmt.Errorf("cannot scan %T", src)
   254  }
   255  
   256  // Value implements the database/sql/driver Valuer interface.
   257  func (src Date) Value() (driver.Value, error) {
   258  	switch src.Status {
   259  	case Present:
   260  		if src.InfinityModifier != None {
   261  			return src.InfinityModifier.String(), nil
   262  		}
   263  		return src.Time, nil
   264  	case Null:
   265  		return nil, nil
   266  	default:
   267  		return nil, errUndefined
   268  	}
   269  }
   270  
   271  func (src Date) MarshalJSON() ([]byte, error) {
   272  	switch src.Status {
   273  	case Null:
   274  		return []byte("null"), nil
   275  	case Undefined:
   276  		return nil, errUndefined
   277  	}
   278  
   279  	if src.Status != Present {
   280  		return nil, errBadStatus
   281  	}
   282  
   283  	var s string
   284  
   285  	switch src.InfinityModifier {
   286  	case None:
   287  		s = src.Time.Format("2006-01-02")
   288  	case Infinity:
   289  		s = "infinity"
   290  	case NegativeInfinity:
   291  		s = "-infinity"
   292  	}
   293  
   294  	return json.Marshal(s)
   295  }
   296  
   297  func (dst *Date) UnmarshalJSON(b []byte) error {
   298  	var s *string
   299  	err := json.Unmarshal(b, &s)
   300  	if err != nil {
   301  		return err
   302  	}
   303  
   304  	if s == nil {
   305  		*dst = Date{Status: Null}
   306  		return nil
   307  	}
   308  
   309  	switch *s {
   310  	case "infinity":
   311  		*dst = Date{Status: Present, InfinityModifier: Infinity}
   312  	case "-infinity":
   313  		*dst = Date{Status: Present, InfinityModifier: -Infinity}
   314  	default:
   315  		t, err := time.ParseInLocation("2006-01-02", *s, time.UTC)
   316  		if err != nil {
   317  			return err
   318  		}
   319  
   320  		*dst = Date{Time: t, Status: Present}
   321  	}
   322  
   323  	return nil
   324  }
   325  

View as plain text