commit 8e7a9e733d06131251b8555a56955d3ef931499a Author: kanade Date: Thu Jun 30 17:22:39 2022 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1c181e --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6158730 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 golangkit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.cn.md b/README.cn.md new file mode 100644 index 0000000..ebebc07 --- /dev/null +++ b/README.cn.md @@ -0,0 +1,45 @@ +[![](https://img.shields.io/github/license/golangkit/formatime)](https://img.shields.io/github/license/golangkit/formatime) +[![GoDoc](https://godoc.org/github.com/golangkit/formatime?status.svg)](https://godoc.org/github.com/golangkit/formatime) + +# formatime + +自定义golang时间的格式,能与gorm结合使用,无需再使用sql.nullTime,并能在struct的json序列化中返回指定的时间格式 + +## 安装 +```bash +go get github.com/golangkit/formatime +``` + +## 使用 +```go + +type foo struct { + CreatedAt formatime.Date `gorm:"column:created_at" json:"created_at"` + UpdatedAt formatime.Timestamp `gorm:"column:updated_at" json:"updated_at"` + DeletedAt formatime.CustomTime `gorm:"column:deleted_at" json:"deleted_at"` +} + +func Test_datetime(t *testing.T) { + b := foo{ + CreatedAt: formatime.NewDateNow(), + UpdatedAt: formatime.NewTimestampNow(), + DeletedAt: formatime.NewCustomTimeNow("Jan _2 15:04:05"), + } + + text, err := json.Marshal(&b) + if err != nil { + t.Fatal(err) + } else { + log.Print(string(text)) + } +} +``` + +```javascript +// log结果如下 +{ + "created_at":"2019-10-29", + "updated_at":"1572312916", + "deleted_at":"Oct 29 09:35:16" +} +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..479b144 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +[![](https://img.shields.io/github/license/golangkit/formatime)](https://img.shields.io/github/license/golangkit/formatime) +[![GoDoc](https://godoc.org/github.com/golangkit/formatime?status.svg)](https://godoc.org/github.com/golangkit/formatime) + +# formatime + +[中文](./README.cn.md) + +Customize the format of the time in golang, can be used in conjunction with the gorm, no need to use sql.nullTime, and can return the specified time format in the json serialization of the struct +## Installation +```bash +go get github.com/golangkit/formatime +``` + +## Usage +```go + +type foo struct { + ID int64 `gorm:"column:id"` + CreatedAt formatime.Date `gorm:"column:created_at" json:"created_at"` + UpdatedAt formatime.Timestamp `gorm:"column:updated_at" json:"updated_at"` + DeletedAt formatime.CustomTime `gorm:"column:deleted_at" json:"deleted_at"` +} + +func Test_json(t *testing.T) { + b := foo{ + CreatedAt: formatime.NewDateNow(), + UpdatedAt: formatime.NewTimestampNow(), + DeletedAt: formatime.NewCustomTimeNow("Jan _2 15:04:05"), + } + + text, err := json.Marshal(&b) + if err != nil { + t.Fatal(err) + } else { + log.Print(string(text)) + } +} + +func Test_gorm(t *testing.T) { + b := &foo{} + gorm.DB.First(b) + text, err := json.Marshal(&b) + if err != nil { + t.Fatal(err) + } else { + log.Print(string(text)) + } +} +``` + +```javascript +// log +{ + "created_at":"2019-10-29", + "updated_at":"1572312916", + "deleted_at":"Oct 29 09:35:16" +} +``` diff --git a/customize.go b/customize.go new file mode 100644 index 0000000..2cc9fc2 --- /dev/null +++ b/customize.go @@ -0,0 +1,73 @@ +package formatime + +import ( + "fmt" + "time" +) + +// CustomTime is a nullable time.Time. It supports SQL, JSON serialization AND custom format. +// It will marshal to null if null. +type CustomTime struct { + protocol + Format string +} + +// new custom format of time, s is timestamp +// examples: +// ANSIC = "Mon Jan _2 15:04:05 2006" +// UnixDate = "Mon Jan _2 15:04:05 MST 2006" +// RubyDate = "Mon Jan 02 15:04:05 -0700 2006" +// RFC822 = "02 Jan 06 15:04 MST" +// RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone +// RFC850 = "Monday, 02-Jan-06 15:04:05 MST" +// RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" +// RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone +// RFC3339 = "2006-01-02T15:04:05Z07:00" +// RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" +// Kitchen = "3:04PM" +// // Handy time stamps. +// Stamp = "Jan _2 15:04:05" +// StampMilli = "Jan _2 15:04:05.000" +// StampMicro = "Jan _2 15:04:05.000000" +// StampNano = "Jan _2 15:04:05.000000000" +func NewCustomTime(s int64, format string) CustomTime { + return CustomTime{ + protocol: protocol{ + Time: time.Unix(s, 0), + Valid: true, + }, + Format: format, + } +} + +// new custom format of time from time.Time +func NewCustomTimeFrom(t time.Time, format string) CustomTime { + return CustomTime{ + protocol: protocol{ + Time: t, + Valid: true, + }, + Format: format, + } +} + +// new custom format of time right now +func NewCustomTimeNow(format string) CustomTime { + return CustomTime{ + protocol: protocol{ + Time: time.Now(), + Valid: true, + }, + Format: format, + } +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this time is null. +func (t CustomTime) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + var stamp = fmt.Sprintf("\"%s\"", t.Time.Format(t.Format)) + return []byte(stamp), nil +} diff --git a/customize_test.go b/customize_test.go new file mode 100644 index 0000000..45c9957 --- /dev/null +++ b/customize_test.go @@ -0,0 +1,25 @@ +package formatime + +import ( + "encoding/json" + "log" + "testing" +) + +type joo struct { + Name string `json:"name"` + CreatedAt CustomTime `json:"created_at"` +} + +func Test_customize(t *testing.T) { + b := joo{ + Name: "Joo", + CreatedAt: NewCustomTimeNow("Jan _2 15:04:05.000000000"), + } + text, err := json.Marshal(&b) + if err != nil { + t.Fatal(err) + } else { + log.Print(string(text)) + } +} \ No newline at end of file diff --git a/date.go b/date.go new file mode 100644 index 0000000..31aed15 --- /dev/null +++ b/date.go @@ -0,0 +1,49 @@ +package formatime + + +import ( + "fmt" + "time" +) + +// Date is a nullable time.Time. It supports SQL, JSON serialization AND datetime format(2006-01-02). +// It will marshal to null if null. +type Date struct { + protocol +} + +const _DateFormat = "2006-01-02" + +// new Date format of time, s is timestamp +func NewDate(s int64) Date { + return Date{protocol{ + Time: time.Unix(s, 0), + Valid: true, + }} +} + +// new Date format of time right now +func NewDateNow() Date { + return Date{protocol{ + Time: time.Now(), + Valid: true, + }} +} + +// new Date format of time from time.Time +func NewDateFrom(t time.Time) Date { + return Date{protocol{ + Time: t, + Valid: true, + }} +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this time is null. +func (t Date) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + var stamp = fmt.Sprintf("\"%s\"", t.Time.Format(_DateFormat)) + return []byte(stamp), nil +} diff --git a/hour.go b/hour.go new file mode 100644 index 0000000..9903f9e --- /dev/null +++ b/hour.go @@ -0,0 +1,48 @@ +package formatime + +import ( + "fmt" + "time" +) + +// Hour is a nullable time.Time. It supports SQL, JSON serialization AND datetime format(2006-01-02 15). +// It will marshal to null if null. +type Hour struct { + protocol +} + +const _HourFormat = "2006-01-02 15" + +// new Hour format of time, s is timestamp +func NewHour(s int64) Hour { + return Hour{protocol{ + Time: time.Unix(s, 0), + Valid: true, + }} +} + +// new Hour format of time right now +func NewHourNow() Hour { + return Hour{protocol{ + Time: time.Now(), + Valid: true, + }} +} + +// new Hour format of time from time.Time +func NewHourFrom(t time.Time) Hour { + return Hour{protocol{ + Time: t, + Valid: true, + }} +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this time is null. +func (t Hour) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + var stamp = fmt.Sprintf("\"%s\"", t.Time.Format(_HourFormat)) + return []byte(stamp), nil +} diff --git a/minute.go b/minute.go new file mode 100644 index 0000000..f9e8d5c --- /dev/null +++ b/minute.go @@ -0,0 +1,48 @@ +package formatime + +import ( + "fmt" + "time" +) + +// Minute is a nullable time.Time. It supports SQL, JSON serialization AND datetime format(2006-01-02 15). +// It will marshal to null if null. +type Minute struct { + protocol +} + +const _MinuteFormat = "2006-01-02 15:04" + +// new Minute format of time, s is timestamp +func NewMinute(s int64) Minute { + return Minute{protocol{ + Time: time.Unix(s, 0), + Valid: true, + }} +} + +// new Minute format of time right now +func NewMinuteNow() Minute { + return Minute{protocol{ + Time: time.Now(), + Valid: true, + }} +} + +// new Minute format of time from time.Time +func NewMinuteFrom(t time.Time) Minute { + return Minute{protocol{ + Time: t, + Valid: true, + }} +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this time is null. +func (t Minute) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + var stamp = fmt.Sprintf("\"%s\"", t.Time.Format(_MinuteFormat)) + return []byte(stamp), nil +} diff --git a/second.go b/second.go new file mode 100644 index 0000000..821f614 --- /dev/null +++ b/second.go @@ -0,0 +1,49 @@ +package formatime + + +import ( + "fmt" + "time" +) + +// Second is a nullable time.Time. It supports SQL, JSON serialization AND datetime format(2006-01-02 15:04:05). +// It will marshal to null if null. +type Second struct { + protocol +} + +const _SecondFormat = "2006-01-02 15:04:05" + +// new Second format of time, s is timestamp +func NewSecond(s int64) Second { + return Second{protocol{ + Time: time.Unix(s, 0), + Valid: true, + }} +} + +// new Second format of time right now +func NewSecondNow() Second { + return Second{protocol{ + Time: time.Now(), + Valid: true, + }} +} + +// new Second format of time from time.Time +func NewSecondFrom(t time.Time) Second { + return Second{protocol{ + Time: t, + Valid: true, + }} +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this time is null. +func (t Second) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + var stamp = fmt.Sprintf("\"%s\"", t.Time.Format(_SecondFormat)) + return []byte(stamp), nil +} diff --git a/timestamp.go b/timestamp.go new file mode 100644 index 0000000..ea0439e --- /dev/null +++ b/timestamp.go @@ -0,0 +1,46 @@ +package formatime + +import ( + "fmt" + "time" +) + +// Timestamp is a nullable time.Time. It supports SQL, JSON serialization AND timestamp format. +// It will marshal to null if null. +type Timestamp struct { + protocol +} + +// new timestamp format of time +func NewTimestamp(s int64) Timestamp { + return Timestamp{protocol{ + Time: time.Unix(s, 0), + Valid: true, + }} +} + +// new timestamp format of time right now +func NewTimestampNow() Timestamp { + return Timestamp{protocol{ + Time: time.Now(), + Valid: true, + }} +} + +// new timestamp format of time from time.Time +func NewTimestampFrom(t time.Time) Timestamp { + return Timestamp{protocol{ + Time: t, + Valid: true, + }} +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this time is null. +func (t Timestamp) MarshalJSON() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + var stamp = fmt.Sprintf("\"%d\"", t.Time.Unix()) + return []byte(stamp), nil +} diff --git a/timestamp_test.go b/timestamp_test.go new file mode 100644 index 0000000..176d6ca --- /dev/null +++ b/timestamp_test.go @@ -0,0 +1,26 @@ +package formatime + +import ( + "encoding/json" + "log" + "testing" +) + +type boo struct { + Name string `json:"name"` + CreatedAt Timestamp `json:"created_at"` +} + +func Test_timestamp(t *testing.T) { + b := boo{ + Name: "Boo", + CreatedAt: NewTimestampNow(), + } + + jsonText, err := json.Marshal(&b) + if err != nil { + t.Fatal(err) + } else { + log.Print(string(jsonText)) + } +} diff --git a/type.go b/type.go new file mode 100644 index 0000000..c41b95d --- /dev/null +++ b/type.go @@ -0,0 +1,98 @@ +package formatime + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "reflect" + "time" +) + +// protocol is a nullable time.Time. It supports SQL and JSON serialization. +// It will marshal to null if null. +type protocol struct { + Time time.Time + Valid bool +} + +// Scan implements the Scanner interface. +func (t *protocol) Scan(value interface{}) error { + var err error + switch x := value.(type) { + case time.Time: + t.Time = x + case nil: + t.Valid = false + return nil + default: + err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value) + } + t.Valid = err == nil + return err +} + +// Value implements the driver Valuer interface. +func (t protocol) Value() (driver.Value, error) { + if !t.Valid { + return nil, nil + } + return t.Time, nil +} + +// IsZero returns true for invalid Times, hopefully for future omitempty support. +// A non-null Time with a zero value will not be considered zero. +func (t protocol) IsZero() bool { + return !t.Valid +} + +// UnmarshalJSON implements json.Unmarshaler. +// It supports string, object (e.g. pq.NullTime and friends) +// and null input. +func (t *protocol) UnmarshalJSON(data []byte) error { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { + return err + } + switch x := v.(type) { + case string: + err = t.Time.UnmarshalJSON(data) + case map[string]interface{}: + ti, tiOK := x["Time"].(string) + valid, validOK := x["Valid"].(bool) + if !tiOK || !validOK { + return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"]) + } + err = t.Time.UnmarshalText([]byte(ti)) + t.Valid = valid + return err + case nil: + t.Valid = false + return nil + default: + err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Time", reflect.TypeOf(v).Name()) + } + t.Valid = err == nil + return err +} + +// Marshal +func (t protocol) MarshalText() ([]byte, error) { + if !t.Valid { + return []byte("null"), nil + } + return t.Time.MarshalText() +} + +func (t *protocol) UnmarshalText(text []byte) error { + str := string(text) + if str == "" || str == "null" { + t.Valid = false + return nil + } + if err := t.Time.UnmarshalText(text); err != nil { + return err + } + t.Valid = true + return nil +}