Compare commits

...

5 Commits

Author SHA1 Message Date
a6252f0743 v0.0.93 2023-03-15 15:41:55 +01:00
86c01659d7 base58 2023-03-15 14:00:48 +01:00
62acddda5e v0.0.91 2023-03-11 14:38:19 +01:00
ee325f67fd v0.0.90 2023-03-09 14:51:53 +01:00
dba0cd229e v0.0.89 2023-03-07 10:43:30 +01:00
13 changed files with 558 additions and 145 deletions

View File

@@ -32,7 +32,13 @@ echo ""
git add --verbose . git add --verbose .
git commit -a -m "v${next_ver}" msg="v${next_ver}"
if [ $# -gt 0 ]; then
msg="$1"
fi
git commit -a -m "${msg}"
git tag "v${next_ver}" git tag "v${next_ver}"

View File

@@ -22,10 +22,10 @@ import (
// //
// sub-structs are recursively parsed (if they have an env tag) and the env-variable keys are delimited by the delim parameter // sub-structs are recursively parsed (if they have an env tag) and the env-variable keys are delimited by the delim parameter
// sub-structs with `env:""` are also parsed, but the delimited is skipped (they are handled as if they were one level higher) // sub-structs with `env:""` are also parsed, but the delimited is skipped (they are handled as if they were one level higher)
func ApplyEnvOverrides[T any](c *T, delim string) error { func ApplyEnvOverrides[T any](prefix string, c *T, delim string) error {
rval := reflect.ValueOf(c).Elem() rval := reflect.ValueOf(c).Elem()
return processEnvOverrides(rval, delim, "") return processEnvOverrides(rval, delim, prefix)
} }
func processEnvOverrides(rval reflect.Value, delim string, prefix string) error { func processEnvOverrides(rval reflect.Value, delim string, prefix string) error {
@@ -70,103 +70,114 @@ func processEnvOverrides(rval reflect.Value, delim string, prefix string) error
continue continue
} }
if rvfield.Type() == reflect.TypeOf("") { if rvfield.Type().Kind() == reflect.Pointer {
rvfield.Set(reflect.ValueOf(envval)) newval, err := parseEnvToValue(envval, fullEnvKey, rvfield.Type().Elem())
if err != nil {
return err
}
// converts reflect.Value to pointer
ptrval := reflect.New(rvfield.Type().Elem())
ptrval.Elem().Set(newval)
rvfield.Set(ptrval)
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval) fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval)
} else if rvfield.Type() == reflect.TypeOf(int(0)) {
envint, err := strconv.ParseInt(envval, 10, bits.UintSize)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int (value := '%s')", fullEnvKey, envval))
}
rvfield.Set(reflect.ValueOf(int(envint)))
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval)
} else if rvfield.Type() == reflect.TypeOf(int64(0)) {
envint, err := strconv.ParseInt(envval, 10, 64)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int64 (value := '%s')", fullEnvKey, envval))
}
rvfield.Set(reflect.ValueOf(int64(envint)))
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval)
} else if rvfield.Type() == reflect.TypeOf(int32(0)) {
envint, err := strconv.ParseInt(envval, 10, 32)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int32 (value := '%s')", fullEnvKey, envval))
}
rvfield.Set(reflect.ValueOf(int32(envint)))
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval)
} else if rvfield.Type() == reflect.TypeOf(int8(0)) {
envint, err := strconv.ParseInt(envval, 10, 8)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int32 (value := '%s')", fullEnvKey, envval))
}
rvfield.Set(reflect.ValueOf(int8(envint)))
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval)
} else if rvfield.Type() == reflect.TypeOf(time.Duration(0)) {
dur, err := timeext.ParseDurationShortString(envval)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to duration (value := '%s')", fullEnvKey, envval))
}
rvfield.Set(reflect.ValueOf(dur))
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, dur.String())
} else if rvfield.Type() == reflect.TypeOf(time.UnixMilli(0)) {
tim, err := time.Parse(time.RFC3339Nano, envval)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to time.time (value := '%s')", fullEnvKey, envval))
}
rvfield.Set(reflect.ValueOf(tim))
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, tim.String())
} else if rvfield.Type().ConvertibleTo(reflect.TypeOf(int(0))) {
envint, err := strconv.ParseInt(envval, 10, 8)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to <%s, ,int> (value := '%s')", rvfield.Type().Name(), fullEnvKey, envval))
}
envcvl := reflect.ValueOf(envint).Convert(rvfield.Type())
rvfield.Set(envcvl)
fmt.Printf("[CONF] Overwrite config '%s' with '%v'\n", fullEnvKey, envcvl.Interface())
} else if rvfield.Type().ConvertibleTo(reflect.TypeOf("")) {
envcvl := reflect.ValueOf(envval).Convert(rvfield.Type())
rvfield.Set(envcvl)
fmt.Printf("[CONF] Overwrite config '%s' with '%v'\n", fullEnvKey, envcvl.Interface())
} else { } else {
return errors.New(fmt.Sprintf("Unknown kind/type in config: [ %s | %s ]", rvfield.Kind().String(), rvfield.Type().String()))
newval, err := parseEnvToValue(envval, fullEnvKey, rvfield.Type())
if err != nil {
return err
} }
rvfield.Set(newval)
fmt.Printf("[CONF] Overwrite config '%s' with '%s'\n", fullEnvKey, envval)
}
} }
return nil return nil
} }
func parseEnvToValue(envval string, fullEnvKey string, rvtype reflect.Type) (reflect.Value, error) {
if rvtype == reflect.TypeOf("") {
return reflect.ValueOf(envval), nil
} else if rvtype == reflect.TypeOf(int(0)) {
envint, err := strconv.ParseInt(envval, 10, bits.UintSize)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int (value := '%s')", fullEnvKey, envval))
}
return reflect.ValueOf(int(envint)), nil
} else if rvtype == reflect.TypeOf(int64(0)) {
envint, err := strconv.ParseInt(envval, 10, 64)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int64 (value := '%s')", fullEnvKey, envval))
}
return reflect.ValueOf(int64(envint)), nil
} else if rvtype == reflect.TypeOf(int32(0)) {
envint, err := strconv.ParseInt(envval, 10, 32)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int32 (value := '%s')", fullEnvKey, envval))
}
return reflect.ValueOf(int32(envint)), nil
} else if rvtype == reflect.TypeOf(int8(0)) {
envint, err := strconv.ParseInt(envval, 10, 8)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to int32 (value := '%s')", fullEnvKey, envval))
}
return reflect.ValueOf(int8(envint)), nil
} else if rvtype == reflect.TypeOf(time.Duration(0)) {
dur, err := timeext.ParseDurationShortString(envval)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to duration (value := '%s')", fullEnvKey, envval))
}
return reflect.ValueOf(dur), nil
} else if rvtype == reflect.TypeOf(time.UnixMilli(0)) {
tim, err := time.Parse(time.RFC3339Nano, envval)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to time.time (value := '%s')", fullEnvKey, envval))
}
return reflect.ValueOf(tim), nil
} else if rvtype.ConvertibleTo(reflect.TypeOf(int(0))) {
envint, err := strconv.ParseInt(envval, 10, 8)
if err != nil {
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to <%s, ,int> (value := '%s')", rvtype.Name(), fullEnvKey, envval))
}
envcvl := reflect.ValueOf(envint).Convert(rvtype)
return envcvl, nil
} else if rvtype.ConvertibleTo(reflect.TypeOf("")) {
envcvl := reflect.ValueOf(envval).Convert(rvtype)
return envcvl, nil
} else {
return reflect.Value{}, errors.New(fmt.Sprintf("Unknown kind/type in config: [ %s | %s ]", rvtype.Kind().String(), rvtype.String()))
}
}

View File

@@ -213,8 +213,65 @@ func TestApplyEnvOverridesRecursive(t *testing.T) {
assertEqual(t, data.Sub4.V9, time.Unix(2335219200, 0).UTC()) assertEqual(t, data.Sub4.V9, time.Unix(2335219200, 0).UTC())
} }
func TestApplyEnvOverridesPointer(t *testing.T) {
type aliasint int
type aliasstring string
type testdata struct {
V1 *int `env:"TEST_V1"`
VX *string ``
V2 *string `env:"TEST_V2"`
V3 *int8 `env:"TEST_V3"`
V4 *int32 `env:"TEST_V4"`
V5 *int64 `env:"TEST_V5"`
V6 *aliasint `env:"TEST_V6"`
VY *aliasint ``
V7 *aliasstring `env:"TEST_V7"`
V8 *time.Duration `env:"TEST_V8"`
V9 *time.Time `env:"TEST_V9"`
}
data := testdata{}
t.Setenv("TEST_V1", "846")
t.Setenv("TEST_V2", "hello_world")
t.Setenv("TEST_V3", "6")
t.Setenv("TEST_V4", "333")
t.Setenv("TEST_V5", "-937")
t.Setenv("TEST_V6", "070")
t.Setenv("TEST_V7", "AAAAAA")
t.Setenv("TEST_V8", "1min4s")
t.Setenv("TEST_V9", "2009-11-10T23:00:00Z")
err := ApplyEnvOverrides(&data, ".")
if err != nil {
t.Errorf("%v", err)
t.FailNow()
}
assertPtrEqual(t, data.V1, 846)
assertPtrEqual(t, data.V2, "hello_world")
assertPtrEqual(t, data.V3, 6)
assertPtrEqual(t, data.V4, 333)
assertPtrEqual(t, data.V5, -937)
assertPtrEqual(t, data.V6, 70)
assertPtrEqual(t, data.V7, "AAAAAA")
assertPtrEqual(t, data.V8, time.Second*64)
assertPtrEqual(t, data.V9, time.Unix(1257894000, 0).UTC())
}
func assertEqual[T comparable](t *testing.T, actual T, expected T) { func assertEqual[T comparable](t *testing.T, actual T, expected T) {
if actual != expected { if actual != expected {
t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected) t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
} }
} }
func assertPtrEqual[T comparable](t *testing.T, actual *T, expected T) {
if actual == nil {
t.Errorf("values differ: Actual: NIL, Expected: '%v'", expected)
}
if *actual != expected {
t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
}
}

View File

@@ -295,6 +295,16 @@ func ArrFilterMap[T1 any, T2 any](arr []T1, filter func(v T1) bool, conv func(v
return r return r
} }
func ArrFilter[T any](arr []T, filter func(v T) bool) []T {
r := make([]T, 0, len(arr))
for _, v := range arr {
if filter(v) {
r = append(r, v)
}
}
return r
}
func ArrSum[T NumberConstraint](arr []T) T { func ArrSum[T NumberConstraint](arr []T) T {
var r T = 0 var r T = 0
for _, v := range arr { for _, v := range arr {

178
langext/base58.go Normal file
View File

@@ -0,0 +1,178 @@
package langext
import (
"bytes"
"errors"
"math/big"
)
// shamelessly stolen from https://github.com/btcsuite/
type B58Encoding struct {
bigRadix [11]*big.Int
bigRadix10 *big.Int
alphabet string
alphabetIdx0 byte
b58 [256]byte
}
var Base58DefaultEncoding = newBase58Encoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
var Base58FlickrEncoding = newBase58Encoding("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ")
var Base58RippleEncoding = newBase58Encoding("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz")
var Base58BitcoinEncoding = newBase58Encoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
func newBase58Encoding(alphabet string) *B58Encoding {
bigRadix10 := big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58)
enc := &B58Encoding{
alphabet: alphabet,
alphabetIdx0: '1',
bigRadix: [...]*big.Int{
big.NewInt(0),
big.NewInt(58),
big.NewInt(58 * 58),
big.NewInt(58 * 58 * 58),
big.NewInt(58 * 58 * 58 * 58),
big.NewInt(58 * 58 * 58 * 58 * 58),
big.NewInt(58 * 58 * 58 * 58 * 58 * 58),
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58),
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58),
bigRadix10,
},
bigRadix10: bigRadix10,
}
b58 := make([]byte, 0, 256)
for i := byte(0); i < 32; i++ {
for j := byte(0); j < 8; j++ {
b := i*8 + j
idx := bytes.IndexByte([]byte(alphabet), b)
if idx == -1 {
b58 = append(b58, 255)
} else {
b58 = append(b58, byte(idx))
}
}
}
enc.b58 = *((*[256]byte)(b58))
return enc
}
func (enc *B58Encoding) EncodeString(src string) (string, error) {
v, err := enc.Encode([]byte(src))
if err != nil {
return "", err
}
return string(v), nil
}
func (enc *B58Encoding) Encode(src []byte) ([]byte, error) {
x := new(big.Int)
x.SetBytes(src)
// maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
maxlen := int(float64(len(src))*1.365658237309761) + 1
answer := make([]byte, 0, maxlen)
mod := new(big.Int)
for x.Sign() > 0 {
// Calculating with big.Int is slow for each iteration.
// x, mod = x / 58, x % 58
//
// Instead we can try to do as much calculations on int64.
// x, mod = x / 58^10, x % 58^10
//
// Which will give us mod, which is 10 digit base58 number.
// We'll loop that 10 times to convert to the answer.
x.DivMod(x, enc.bigRadix10, mod)
if x.Sign() == 0 {
// When x = 0, we need to ensure we don't add any extra zeros.
m := mod.Int64()
for m > 0 {
answer = append(answer, enc.alphabet[m%58])
m /= 58
}
} else {
m := mod.Int64()
for i := 0; i < 10; i++ {
answer = append(answer, enc.alphabet[m%58])
m /= 58
}
}
}
// leading zero bytes
for _, i := range src {
if i != 0 {
break
}
answer = append(answer, enc.alphabetIdx0)
}
// reverse
alen := len(answer)
for i := 0; i < alen/2; i++ {
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
}
return answer, nil
}
func (enc *B58Encoding) DecodeString(src string) (string, error) {
v, err := enc.Decode([]byte(src))
if err != nil {
return "", err
}
return string(v), nil
}
func (enc *B58Encoding) Decode(src []byte) ([]byte, error) {
answer := big.NewInt(0)
scratch := new(big.Int)
for t := src; len(t) > 0; {
n := len(t)
if n > 10 {
n = 10
}
total := uint64(0)
for _, v := range t[:n] {
if v > 255 {
return []byte{}, errors.New("invalid char in input")
}
tmp := enc.b58[v]
if tmp == 255 {
return []byte{}, errors.New("invalid char in input")
}
total = total*58 + uint64(tmp)
}
answer.Mul(answer, enc.bigRadix[n])
scratch.SetUint64(total)
answer.Add(answer, scratch)
t = t[n:]
}
tmpval := answer.Bytes()
var numZeros int
for numZeros = 0; numZeros < len(src); numZeros++ {
if src[numZeros] != enc.alphabetIdx0 {
break
}
}
flen := numZeros + len(tmpval)
val := make([]byte, flen)
copy(val[numZeros:], tmpval)
return val, nil
}

67
langext/base58_test.go Normal file
View File

@@ -0,0 +1,67 @@
package langext
import (
"testing"
)
func _encStr(t *testing.T, enc *B58Encoding, v string) string {
v, err := enc.EncodeString(v)
if err != nil {
t.Error(err)
}
return v
}
func _decStr(t *testing.T, enc *B58Encoding, v string) string {
v, err := enc.DecodeString(v)
if err != nil {
t.Error(err)
}
return v
}
func TestBase58DefaultEncoding(t *testing.T) {
assertEqual(t, _encStr(t, Base58DefaultEncoding, "Hello"), "9Ajdvzr")
assertEqual(t, _encStr(t, Base58DefaultEncoding, "If debugging is the process of removing software bugs, then programming must be the process of putting them in."), "48638SMcJuah5okqPx4kCVf5d8QAdgbdNf28g7ReY13prUENNbMyssjq5GjsrJHF5zeZfqs4uJMUJHr7VbrU4XBUZ2Fw9DVtqtn9N1eXucEWSEZahXV6w4ysGSWqGdpeYTJf1MdDzTg8vfcQViifJjZX")
}
func TestBase58DefaultDecoding(t *testing.T) {
assertEqual(t, _decStr(t, Base58DefaultEncoding, "9Ajdvzr"), "Hello")
assertEqual(t, _decStr(t, Base58DefaultEncoding, "48638SMcJuah5okqPx4kCVf5d8QAdgbdNf28g7ReY13prUENNbMyssjq5GjsrJHF5zeZfqs4uJMUJHr7VbrU4XBUZ2Fw9DVtqtn9N1eXucEWSEZahXV6w4ysGSWqGdpeYTJf1MdDzTg8vfcQViifJjZX"), "If debugging is the process of removing software bugs, then programming must be the process of putting them in.")
}
func TestBase58RippleEncoding(t *testing.T) {
assertEqual(t, _encStr(t, Base58RippleEncoding, "Hello"), "9wjdvzi")
assertEqual(t, _encStr(t, Base58RippleEncoding, "If debugging is the process of removing software bugs, then programming must be the process of putting them in."), "h3as3SMcJu26nokqPxhkUVCnd3Qwdgbd4Cp3gfReYrsFi7N44bMy11jqnGj1iJHEnzeZCq1huJM7JHifVbi7hXB7ZpEA9DVtqt894reXucNWSNZ26XVaAhy1GSWqGdFeYTJCrMdDzTg3vCcQV55CJjZX")
}
func TestBase58RippleDecoding(t *testing.T) {
assertEqual(t, _decStr(t, Base58RippleEncoding, "9wjdvzi"), "Hello")
assertEqual(t, _decStr(t, Base58RippleEncoding, "h3as3SMcJu26nokqPxhkUVCnd3Qwdgbd4Cp3gfReYrsFi7N44bMy11jqnGj1iJHEnzeZCq1huJM7JHifVbi7hXB7ZpEA9DVtqt894reXucNWSNZ26XVaAhy1GSWqGdFeYTJCrMdDzTg3vCcQV55CJjZX"), "If debugging is the process of removing software bugs, then programming must be the process of putting them in.")
}
func TestBase58BitcoinEncoding(t *testing.T) {
assertEqual(t, _encStr(t, Base58BitcoinEncoding, "Hello"), "9Ajdvzr")
assertEqual(t, _encStr(t, Base58BitcoinEncoding, "If debugging is the process of removing software bugs, then programming must be the process of putting them in."), "48638SMcJuah5okqPx4kCVf5d8QAdgbdNf28g7ReY13prUENNbMyssjq5GjsrJHF5zeZfqs4uJMUJHr7VbrU4XBUZ2Fw9DVtqtn9N1eXucEWSEZahXV6w4ysGSWqGdpeYTJf1MdDzTg8vfcQViifJjZX")
}
func TestBase58BitcoinDecoding(t *testing.T) {
assertEqual(t, _decStr(t, Base58BitcoinEncoding, "9Ajdvzr"), "Hello")
assertEqual(t, _decStr(t, Base58BitcoinEncoding, "48638SMcJuah5okqPx4kCVf5d8QAdgbdNf28g7ReY13prUENNbMyssjq5GjsrJHF5zeZfqs4uJMUJHr7VbrU4XBUZ2Fw9DVtqtn9N1eXucEWSEZahXV6w4ysGSWqGdpeYTJf1MdDzTg8vfcQViifJjZX"), "If debugging is the process of removing software bugs, then programming must be the process of putting them in.")
}
func TestBase58FlickrEncoding(t *testing.T) {
assertEqual(t, _encStr(t, Base58FlickrEncoding, "Hello"), "9aJCVZR")
assertEqual(t, _encStr(t, Base58FlickrEncoding, "If debugging is the process of removing software bugs, then programming must be the process of putting them in."), "48638rmBiUzG5NKQoX4KcuE5C8paCFACnE28F7qDx13PRtennAmYSSJQ5gJSRihf5ZDyEQS4UimtihR7uARt4wbty2fW9duTQTM9n1DwUBevreyzGwu6W4YSgrvQgCPDxsiE1mCdZsF8VEBpuHHEiJyw")
}
func TestBase58FlickrDecoding(t *testing.T) {
assertEqual(t, _decStr(t, Base58FlickrEncoding, "9aJCVZR"), "Hello")
assertEqual(t, _decStr(t, Base58FlickrEncoding, "48638rmBiUzG5NKQoX4KcuE5C8paCFACnE28F7qDx13PRtennAmYSSJQ5gJSRihf5ZDyEQS4UimtihR7uARt4wbty2fW9duTQTM9n1DwUBevreyzGwu6W4YSgrvQgCPDxsiE1mCdZsF8VEBpuHHEiJyw"), "If debugging is the process of removing software bugs, then programming must be the process of putting them in.")
}
func assertEqual(t *testing.T, actual string, expected string) {
if actual != expected {
t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
}
}

View File

@@ -3,24 +3,27 @@ package rfctime
import "time" import "time"
type RFCTime interface { type RFCTime interface {
AnyTime
Time() time.Time Time() time.Time
Serialize() string Serialize() string
UnmarshalJSON(bytes []byte) error After(u AnyTime) bool
Before(u AnyTime) bool
Equal(u AnyTime) bool
Sub(u AnyTime) time.Duration
}
type AnyTime interface {
MarshalJSON() ([]byte, error) MarshalJSON() ([]byte, error)
MarshalBinary() ([]byte, error) MarshalBinary() ([]byte, error)
UnmarshalBinary(data []byte) error
GobEncode() ([]byte, error) GobEncode() ([]byte, error)
GobDecode(data []byte) error
MarshalText() ([]byte, error) MarshalText() ([]byte, error)
UnmarshalText(data []byte) error
After(u RFCTime) bool
Before(u RFCTime) bool
Equal(u RFCTime) bool
IsZero() bool IsZero() bool
Date() (year int, month time.Month, day int) Date() (year int, month time.Month, day int)
Year() int Year() int
@@ -34,7 +37,6 @@ type RFCTime interface {
Second() int Second() int
Nanosecond() int Nanosecond() int
YearDay() int YearDay() int
Sub(u RFCTime) time.Duration
Unix() int64 Unix() int64
UnixMilli() int64 UnixMilli() int64
UnixMicro() int64 UnixMicro() int64
@@ -42,6 +44,8 @@ type RFCTime interface {
Format(layout string) string Format(layout string) string
GoString() string GoString() string
String() string String() string
Location() *time.Location
} }
type RFCDuration interface { type RFCDuration interface {
@@ -60,9 +64,9 @@ type RFCDuration interface {
MarshalText() ([]byte, error) MarshalText() ([]byte, error)
UnmarshalText(data []byte) error UnmarshalText(data []byte) error
After(u RFCTime) bool After(u AnyTime) bool
Before(u RFCTime) bool Before(u AnyTime) bool
Equal(u RFCTime) bool Equal(u AnyTime) bool
IsZero() bool IsZero() bool
Date() (year int, month time.Month, day int) Date() (year int, month time.Month, day int)
Year() int Year() int
@@ -76,7 +80,7 @@ type RFCDuration interface {
Second() int Second() int
Nanosecond() int Nanosecond() int
YearDay() int YearDay() int
Sub(u RFCTime) time.Duration Sub(u AnyTime) time.Duration
Unix() int64 Unix() int64
UnixMilli() int64 UnixMilli() int64
UnixMicro() int64 UnixMicro() int64
@@ -85,3 +89,13 @@ type RFCDuration interface {
GoString() string GoString() string
String() string String() string
} }
func tt(v AnyTime) time.Time {
if r, ok := v.(time.Time); ok {
return r
}
if r, ok := v.(RFCTime); ok {
return r.Time()
}
return time.Unix(0, v.UnixNano()).In(v.Location())
}

50
rfctime/interface_test.go Normal file
View File

@@ -0,0 +1,50 @@
package rfctime
import (
"testing"
"time"
)
func TestAnyTimeInterface(t *testing.T) {
var v AnyTime
v = NowRFC3339Nano()
assertEqual(t, v.String(), v.String())
v = NowRFC3339()
assertEqual(t, v.String(), v.String())
v = NowUnix()
assertEqual(t, v.String(), v.String())
v = NowUnixMilli()
assertEqual(t, v.String(), v.String())
v = NowUnixNano()
assertEqual(t, v.String(), v.String())
v = time.Now()
assertEqual(t, v.String(), v.String())
}
func TestRFCTimeInterface(t *testing.T) {
var v RFCTime
v = NowRFC3339Nano()
assertEqual(t, v.String(), v.String())
v = NowRFC3339()
assertEqual(t, v.String(), v.String())
v = NowUnix()
assertEqual(t, v.String(), v.String())
v = NowUnixMilli()
assertEqual(t, v.String(), v.String())
v = NowUnixNano()
assertEqual(t, v.String(), v.String())
}

View File

@@ -90,16 +90,16 @@ func (t RFC3339Time) FormatStr() string {
return time.RFC3339 return time.RFC3339
} }
func (t RFC3339Time) After(u RFCTime) bool { func (t RFC3339Time) After(u AnyTime) bool {
return t.Time().After(u.Time()) return t.Time().After(tt(u))
} }
func (t RFC3339Time) Before(u RFCTime) bool { func (t RFC3339Time) Before(u AnyTime) bool {
return t.Time().Before(u.Time()) return t.Time().Before(tt(u))
} }
func (t RFC3339Time) Equal(u RFCTime) bool { func (t RFC3339Time) Equal(u AnyTime) bool {
return t.Time().Equal(u.Time()) return t.Time().Equal(tt(u))
} }
func (t RFC3339Time) IsZero() bool { func (t RFC3339Time) IsZero() bool {
@@ -158,8 +158,8 @@ func (t RFC3339Time) Add(d time.Duration) RFC3339Time {
return RFC3339Time(t.Time().Add(d)) return RFC3339Time(t.Time().Add(d))
} }
func (t RFC3339Time) Sub(u RFCTime) time.Duration { func (t RFC3339Time) Sub(u AnyTime) time.Duration {
return t.Time().Sub(u.Time()) return t.Time().Sub(tt(u))
} }
func (t RFC3339Time) AddDate(years int, months int, days int) RFC3339Time { func (t RFC3339Time) AddDate(years int, months int, days int) RFC3339Time {
@@ -194,6 +194,10 @@ func (t RFC3339Time) String() string {
return t.Time().String() return t.Time().String()
} }
func (t RFC3339Time) Location() *time.Location {
return t.Time().Location()
}
func NewRFC3339(t time.Time) RFC3339Time { func NewRFC3339(t time.Time) RFC3339Time {
return RFC3339Time(t) return RFC3339Time(t)
} }

View File

@@ -90,16 +90,16 @@ func (t RFC3339NanoTime) FormatStr() string {
return time.RFC3339Nano return time.RFC3339Nano
} }
func (t RFC3339NanoTime) After(u RFCTime) bool { func (t RFC3339NanoTime) After(u AnyTime) bool {
return t.Time().After(u.Time()) return t.Time().After(tt(u))
} }
func (t RFC3339NanoTime) Before(u RFCTime) bool { func (t RFC3339NanoTime) Before(u AnyTime) bool {
return t.Time().Before(u.Time()) return t.Time().Before(tt(u))
} }
func (t RFC3339NanoTime) Equal(u RFCTime) bool { func (t RFC3339NanoTime) Equal(u AnyTime) bool {
return t.Time().Equal(u.Time()) return t.Time().Equal(tt(u))
} }
func (t RFC3339NanoTime) IsZero() bool { func (t RFC3339NanoTime) IsZero() bool {
@@ -158,8 +158,8 @@ func (t RFC3339NanoTime) Add(d time.Duration) RFC3339NanoTime {
return RFC3339NanoTime(t.Time().Add(d)) return RFC3339NanoTime(t.Time().Add(d))
} }
func (t RFC3339NanoTime) Sub(u RFCTime) time.Duration { func (t RFC3339NanoTime) Sub(u AnyTime) time.Duration {
return t.Time().Sub(u.Time()) return t.Time().Sub(tt(u))
} }
func (t RFC3339NanoTime) AddDate(years int, months int, days int) RFC3339NanoTime { func (t RFC3339NanoTime) AddDate(years int, months int, days int) RFC3339NanoTime {
@@ -194,6 +194,10 @@ func (t RFC3339NanoTime) String() string {
return t.Time().String() return t.Time().String()
} }
func (t RFC3339NanoTime) Location() *time.Location {
return t.Time().Location()
}
func NewRFC3339Nano(t time.Time) RFC3339NanoTime { func NewRFC3339Nano(t time.Time) RFC3339NanoTime {
return RFC3339NanoTime(t) return RFC3339NanoTime(t)
} }

View File

@@ -63,16 +63,16 @@ func (t UnixTime) Serialize() string {
return strconv.FormatInt(t.Time().Unix(), 10) return strconv.FormatInt(t.Time().Unix(), 10)
} }
func (t UnixTime) After(u RFCTime) bool { func (t UnixTime) After(u AnyTime) bool {
return t.Time().After(u.Time()) return t.Time().After(tt(u))
} }
func (t UnixTime) Before(u RFCTime) bool { func (t UnixTime) Before(u AnyTime) bool {
return t.Time().Before(u.Time()) return t.Time().Before(tt(u))
} }
func (t UnixTime) Equal(u RFCTime) bool { func (t UnixTime) Equal(u AnyTime) bool {
return t.Time().Equal(u.Time()) return t.Time().Equal(tt(u))
} }
func (t UnixTime) IsZero() bool { func (t UnixTime) IsZero() bool {
@@ -131,8 +131,8 @@ func (t UnixTime) Add(d time.Duration) UnixTime {
return UnixTime(t.Time().Add(d)) return UnixTime(t.Time().Add(d))
} }
func (t UnixTime) Sub(u RFCTime) time.Duration { func (t UnixTime) Sub(u AnyTime) time.Duration {
return t.Time().Sub(u.Time()) return t.Time().Sub(tt(u))
} }
func (t UnixTime) AddDate(years int, months int, days int) UnixTime { func (t UnixTime) AddDate(years int, months int, days int) UnixTime {
@@ -167,6 +167,10 @@ func (t UnixTime) String() string {
return t.Time().String() return t.Time().String()
} }
func (t UnixTime) Location() *time.Location {
return t.Time().Location()
}
func NewUnix(t time.Time) UnixTime { func NewUnix(t time.Time) UnixTime {
return UnixTime(t) return UnixTime(t)
} }

View File

@@ -63,16 +63,16 @@ func (t UnixMilliTime) Serialize() string {
return strconv.FormatInt(t.Time().UnixMilli(), 10) return strconv.FormatInt(t.Time().UnixMilli(), 10)
} }
func (t UnixMilliTime) After(u RFCTime) bool { func (t UnixMilliTime) After(u AnyTime) bool {
return t.Time().After(u.Time()) return t.Time().After(tt(u))
} }
func (t UnixMilliTime) Before(u RFCTime) bool { func (t UnixMilliTime) Before(u AnyTime) bool {
return t.Time().Before(u.Time()) return t.Time().Before(tt(u))
} }
func (t UnixMilliTime) Equal(u RFCTime) bool { func (t UnixMilliTime) Equal(u AnyTime) bool {
return t.Time().Equal(u.Time()) return t.Time().Equal(tt(u))
} }
func (t UnixMilliTime) IsZero() bool { func (t UnixMilliTime) IsZero() bool {
@@ -131,8 +131,8 @@ func (t UnixMilliTime) Add(d time.Duration) UnixMilliTime {
return UnixMilliTime(t.Time().Add(d)) return UnixMilliTime(t.Time().Add(d))
} }
func (t UnixMilliTime) Sub(u RFCTime) time.Duration { func (t UnixMilliTime) Sub(u AnyTime) time.Duration {
return t.Time().Sub(u.Time()) return t.Time().Sub(tt(u))
} }
func (t UnixMilliTime) AddDate(years int, months int, days int) UnixMilliTime { func (t UnixMilliTime) AddDate(years int, months int, days int) UnixMilliTime {
@@ -167,6 +167,10 @@ func (t UnixMilliTime) String() string {
return t.Time().String() return t.Time().String()
} }
func (t UnixMilliTime) Location() *time.Location {
return t.Time().Location()
}
func NewUnixMilli(t time.Time) UnixMilliTime { func NewUnixMilli(t time.Time) UnixMilliTime {
return UnixMilliTime(t) return UnixMilliTime(t)
} }

View File

@@ -63,16 +63,16 @@ func (t UnixNanoTime) Serialize() string {
return strconv.FormatInt(t.Time().UnixNano(), 10) return strconv.FormatInt(t.Time().UnixNano(), 10)
} }
func (t UnixNanoTime) After(u RFCTime) bool { func (t UnixNanoTime) After(u AnyTime) bool {
return t.Time().After(u.Time()) return t.Time().After(tt(u))
} }
func (t UnixNanoTime) Before(u RFCTime) bool { func (t UnixNanoTime) Before(u AnyTime) bool {
return t.Time().Before(u.Time()) return t.Time().Before(tt(u))
} }
func (t UnixNanoTime) Equal(u RFCTime) bool { func (t UnixNanoTime) Equal(u AnyTime) bool {
return t.Time().Equal(u.Time()) return t.Time().Equal(tt(u))
} }
func (t UnixNanoTime) IsZero() bool { func (t UnixNanoTime) IsZero() bool {
@@ -131,8 +131,8 @@ func (t UnixNanoTime) Add(d time.Duration) UnixNanoTime {
return UnixNanoTime(t.Time().Add(d)) return UnixNanoTime(t.Time().Add(d))
} }
func (t UnixNanoTime) Sub(u RFCTime) time.Duration { func (t UnixNanoTime) Sub(u AnyTime) time.Duration {
return t.Time().Sub(u.Time()) return t.Time().Sub(tt(u))
} }
func (t UnixNanoTime) AddDate(years int, months int, days int) UnixNanoTime { func (t UnixNanoTime) AddDate(years int, months int, days int) UnixNanoTime {
@@ -167,6 +167,10 @@ func (t UnixNanoTime) String() string {
return t.Time().String() return t.Time().String()
} }
func (t UnixNanoTime) Location() *time.Location {
return t.Time().Location()
}
func NewUnixNano(t time.Time) UnixNanoTime { func NewUnixNano(t time.Time) UnixNanoTime {
return UnixNanoTime(t) return UnixNanoTime(t)
} }