diff --git a/crypto/crypt/Config.go b/crypto/crypt/Config.go index 6cb714a..5c4636c 100644 --- a/crypto/crypt/Config.go +++ b/crypto/crypt/Config.go @@ -7,6 +7,7 @@ type Crypt interface { NewHash() hash.Hash Encrypt(data []byte, key []byte, iv []byte) (enData []byte, err error) Decrypt(enData []byte, key []byte, iv []byte) (data []byte, err error) + MakeSyEnc(key []byte, iv []byte) *SyEnc GenKey() (priKey []byte, pubKey []byte, err error) Sign(data []byte, priKey []byte) (signature []byte, err error) Verify(data []byte, signature []byte, pubKey []byte) bool @@ -14,6 +15,12 @@ type Crypt interface { DecryptE(enData []byte, priKey []byte) (data []byte, err error) } +type SyEnc struct { + crypt Crypt + key []byte + iv []byte +} + type CMCrypt struct { } diff --git a/crypto/crypt/Crypt.go b/crypto/crypt/Crypt.go index 1ede7f7..957a8f2 100644 --- a/crypto/crypt/Crypt.go +++ b/crypto/crypt/Crypt.go @@ -7,6 +7,34 @@ import ( "github.com/ssgo/u" ) +//var defaultKey = []byte{173, 233, 12, 146, 252, 29, 111, 183, 104, 232, 56, 134, 235, 232, 19, 168, 205, 26, 51, 189, 54, 125, 211, 233, 116, 173, 41, 154, 116, 215, 200, 2, 241, 61, 40, 230, 226, 62, 8, 220, 210, 72, 251, 232, 16, 198, 36, 2} +//var defaultIv = []byte{196, 189, 148, 57, 118, 84, 40, 128, 241, 143, 32, 82, 138, 182, 196, 190, 3, 141, 56, 253, 241, 159, 22, 206, 199, 146, 99, 75, 123, 167, 137, 195, 194, 188, 127, 204, 133, 227, 93, 248, 209, 100, 221, 192, 170, 248, 86, 104} +//var keyIsSet = false +// +//func SetSSKey(key, iv []byte) { +// if !keyIsSet { +// keyIsSet = true +// defaultKey = key +// defaultIv = iv +// } +//} + +//func (c *CMCrypt) DefaultEncrypt(data []byte) (enData []byte, err error) { +// return c.Encrypt(data, defaultKey, defaultIv) +//} +// +//func (c *CMCrypt) DefaultDecrypt(enData []byte) (data []byte, err error) { +// return c.Decrypt(enData, defaultKey, defaultIv) +//} +// +//func (c *GMCrypt) DefaultEncrypt(data []byte) (enData []byte, err error) { +// return c.Encrypt(data, defaultKey, defaultIv) +//} +// +//func (c *GMCrypt) DefaultDecrypt(enData []byte) (data []byte, err error) { +// return c.Encrypt(enData, defaultKey, defaultIv) +//} + func (c *CMCrypt) Encrypt(data []byte, key []byte, iv []byte) (enData []byte, err error) { return u.EncryptAesBytes(data, key, iv) } @@ -36,3 +64,27 @@ func (c *GMCrypt) Decrypt(enData []byte, key []byte, iv []byte) (data []byte, er } return data, err } + +func (c *CMCrypt) MakeSyEnc(key []byte, iv []byte) *SyEnc { + return &SyEnc{ + crypt: c, + key: key, + iv: iv, + } +} + +func (c *GMCrypt) MakeSyEnc(key []byte, iv []byte) *SyEnc { + return &SyEnc{ + crypt: c, + key: key, + iv: iv, + } +} + +func (c *SyEnc) Encrypt(data []byte) (enData []byte, err error) { + return c.Encrypt(data) +} + +func (c *SyEnc) Decrypt(enData []byte) (data []byte, err error) { + return c.Decrypt(enData) +} diff --git a/crypto/crypto.go b/crypto/crypto.go index 2122730..3feca83 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -9,15 +9,19 @@ import ( "crypto/sha512" "encoding/base64" "encoding/hex" + "fmt" "github.com/ZZMarquis/gm/sm3" + "github.com/ssgo/u" "hash" "sync" ) var gmCrypto = &crypt.GMCrypt{} var cmCrypto = &crypt.CMCrypt{} -var defaultCrypto crypt.Crypt +var defaultCrypto crypt.Crypt = cmCrypto var defaultCryptoLock = sync.RWMutex{} +var configKeys = map[string][][]byte{} +var configKeysLock = sync.RWMutex{} func init() { plugin.Register(plugin.Plugin{ @@ -33,6 +37,37 @@ func init() { } defaultCryptoLock.Unlock() }, Objects: map[string]interface{}{ + "getConfigEnc": func(name string, ctx *plugin.Context) *AesWithKey { + if u.Bool(ctx.GetData("_gotConfigEnc" + name)) { + return nil + } + ctx.SetData("_gotConfigEnc"+name, true) + configKeysLock.RLock() + configKeyIv := configKeys[name] + configKeysLock.RUnlock() + if configKeyIv != nil && len(configKeyIv) == 2 { + return &AesWithKey{ + crypto: defaultCrypto, + key: configKeyIv[0], + iv: configKeyIv[1], + } + } + return nil + }, + "setConfigEnc": func(name string, key, iv []byte) { + if name != "" && key != nil && iv != nil { + configKeysLock.Lock() + configKeys[name] = [][]byte{key, iv} + configKeysLock.Unlock() + } + }, + "removeConfigEnc": func(name string) { + if name != "" { + configKeysLock.Lock() + delete(configKeys, name) + configKeysLock.Unlock() + } + }, // base64 Base64编码 "base64": map[string]interface{}{ // encode 将字符串编码 @@ -209,6 +244,14 @@ func init() { }) } +func SetConfigEnc(name string, key, iv []byte) { + if name != "" && key != nil && iv != nil { + configKeysLock.Lock() + configKeys[name] = [][]byte{key, iv} + configKeysLock.Unlock() + } +} + type Hash struct { hash hash.Hash } @@ -309,6 +352,45 @@ func (ae *Aes) DecryptBytes(data, key, iv []byte) ([]byte, error) { return ae.crypto.Decrypt(data, key, iv) } +type AesWithKey struct { + crypto crypt.Crypt + key []byte + iv []byte +} + +// Encrypt 将字符串加密 +// Encrypt data 用于加密的数据,string类型 +// Encrypt return 返回hex编码的加密结果 +func (ae *AesWithKey) Encrypt(data string) (string, error) { + fmt.Println(ae.crypto.Encrypt([]byte(data), ae.key, ae.iv)) + return makeHexStringResult(ae.crypto.Encrypt([]byte(data), ae.key, ae.iv)) +} + +// EncryptBytes 将二进制数据加密 +// EncryptBytes data 用于加密的数据,二进制的字节数组 +// EncryptBytes return 返回二进制的加密结果 +func (ae *AesWithKey) EncryptBytes(data []byte) ([]byte, error) { + return ae.crypto.Encrypt(data, ae.key, ae.iv) +} + +// Decrypt 解密为字符串 +// Decrypt data hex编码的加密后结果 +// Decrypt return 解密后的数据,string类型 +func (ae *AesWithKey) Decrypt(data string) (string, error) { + if dataD, err3 := hex.DecodeString(data); err3 == nil { + return makeStringResult(ae.crypto.Decrypt(dataD, ae.key, ae.iv)) + } else { + return "", err3 + } +} + +// DecryptBytes 解密为二进制数据 +// DecryptBytes data 二进制的加密后结果 +// DecryptBytes return 解密后的数据,二进制的字节数组 +func (ae *AesWithKey) DecryptBytes(data []byte) ([]byte, error) { + return ae.crypto.Decrypt(data, ae.key, ae.iv) +} + type Ecdsa struct { crypto crypt.Crypt } diff --git a/db/MakeTable.go b/db/MakeTable.go index e44d8c1..11a6d2e 100644 --- a/db/MakeTable.go +++ b/db/MakeTable.go @@ -503,7 +503,7 @@ func MakeTable(conn *db.DB, table *TableStruct, ctx *plugin.Context) ([]string, for oldFieldName := range oldFields { if newFieldExists[oldFieldName] != true { if conn.Config.Type == "sqlite3" { - //actions = append(actions, "ALTER TABLE `"+table.Name+"` DROP COLUMN `"+oldFieldName+"`") + actions = append(actions, "ALTER TABLE `"+table.Name+"` DROP COLUMN `"+oldFieldName+"`") } else if conn.Config.Type == "mysql" { actions = append(actions, "DROP COLUMN `"+oldFieldName+"`") } diff --git a/go.mod b/go.mod index 515ae12..ff729dd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apigo.cc/apigo/plugins -go 1.17 +go 1.18 require ( apigo.cc/apigo/plugin v1.0.2 @@ -8,11 +8,11 @@ require ( github.com/emmansun/gmsm v0.21.1 github.com/gorilla/websocket v1.5.1 github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9 - github.com/ssgo/db v1.7.5 - github.com/ssgo/httpclient v1.7.5 - github.com/ssgo/log v1.7.5 - github.com/ssgo/redis v1.7.5 - github.com/ssgo/u v1.7.5 + github.com/ssgo/db v1.7.6 + github.com/ssgo/httpclient v1.7.6 + github.com/ssgo/log v1.7.6 + github.com/ssgo/redis v1.7.6 + github.com/ssgo/u v1.7.6 github.com/tdewolff/parse/v2 v2.7.12 gopkg.in/yaml.v3 v3.0.1 ) @@ -21,8 +21,8 @@ require ( github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gomodule/redigo v1.8.8 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/ssgo/config v1.7.5 // indirect - github.com/ssgo/standard v1.7.5 // indirect + github.com/ssgo/config v1.7.6 // indirect + github.com/ssgo/standard v1.7.6 // indirect github.com/stretchr/testify v1.8.1 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/runtime/runtime.go b/runtime/runtime.go index e73af31..d7f618c 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -31,8 +31,13 @@ func init() { // shell command 命令 // shell args 参数 // shell return 运行结果 - "shell": func(command string, args ...string) ([]string, error) { - return u.RunCommand(command, args...) + "shell": func(command string, callback func(string), args ...string) ([]string, error) { + for i := 1; i <= 10; i++ { + callback(u.String(i)) + time.Sleep(time.Millisecond * 200) + } + return nil, nil + //return u.RunCommand(command, args...) }, }, }) diff --git a/tests/go.mod b/tests/go.mod index 34da633..b560ff1 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -3,17 +3,17 @@ module tests go 1.17 require ( - apigo.cc/apigo/gojs v0.0.9 + apigo.cc/apigo/gojs v0.0.10 apigo.cc/apigo/plugin v1.0.2 apigo.cc/apigo/plugins v0.0.0 github.com/mattn/go-sqlite3 v1.14.18 - github.com/ssgo/s v1.7.5 - github.com/ssgo/u v1.7.5 + github.com/ssgo/s v1.7.6 + github.com/ssgo/u v1.7.6 github.com/tidwall/redcon v1.6.2 ) require ( - apigo.cc/apigo/qjs v0.0.3 // indirect + apigo.cc/apigo/quickjs-go v0.4.12 // indirect github.com/ZZMarquis/gm v1.3.2 // indirect github.com/emmansun/gmsm v0.21.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -25,13 +25,13 @@ require ( github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/shirou/gopsutil/v3 v3.22.10 // indirect - github.com/ssgo/config v1.7.5 // indirect - github.com/ssgo/db v1.7.5 // indirect - github.com/ssgo/discover v1.7.5 // indirect - github.com/ssgo/httpclient v1.7.5 // indirect - github.com/ssgo/log v1.7.5 // indirect - github.com/ssgo/redis v1.7.5 // indirect - github.com/ssgo/standard v1.7.5 // indirect + github.com/ssgo/config v1.7.6 // indirect + github.com/ssgo/db v1.7.6 // indirect + github.com/ssgo/discover v1.7.6 // indirect + github.com/ssgo/httpclient v1.7.6 // indirect + github.com/ssgo/log v1.7.6 // indirect + github.com/ssgo/redis v1.7.6 // indirect + github.com/ssgo/standard v1.7.6 // indirect github.com/tdewolff/parse/v2 v2.7.12 // indirect github.com/tidwall/btree v1.1.0 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/util/util.go b/util/util.go index 68ac32b..b0f34aa 100644 --- a/util/util.go +++ b/util/util.go @@ -5,14 +5,15 @@ import ( "encoding/hex" "github.com/ssgo/u" "gopkg.in/yaml.v3" + "time" ) func init() { plugin.Register(plugin.Plugin{ - Id: "util", + Id: "apigo.cc/apigo/plugins/util", Name: "基础工具", JsCode: ` -util.formatDate = function (date, fmt, timezone) { +${OBJECT}.formatDate = function (date, fmt) { let ret if (!date) return '' if (!fmt) fmt = 'YYYY-mm-dd HH:MM:SS' @@ -37,11 +38,11 @@ util.formatDate = function (date, fmt, timezone) { return fmt } -util.parseDate = function (str) { +${OBJECT}.parseDate = function (str) { if (typeof str === 'number') str = str + '' if (/^\d+[\\.]?\d*$/.test(str)) { if (str.length === 10) str += '000' - return new Date(util.int(str)) + return new Date(${OBJECT}.int(str)) } if (str.length === 19) { return new Date(parseInt(str.substr(0, 4)), parseInt(str.substr(5, 2)) - 1, parseInt(str.substr(8, 2)), parseInt(str.substr(11, 2)), parseInt(str.substr(14, 2)), parseInt(str.substr(17, 2))) @@ -59,7 +60,7 @@ util.parseDate = function (str) { return new Date(str) } -util.int = function (v) { +${OBJECT}.int = function (v) { if (!v) return 0 if (typeof v === 'number' || v instanceof Number) return v if (typeof v === 'object') v = v.toString ? v.toString() : '' @@ -71,7 +72,7 @@ util.int = function (v) { } } -util.float = function (v) { +${OBJECT}.float = function (v) { if (!v) return 0.0 if (typeof v === 'number' || v instanceof Number) return v if (typeof v === 'object') v = v.toString ? v.toString() : '' @@ -83,14 +84,14 @@ util.float = function (v) { } } -util.str = function (v) { +${OBJECT}.str = function (v) { if (!v) return '' if (typeof v === 'string') return v if (v.toString) return v.toString() return '' } -util.keysBy = function (obj, ...fieldAndValues) { +${OBJECT}.keysBy = function (obj, ...fieldAndValues) { let keys = [] for (let k in obj) { let match = true @@ -115,9 +116,9 @@ util.keysBy = function (obj, ...fieldAndValues) { return keys } -util.listBy = function (obj, ...fieldAndValues) { +${OBJECT}.listBy = function (obj, ...fieldAndValues) { let list = obj instanceof Array || obj instanceof NodeList ? [] : {} - let keys = util.keysBy(obj, ...fieldAndValues) + let keys = ${OBJECT}.keysBy(obj, ...fieldAndValues) for (let k of keys) { if (obj instanceof Array || obj instanceof NodeList) { list.push(obj[k]) @@ -128,32 +129,32 @@ util.listBy = function (obj, ...fieldAndValues) { return list } -util.hasBy = function (obj, ...fieldAndValues) { - let keys = util.keysBy(obj, ...fieldAndValues) +${OBJECT}.hasBy = function (obj, ...fieldAndValues) { + let keys = ${OBJECT}.keysBy(obj, ...fieldAndValues) return keys.length > 0 } -util.getBy = function (obj, ...fieldAndValues) { - let keys = util.keysBy(obj, ...fieldAndValues) +${OBJECT}.getBy = function (obj, ...fieldAndValues) { + let keys = ${OBJECT}.keysBy(obj, ...fieldAndValues) if (keys.length > 0) return obj[keys[0]] return null } -util.setBy = function (obj, value, ...fieldAndValues) { - let keys = util.keysBy(obj, ...fieldAndValues) +${OBJECT}.setBy = function (obj, value, ...fieldAndValues) { + let keys = ${OBJECT}.keysBy(obj, ...fieldAndValues) if (keys.length > 0) obj[keys[0]] = value } -util.indexBy = function (obj, ...fieldAndValues) { - let keys = util.keysBy(obj, ...fieldAndValues) +${OBJECT}.indexBy = function (obj, ...fieldAndValues) { + let keys = ${OBJECT}.keysBy(obj, ...fieldAndValues) if (keys.length > 0) { - return obj instanceof Array || obj instanceof NodeList ? util.int(keys[0]) : keys[0] + return obj instanceof Array || obj instanceof NodeList ? ${OBJECT}.int(keys[0]) : keys[0] } return -1 } -util.removeBy = function (obj, ...fieldAndValues) { - let keys = util.keysBy(obj, ...fieldAndValues) +${OBJECT}.removeBy = function (obj, ...fieldAndValues) { + let keys = ${OBJECT}.keysBy(obj, ...fieldAndValues) let n = 0 for (let i = keys.length - 1; i >= 0; i--) { let k = keys[i] @@ -167,19 +168,19 @@ util.removeBy = function (obj, ...fieldAndValues) { return n } -util.removeArrayItem = function (list, item) { +${OBJECT}.removeArrayItem = function (list, item) { let pos = list.indexOf(item) if (pos !== -1) list.splice(pos, 1) } -util.last = function (arr) { +${OBJECT}.last = function (arr) { if (arr && arr.length) { return arr[arr.length - 1] } return null } -util.len = function (obj) { +${OBJECT}.len = function (obj) { if (obj instanceof Array || obj instanceof NodeList) { return obj.length } else { @@ -189,14 +190,14 @@ util.len = function (obj) { } } -util.mergeBy = function (olds, news, ...fields) { +${OBJECT}.mergeBy = function (olds, news, ...fields) { if (!olds) return news for (let newItem of news) { let fieldAndValues = [] for (let field of fields) { fieldAndValues.push(field, newItem[field]) } - let oldIndex = util.indexBy(olds, ...fieldAndValues) + let oldIndex = ${OBJECT}.indexBy(olds, ...fieldAndValues) if (oldIndex === -1) { olds.push(newItem) } else { @@ -206,7 +207,7 @@ util.mergeBy = function (olds, news, ...fields) { return olds } -util.sortBy = function (obj, field, isReverse = false, sortType) { +${OBJECT}.sortBy = function (obj, field, isReverse = false, sortType) { let list = obj instanceof Array || obj instanceof NodeList ? [] : {} let sortedKeys = {} let sortArr = [] @@ -224,11 +225,11 @@ util.sortBy = function (obj, field, isReverse = false, sortType) { } sortArr.sort((a, b) => { if(sortType === 'int'){ - a = util.int(a) - b = util.int(b) + a = ${OBJECT}.int(a) + b = ${OBJECT}.int(b) } else if(sortType === 'float'){ - a = util.float(a) - b = util.float(b) + a = ${OBJECT}.float(a) + b = ${OBJECT}.float(b) } if (a == b) return 0 if (typeof a === 'number' && typeof b === 'number') { @@ -256,44 +257,44 @@ util.sortBy = function (obj, field, isReverse = false, sortType) { return list } -util.in = function (v1, v2) { - if (!(v1 instanceof Array)) v1 = util.split(v1, /,\s*/) +${OBJECT}.in = function (v1, v2) { + if (!(v1 instanceof Array)) v1 = ${OBJECT}.split(v1, /,\s*/) return v1.indexOf(String(v2)) !== -1 } -util.uniquePush = function (arr, ...values) { +${OBJECT}.uniquePush = function (arr, ...values) { for (let v of values) { if (arr.indexOf(v) === -1) arr.push(v) } } -util.clearEmpty = function (arr) { +${OBJECT}.clearEmpty = function (arr) { let a = [] for (let v of arr) if (v) a.push(v) return a } -util.split = function (v1, v2) { - return util.clearEmpty(util.str(v1).split(v2)) +${OBJECT}.split = function (v1, v2) { + return ${OBJECT}.clearEmpty(${OBJECT}.str(v1).split(v2)) } -util.join = function (arr, separator) { - return util.clearEmpty(arr).join(separator) +${OBJECT}.join = function (arr, separator) { + return ${OBJECT}.clearEmpty(arr).join(separator) } -util.copy = function (obj, isDeepCopy) { +${OBJECT}.copy = function (obj, isDeepCopy) { let newObj if (obj instanceof Array || obj instanceof NodeList) { newObj = [] for (let o of obj) { - if (isDeepCopy && typeof o === 'object' && o) o = util.copy(o) + if (isDeepCopy && typeof o === 'object' && o) o = ${OBJECT}.copy(o) newObj.push(o) } } else { newObj = {} for (let k in obj) { let v = obj[k] - if (isDeepCopy && typeof v === 'object' && v) v = util.copy(v) + if (isDeepCopy && typeof v === 'object' && v) v = ${OBJECT}.copy(v) newObj[k] = v } } @@ -351,6 +352,159 @@ util.copy = function (obj, isDeepCopy) { err := yaml.Unmarshal([]byte(content), &data) return data, err }, + + "now": func() *Time { + return &Time{tm: time.Now()} + }, + + "makeTime": func(year, month, day, hour, min, sec, msec int, location *string) *Time { + loc := time.Local + if location != nil { + if loc2, err := time.LoadLocation(*location); err == nil { + loc = loc2 + } + } + return makeTime(time.Date(year, time.Month(month), day, hour, min, sec, msec*1000000, loc)) + }, + + "unixTime": func(milliUnix int64) *Time { + return makeTime(time.UnixMilli(milliUnix)) + }, + "int": func(v any) int { return 0 }, + "float": func(v any) float64 { return 0 }, + "str": func(v any) string { return "" }, + "keysBy": func(obj []any, fieldAndValues ...any) []string { return nil }, + "listBy": func(obj []any, fieldAndValues ...any) []any { return nil }, + "hasBy": func(obj []any, fieldAndValues ...any) bool { return false }, + "getBy": func(obj []any, fieldAndValues ...any) any { return nil }, + "setBy": func(obj []any, value any, fieldAndValues ...any) {}, + "indexBy": func(obj []any, fieldAndValues ...any) int { return 0 }, + "removeBy": func(obj []any, fieldAndValues ...any) int { return 0 }, + "removeArrayItem": func(list []any, item any) {}, + "last": func(arr []any) any { return nil }, + "len": func(obj any) int { return 0 }, + "mergeBy": func(olds []any, news []any, fields ...string) []any { return nil }, + "sortBy": func(arr []any, field string, isReverse *bool, sortType *string) []any { return nil }, + "in": func(arr []any, v any) bool { return false }, + "uniquePush": func(arr []any, values ...any) {}, + "clearEmpty": func(arr []any) []any { return nil }, + "split": func(str string, separator string) []string { return nil }, + "join": func(arr []any, separator string) string { return "" }, + "copy": func(obj any, isDeepCopy bool) any { return nil }, }, }) } + +func makeTime(tm time.Time) *Time { + return &Time{tm: tm} +} + +type Time struct { + tm time.Time +} + +func (t *Time) After(u *Time) bool { + return t.tm.After(u.tm) +} + +func (t *Time) Before(u *Time) bool { + return t.tm.Before(u.tm) +} + +func (t *Time) Compare(u *Time) int { + return t.tm.Compare(u.tm) +} + +func (t *Time) Equal(u *Time) bool { + return t.tm.Equal(u.tm) +} + +func (t *Time) Year() int { + return t.tm.Year() +} + +func (t *Time) Month() int { + return int(t.tm.Month()) +} + +func (t *Time) Day() int { + return t.tm.Day() +} + +func (t *Time) Weekday() int { + return int(t.tm.Weekday()) +} + +func (t *Time) ISOWeek() (year, week int) { + return t.tm.ISOWeek() +} + +func (t *Time) Hour() int { + return t.tm.Hour() +} + +func (t *Time) Minute() int { + return t.tm.Minute() +} + +func (t *Time) Second() int { + return t.tm.Second() +} + +func (t *Time) Millisecond() int { + return t.tm.Nanosecond() / 1000000 +} + +func (t *Time) YearDay() int { + return t.tm.YearDay() +} + +func (t *Time) Add(msec int) *Time { + return makeTime(t.tm.Add(time.Duration(msec) * time.Millisecond)) +} + +func (t *Time) Sub(u *Time) int { + return int(t.tm.Sub(u.tm) / time.Millisecond) +} + +func (t *Time) AddDate(years int, months int, days int) *Time { + return makeTime(t.tm.AddDate(years, months, days)) +} + +func (t *Time) UTC() *Time { + return makeTime(t.tm.UTC()) +} + +func (t *Time) Local() *Time { + return makeTime(t.tm.Local()) +} + +func (t *Time) Location() string { + return t.tm.Location().String() +} + +func (t *Time) Zone() string { + name, _ := t.tm.Zone() + return name +} + +func (t *Time) ZoneOffset() int { + _, offset := t.tm.Zone() + return offset +} + +func (t *Time) Unix() int64 { + return t.tm.UnixMilli() +} + +func (t *Time) Format(fmt *string) string { + if fmt == nil || *fmt == "" { + fmt2 := "2006-01-02 15:04:05" + fmt = &fmt2 + } + return t.tm.Format(*fmt) +} + +func (t *Time) String() string { + return t.Format(nil) +}