feat: time util
This commit is contained in:
2
tag.bat
2
tag.bat
@@ -1,4 +1,4 @@
|
|||||||
git tag v1.1.23
|
git tag v1.1.24
|
||||||
|
|
||||||
git tag bank_card/v1.1.5
|
git tag bank_card/v1.1.5
|
||||||
git tag geoip/v1.1.5
|
git tag geoip/v1.1.5
|
||||||
|
|||||||
@@ -10,4 +10,52 @@ const (
|
|||||||
DefaultTimeLocationName = "Asia/Shanghai"
|
DefaultTimeLocationName = "Asia/Shanghai"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// More predefined layouts for use in Time.Format and time.Parse.
|
||||||
|
const (
|
||||||
|
DT14 = "20060102150405"
|
||||||
|
DT8 = "20060102"
|
||||||
|
DT8MDY = "01022006"
|
||||||
|
DT6 = "200601"
|
||||||
|
MonthDay = "1/2"
|
||||||
|
DIN5008FullDate = "02.01.2006" // German DIN 5008 standard
|
||||||
|
DIN5008Date = "02.01.06"
|
||||||
|
RFC3339FullDate = time.DateOnly
|
||||||
|
RFC3339Milli = "2006-01-02T15:04:05.999Z07:00"
|
||||||
|
RFC3339Dash = "2006-01-02T15-04-05Z07-00"
|
||||||
|
ISO8601 = "2006-01-02T15:04:05Z0700"
|
||||||
|
ISO8601TZHour = "2006-01-02T15:04:05Z07"
|
||||||
|
ISO8601NoTZ = "2006-01-02T15:04:05"
|
||||||
|
ISO8601MilliNoTZ = "2006-01-02T15:04:05.999"
|
||||||
|
ISO8601Milli = "2006-01-02T15:04:05.999Z0700"
|
||||||
|
ISO8601CompactZ = "20060102T150405Z0700"
|
||||||
|
ISO8601CompactNoTZ = "20060102T150405"
|
||||||
|
ISO8601YM = "2006-01"
|
||||||
|
ISO9075 = time.DateTime // ISO/IEC 9075 used by MySQL, BigQuery, etc.
|
||||||
|
ISO9075MicroTZ = "2006-01-02 15:04:05.999999-07" // ISO/IEC 9075 used by PostgreSQL
|
||||||
|
RFC5322 = "Mon, 2 Jan 2006 15:04:05 -0700" // RFC5322 = "Mon Jan 02 15:04:05 -0700 2006"
|
||||||
|
SQLTimestamp = ISO9075
|
||||||
|
SQLTimestampMinutes = "2006-01-02 15:04"
|
||||||
|
Ruby = "2006-01-02 15:04:05 -0700" // Ruby Time.now.to_s
|
||||||
|
InsightlyAPIQuery = "_1/_2/2006 _3:04:05 PM"
|
||||||
|
DateMDY = "1/2/2006" // an underscore results in a space.
|
||||||
|
DateMDYSlash = "01/02/2006"
|
||||||
|
DateDMYDash = "_2-01-2006" // Jira XML Date format
|
||||||
|
DateDMYHM2 = "02:01:06 15:04" // GMT time in format dd:mm:yy hh:mm
|
||||||
|
DateYMD = RFC3339FullDate
|
||||||
|
DateTextUS = "January 2, 2006"
|
||||||
|
DateTextUSAbbr3 = "Jan 2, 2006"
|
||||||
|
DateTextEU = "2 January 2006"
|
||||||
|
DateTextEUAbbr3 = "2 Jan 2006"
|
||||||
|
MonthAbbrYear = "Jan 2006"
|
||||||
|
MonthYear = "January 2006"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RFC3339Min = "0000-01-01T00:00:00Z"
|
||||||
|
RFC3339Max = "9999-12-31T23:59:59Z"
|
||||||
|
RFC3339Zero = "0001-01-01T00:00:00Z" // Golang zero value
|
||||||
|
RFC3339ZeroUnix = "1970-01-01T00:00:00Z"
|
||||||
|
RFC3339YMDZeroUnix = int64(-62135596800)
|
||||||
|
)
|
||||||
|
|
||||||
var ReferenceTimeValue time.Time = time.Date(2006, 1, 2, 15, 4, 5, 999999999, time.FixedZone("MST", -7*60*60))
|
var ReferenceTimeValue time.Time = time.Date(2006, 1, 2, 15, 4, 5, 999999999, time.FixedZone("MST", -7*60*60))
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package timeutil
|
package timeutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -43,3 +45,291 @@ func DurationHMS(d time.Duration) (int, int, int) {
|
|||||||
|
|
||||||
return int(h), int(m), int(s)
|
return int(h), int(m), int(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reformat a time string from one format to another
|
||||||
|
// Deprecated...
|
||||||
|
func FromTo(value, fromLayout, toLayout string) (string, error) {
|
||||||
|
t, err := time.Parse(fromLayout, strings.TrimSpace(value))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return t.Format(toLayout), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reformat a time string from one format to another
|
||||||
|
func FromTo2(fromLayout, toLayout, value string) (string, error) {
|
||||||
|
t, err := time.Parse(fromLayout, strings.TrimSpace(value))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return t.Format(toLayout), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromToFirstValueOrEmpty(fromLayout, toLayout string, values []string) string {
|
||||||
|
dtString, err := FromToFirstValue(fromLayout, toLayout, values)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return dtString
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromToFirstValue(fromLayout, toLayout string, values []string) (string, error) {
|
||||||
|
for _, val := range values {
|
||||||
|
dt, err := time.Parse(fromLayout, val)
|
||||||
|
if err == nil {
|
||||||
|
return dt.Format(toLayout), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("no match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFirstValueOrZero(layout string, values []string) time.Time {
|
||||||
|
dt, err := ParseFirstValue(layout, values)
|
||||||
|
if err != nil {
|
||||||
|
return TimeZeroRFC3339()
|
||||||
|
}
|
||||||
|
return dt
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFirstValue(layout string, values []string) (time.Time, error) {
|
||||||
|
for _, val := range values {
|
||||||
|
try, err := time.Parse(layout, val)
|
||||||
|
if err == nil {
|
||||||
|
return try, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numVals := len(values)
|
||||||
|
if numVals == 0 {
|
||||||
|
return time.Now(), errors.New("no time values supplied")
|
||||||
|
}
|
||||||
|
return time.Now(), fmt.Errorf("no valid string of [%v] supplied values", strconv.Itoa(numVals))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseOrZero returns a parsed time.Time or the RFC-3339 zero time.
|
||||||
|
func ParseOrZero(layout, value string) time.Time {
|
||||||
|
t, err := time.Parse(layout, value)
|
||||||
|
if err != nil {
|
||||||
|
return TimeZeroRFC3339()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFirst attempts to parse a string with a set of layouts.
|
||||||
|
func ParseFirst(layouts []string, value string) (time.Time, error) {
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
if len(value) == 0 || len(layouts) == 0 {
|
||||||
|
return time.Now(), fmt.Errorf(
|
||||||
|
"requires value [%v] and at least one layout [%v]", value, strings.Join(layouts, ","))
|
||||||
|
}
|
||||||
|
for _, layout := range layouts {
|
||||||
|
layout = strings.TrimSpace(layout)
|
||||||
|
if len(layout) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dt, err := time.Parse(layout, value); err == nil {
|
||||||
|
return dt, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Now(), fmt.Errorf("cannot parse time [%v] with layouts [%v]",
|
||||||
|
value, strings.Join(layouts, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
var FormatMap = map[string]string{
|
||||||
|
"RFC3339": time.RFC3339,
|
||||||
|
"RFC3339YMD": RFC3339FullDate,
|
||||||
|
"ISO8601YM": ISO8601YM,
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFormat(formatName string) (string, error) {
|
||||||
|
format, ok := FormatMap[strings.TrimSpace(formatName)]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("format Not Found: %v", format)
|
||||||
|
}
|
||||||
|
return format, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeMinRFC3339() time.Time {
|
||||||
|
t0, _ := time.Parse(time.RFC3339, RFC3339Min)
|
||||||
|
return t0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeZeroRFC3339() time.Time {
|
||||||
|
t0, _ := time.Parse(time.RFC3339, RFC3339Zero)
|
||||||
|
return t0
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeZeroUnix() time.Time {
|
||||||
|
t0, _ := time.Parse(time.RFC3339, RFC3339ZeroUnix)
|
||||||
|
return t0
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZeroAny(u time.Time) bool {
|
||||||
|
return u.Equal(TimeZeroRFC3339()) ||
|
||||||
|
u.Equal(TimeMinRFC3339()) ||
|
||||||
|
u.Equal(TimeZeroUnix())
|
||||||
|
}
|
||||||
|
|
||||||
|
type RFC3339YMDTime struct{ time.Time }
|
||||||
|
|
||||||
|
type ISO8601NoTzMilliTime struct{ time.Time }
|
||||||
|
|
||||||
|
func (t *RFC3339YMDTime) UnmarshalJSON(buf []byte) error {
|
||||||
|
tt, isNil, err := timeUnmarshalJSON(buf, RFC3339FullDate)
|
||||||
|
if err != nil || isNil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Time = tt
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t RFC3339YMDTime) MarshalJSON() ([]byte, error) {
|
||||||
|
return timeMarshalJSON(t.Time, RFC3339FullDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ISO8601NoTzMilliTime) UnmarshalJSON(buf []byte) error {
|
||||||
|
tt, isNil, err := timeUnmarshalJSON(buf, ISO8601MilliNoTZ)
|
||||||
|
if err != nil || isNil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Time = tt
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t ISO8601NoTzMilliTime) MarshalJSON() ([]byte, error) {
|
||||||
|
return timeMarshalJSON(t.Time, ISO8601MilliNoTZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeUnmarshalJSON(buf []byte, layout string) (time.Time, bool, error) {
|
||||||
|
str := string(buf)
|
||||||
|
isNil := true
|
||||||
|
if str == "null" || str == "\"\"" {
|
||||||
|
return time.Time{}, isNil, nil
|
||||||
|
}
|
||||||
|
tt, err := time.Parse(layout, strings.Trim(str, `"`))
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, false, err
|
||||||
|
}
|
||||||
|
return tt, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeMarshalJSON(t time.Time, layout string) ([]byte, error) {
|
||||||
|
return []byte(`"` + t.Format(layout) + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseSlice(layout string, strings []string) ([]time.Time, error) {
|
||||||
|
times := []time.Time{}
|
||||||
|
for _, raw := range strings {
|
||||||
|
t, err := time.Parse(layout, raw)
|
||||||
|
if err != nil {
|
||||||
|
return times, err
|
||||||
|
}
|
||||||
|
times = append(times, t)
|
||||||
|
}
|
||||||
|
return times, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatTimeMulti formats a `time.Time` object or
|
||||||
|
// an epoch number. It is adapted from `github.com/wcharczuk/go-chart`.
|
||||||
|
func FormatTimeMulti(dateFormat string, v any) string {
|
||||||
|
if typed, isTyped := v.(time.Time); isTyped {
|
||||||
|
return typed.Format(dateFormat)
|
||||||
|
}
|
||||||
|
if typed, isTyped := v.(int64); isTyped {
|
||||||
|
return time.Unix(0, typed).Format(dateFormat)
|
||||||
|
}
|
||||||
|
if typed, isTyped := v.(float64); isTyped {
|
||||||
|
return time.Unix(0, int64(typed)).Format(dateFormat)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatTimeToString(format string) func(time.Time) string {
|
||||||
|
return func(dt time.Time) string {
|
||||||
|
return dt.Format(format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OffsetFormat converts an integer offset value to a string
|
||||||
|
// value to use in string time formats. Note: RFC-3339 times
|
||||||
|
// use colons and the UTC "Z" offset.
|
||||||
|
func OffsetFormat(offset int, useColon, useZ bool) string {
|
||||||
|
offsetStr := "+0000"
|
||||||
|
if offset == 0 {
|
||||||
|
if useZ {
|
||||||
|
offsetStr = "Z"
|
||||||
|
} else if useColon {
|
||||||
|
offsetStr = "+00:00"
|
||||||
|
}
|
||||||
|
} else if offset > 0 {
|
||||||
|
if useColon {
|
||||||
|
hr := offset / 100
|
||||||
|
mn := offset - (hr * 100)
|
||||||
|
offsetStr = "+" + fmt.Sprintf("%02d:%02d", hr, mn)
|
||||||
|
} else {
|
||||||
|
offsetStr = "+" + fmt.Sprintf("%04d", offset)
|
||||||
|
}
|
||||||
|
} else if offset < 0 {
|
||||||
|
if useColon {
|
||||||
|
offsetPositive := -1 * offset
|
||||||
|
hr := offsetPositive / 100
|
||||||
|
mn := offsetPositive - (hr * 100)
|
||||||
|
offsetStr = "-" + fmt.Sprintf("%02d:%02d", hr, mn)
|
||||||
|
} else {
|
||||||
|
offsetStr = "-" + fmt.Sprintf("%04d", -1*offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offsetStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// var rxSQLTimestamp = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$`)
|
||||||
|
|
||||||
|
func ParseTimeUsingOffset(format, raw, sep string, offset int, useColon, useZ bool) (time.Time, error) {
|
||||||
|
return time.Parse(format, raw+sep+OffsetFormat(offset, useColon, useZ))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTimeSQLTimestampUsingOffset converts a SQL timestamp without timezone
|
||||||
|
// adding in a manual integer timezone.
|
||||||
|
func ParseTimeSQLTimestampUsingOffset(timeStr string, offset int) (time.Time, error) {
|
||||||
|
return ParseTimeUsingOffset(Ruby, timeStr, " ", offset, false, false)
|
||||||
|
/*
|
||||||
|
timeStr = strings.TrimSpace(timeStr)
|
||||||
|
if !rxSQLTimestamp.MatchString(timeStr) {
|
||||||
|
return time.Now(), fmt.Errorf("E_INVALID_SQL_TIMESTAMP [%v]", timeStr)
|
||||||
|
}
|
||||||
|
offsetStr := OffsetFormat(offset, useColon, useZ)
|
||||||
|
timeStr += " " + offsetStr
|
||||||
|
dt, err := time.Parse(Ruby, timeStr)
|
||||||
|
return dt, err
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LayoutNameDT4 = "dt4"
|
||||||
|
LayoutNameDT6 = "dt6"
|
||||||
|
LayoutNameDT8 = "dt8"
|
||||||
|
LayoutNameDT14 = "dt14"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsDTX returns the dtx format if conformant to various DTX values (dt4, dt6, dt8, dt14).
|
||||||
|
func IsDTX(d int) (string, error) {
|
||||||
|
switch len(strconv.Itoa(d)) {
|
||||||
|
case 4:
|
||||||
|
return LayoutNameDT4, nil
|
||||||
|
case 6:
|
||||||
|
m := d - ((d / 100) * 100)
|
||||||
|
if m < 1 || m > 12 {
|
||||||
|
return LayoutNameDT6, errors.New("dt6 length month value is out of bounds")
|
||||||
|
}
|
||||||
|
return LayoutNameDT6, nil
|
||||||
|
case 8:
|
||||||
|
dy := d - ((d / 100) * 100)
|
||||||
|
if dy < 1 || dy > 31 {
|
||||||
|
return LayoutNameDT6, errors.New("dt8 day value is out of bounds")
|
||||||
|
}
|
||||||
|
return LayoutNameDT8, nil
|
||||||
|
case 14:
|
||||||
|
return LayoutNameDT14, nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("length mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
132
timeutil/format_test.go
Normal file
132
timeutil/format_test.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package timeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dmyhm2ParseTests = []struct {
|
||||||
|
v string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"02:01:06 15:04", "2006-01-02T15:04:00Z"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDMYHM2ParseTime ensures timeutil.DateDMYHM2 is parsed to GMT timezone.
|
||||||
|
func TestDMYHM2ParseTime(t *testing.T) {
|
||||||
|
for _, tt := range dmyhm2ParseTests {
|
||||||
|
got, err := FromTo(tt.v, DateDMYHM2, time.RFC3339)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("time.Parse(DateDMYHM2) Error: with %v, want %v, err %v", tt.v, tt.want, err)
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("time.Parse(\"%v\", DateDMYHM2) Mismatch: want %v, got %v", tt.v, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rfc3339YMDTimeTests = []struct {
|
||||||
|
v string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{`{"MyTime":"2001-02-03"}`, `{"MyTime":"2001-02-03"}`},
|
||||||
|
{`{"MyTime":"0001-01-01"}`, `{"MyTime":"0001-01-01"}`},
|
||||||
|
{`{"MyTime":""}`, `{"MyTime":"0001-01-01"}`},
|
||||||
|
{`{}`, `{"MyTime":"0001-01-01"}`}}
|
||||||
|
|
||||||
|
type myStruct struct {
|
||||||
|
MyTime RFC3339YMDTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRfc3339YMDTime(t *testing.T) {
|
||||||
|
for _, tt := range rfc3339YMDTimeTests {
|
||||||
|
my := myStruct{}
|
||||||
|
//fmt.Println(tt.v)
|
||||||
|
err := json.Unmarshal([]byte(tt.v), &my)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Rfc3339YMDTime.Unmarshal: with %v, want %v, err %v", tt.v, tt.want, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(my)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Rfc3339YMDTime.Marshal: with %v, want %v, err %v", tt.v, tt.want, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := string(bytes)
|
||||||
|
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("Rfc3339YMDTime(%v): want %v, got %v", tt.v, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var offsetFormatTests = []struct {
|
||||||
|
input int
|
||||||
|
useColon bool
|
||||||
|
useZ bool
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{0, false, false, "+0000"},
|
||||||
|
{0, true, false, "+00:00"},
|
||||||
|
{0, true, true, "Z"},
|
||||||
|
{400, false, false, "+0400"},
|
||||||
|
{-400, false, false, "-0400"},
|
||||||
|
{530, false, false, "+0530"},
|
||||||
|
{-530, false, false, "-0530"},
|
||||||
|
{700, true, false, "+07:00"},
|
||||||
|
{-700, true, false, "-07:00"},
|
||||||
|
{845, true, false, "+08:45"},
|
||||||
|
{-845, true, false, "-08:45"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOffsetFormat(t *testing.T) {
|
||||||
|
for _, tt := range offsetFormatTests {
|
||||||
|
got := OffsetFormat(tt.input, tt.useColon, tt.useZ)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("time.OffsetFormat(\"%v\",%v,%v) Mismatch: want [%v], got [%v]", tt.input, tt.useColon, tt.useZ, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isDTXTests = []struct {
|
||||||
|
v int
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{2004, LayoutNameDT4},
|
||||||
|
{200401, LayoutNameDT6},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDTX(t *testing.T) {
|
||||||
|
for _, tt := range isDTXTests {
|
||||||
|
got, err := IsDTX(tt.v)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("time.IsDTX(%d) Error: want (%s), err (%v)", tt.v, tt.want, err)
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("time.IsDTX(%d) Mismatch: want (%s), got (%s)", tt.v, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatsTests = []struct {
|
||||||
|
v string
|
||||||
|
format string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"2023-06-18T00:00:00Z", DateTextUS, "June 18, 2023"},
|
||||||
|
{"2023-06-18T00:00:00Z", DateTextUSAbbr3, "Jun 18, 2023"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormats(t *testing.T) {
|
||||||
|
for _, tt := range formatsTests {
|
||||||
|
dt, err := time.Parse(time.RFC3339, tt.v)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("time.Parse(time.RFC3339, %s) Error: err (%s)", tt.v, err.Error())
|
||||||
|
}
|
||||||
|
got := dt.Format(tt.format)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("time.Format(%s) dt (%s) Mismatch: want (%s), got (%s)", tt.format, tt.v, tt.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,11 +26,17 @@ func GetDefaultTimeLocation() *time.Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnixMilliToStringPtr 毫秒时间戳 -> 字符串
|
// UnixMilliToStringPtr 毫秒时间戳 -> 字符串
|
||||||
func UnixMilliToStringPtr(tm *int64) *string {
|
func UnixMilliToStringPtr(milli *int64) *string {
|
||||||
if tm == nil {
|
if milli == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
str := time.UnixMilli(*tm).Format(TimeLayout)
|
|
||||||
|
tm := time.UnixMilli(*milli)
|
||||||
|
|
||||||
|
// 设置默认时区
|
||||||
|
tm.In(GetDefaultTimeLocation())
|
||||||
|
|
||||||
|
str := tm.Format(TimeLayout)
|
||||||
return &str
|
return &str
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,17 +50,18 @@ func StringToUnixMilliInt64Ptr(tm *string) *int64 {
|
|||||||
if theTime == nil {
|
if theTime == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unixTime := theTime.UnixMilli()
|
unixTime := theTime.UnixMilli()
|
||||||
return &unixTime
|
return &unixTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnixMilliToTimePtr 毫秒时间戳 -> 时间
|
// UnixMilliToTimePtr 毫秒时间戳 -> 时间
|
||||||
func UnixMilliToTimePtr(tm *int64) *time.Time {
|
func UnixMilliToTimePtr(milli *int64) *time.Time {
|
||||||
if tm == nil {
|
if milli == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unixMilli := time.UnixMilli(*tm)
|
unixMilli := time.UnixMilli(*milli)
|
||||||
return &unixMilli
|
return &unixMilli
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +76,13 @@ func TimeToUnixMilliInt64Ptr(tm *time.Time) *int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnixSecondToTimePtr 秒时间戳 -> 时间
|
// UnixSecondToTimePtr 秒时间戳 -> 时间
|
||||||
func UnixSecondToTimePtr(tm *int64) *time.Time {
|
func UnixSecondToTimePtr(second *int64) *time.Time {
|
||||||
if tm == nil {
|
if second == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unixMilli := time.Unix(*tm, 0)
|
unixMilli := time.Unix(*second, 0)
|
||||||
|
|
||||||
return &unixMilli
|
return &unixMilli
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,18 +108,19 @@ func StringTimeToTime(str *string) *time.Time {
|
|||||||
var err error
|
var err error
|
||||||
var theTime time.Time
|
var theTime time.Time
|
||||||
|
|
||||||
theTime, err = time.ParseInLocation(TimeLayout, *str, GetDefaultTimeLocation())
|
if theTime, err = time.ParseInLocation(TimeLayout, *str, GetDefaultTimeLocation()); err == nil {
|
||||||
if err == nil {
|
|
||||||
return &theTime
|
return &theTime
|
||||||
}
|
}
|
||||||
|
|
||||||
theTime, err = time.ParseInLocation(DateLayout, *str, GetDefaultTimeLocation())
|
if theTime, err = time.ParseInLocation(DateLayout, *str, GetDefaultTimeLocation()); err == nil {
|
||||||
if err == nil {
|
|
||||||
return &theTime
|
return &theTime
|
||||||
}
|
}
|
||||||
|
|
||||||
theTime, err = time.ParseInLocation(ClockLayout, *str, GetDefaultTimeLocation())
|
if theTime, err = time.ParseInLocation(ClockLayout, *str, GetDefaultTimeLocation()); err == nil {
|
||||||
if err == nil {
|
return &theTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if theTime, err = time.ParseInLocation(ISO9075MicroTZ, *str, GetDefaultTimeLocation()); err == nil {
|
||||||
return &theTime
|
return &theTime
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +132,10 @@ func TimeToTimeString(tm *time.Time) *string {
|
|||||||
if tm == nil {
|
if tm == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置默认时区
|
||||||
|
tm.In(GetDefaultTimeLocation())
|
||||||
|
|
||||||
return trans.String(tm.Format(TimeLayout))
|
return trans.String(tm.Format(TimeLayout))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,13 +174,17 @@ func TimeToDateString(tm *time.Time) *string {
|
|||||||
if tm == nil {
|
if tm == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置默认时区
|
||||||
|
tm.In(GetDefaultTimeLocation())
|
||||||
|
|
||||||
return trans.String(tm.Format(DateLayout))
|
return trans.String(tm.Format(DateLayout))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimestamppbToTime timestamppb.Timestamp -> time.Time
|
// TimestamppbToTime timestamppb.Timestamp -> time.Time
|
||||||
func TimestamppbToTime(tm *timestamppb.Timestamp) *time.Time {
|
func TimestamppbToTime(timestamp *timestamppb.Timestamp) *time.Time {
|
||||||
if tm != nil {
|
if timestamp != nil {
|
||||||
return trans.Ptr(tm.AsTime())
|
return trans.Ptr(timestamp.AsTime())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package timeutil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tx7do/go-utils/trans"
|
"github.com/tx7do/go-utils/trans"
|
||||||
|
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUnixMilliToStringPtr(t *testing.T) {
|
func TestUnixMilliToStringPtr(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user