github.com/ravendb/ravendb-go-client@v0.0.0-20240229102137-4474ee7aa0fa/time.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"encoding/json"
     5  	"strings"
     6  	"time"
     7  )
     8  
     9  const (
    10  	// time format returned by the server which looks like:
    11  	// 2018-05-08T05:20:31.5233900Z or
    12  	// 2018-07-29T23:50:57.9998240 or
    13  	// 2018-08-16T13:56:59.355664-07:00
    14  	timeFormat  = "2006-01-02T15:04:05.9999999Z"
    15  	timeFormat2 = "2006-01-02T15:04:05.9999999"
    16  	timeFormat3 = "2006-01-02T15:04:05.9999999-07:00"
    17  )
    18  
    19  // Time is an alias for time.Time that serializes/deserializes in ways
    20  // compatible with Ravendb server
    21  type Time time.Time
    22  
    23  // Format formats time in a way that RavenDB server understands.
    24  // RavenDB is strict enough that a single format can't
    25  // produce valid string values.
    26  func (t Time) Format() string {
    27  	s := time.Time(t).Format(timeFormat)
    28  	// ravendb server only accepts 7 digits for fraction part but Go's
    29  	// formatting might remove trailing zeros, producing 6 digits
    30  	dotIdx := strings.LastIndexByte(s, '.')
    31  
    32  	if dotIdx == -1 {
    33  		s = s[:len(s)-1] // remove 'Z'
    34  		s = s + ".0000000Z"
    35  	} else {
    36  		nToAdd := 9 - (len(s) - dotIdx) // 9: 7 + 1 for 'Z' and 1 for '.'
    37  		if nToAdd > 0 {
    38  			s = s[:len(s)-1] // remove 'Z'
    39  			for ; nToAdd > 0; nToAdd-- {
    40  				s = s + "0"
    41  			}
    42  			s = s + "Z"
    43  		}
    44  	}
    45  	return s
    46  }
    47  
    48  // ParseTime parses string time value returned by RavenDB server
    49  // The value can't be parsed with a single string format
    50  func ParseTime(s string) (time.Time, error) {
    51  	tt, err := time.Parse(timeFormat, s)
    52  	if err != nil {
    53  		tt, err = time.Parse(timeFormat2, s)
    54  		if err != nil {
    55  			tt, err = time.Parse(timeFormat3, s)
    56  		}
    57  	}
    58  	return tt, err
    59  }
    60  
    61  func (t Time) MarshalJSON() ([]byte, error) {
    62  	s := t.Format()
    63  	return []byte(`"` + s + `"`), nil
    64  }
    65  
    66  func (t *Time) UnmarshalJSON(d []byte) error {
    67  	s := string(d)
    68  	s = strings.TrimLeft(s, `"`)
    69  	s = strings.TrimRight(s, `"`)
    70  
    71  	if s == "null" {
    72  		return nil
    73  	}
    74  	tt, err := ParseTime(s)
    75  	*t = Time(tt)
    76  	if err != nil {
    77  		// TODO: for now make it a fatal error to catch bugs early
    78  		must(err)
    79  		return err
    80  	}
    81  	return nil
    82  }
    83  
    84  func (t *Time) toTime() time.Time {
    85  	// for convenience make it work on nil pointer
    86  	if t == nil {
    87  		return time.Time{}
    88  	}
    89  	return time.Time(*t)
    90  }
    91  
    92  func (t *Time) toTimePtr() *time.Time {
    93  	if t == nil {
    94  		return nil
    95  	}
    96  	res := time.Time(*t)
    97  	return &res
    98  }
    99  
   100  // RoundToServerTime rounds t to the same precision as round-tripping
   101  // to the server and back. Useful for comparing time.Time values for
   102  // equality with values returned by the server
   103  func RoundToServerTime(t time.Time) time.Time {
   104  	st := Time(t)
   105  	d, err := json.Marshal(st)
   106  	must(err)
   107  	var res Time
   108  	err = json.Unmarshal(d, &res)
   109  	must(err)
   110  	return time.Time(res)
   111  }