diff --git a/pkg/datetime/conversion.go b/pkg/datetime/conversion.go new file mode 100644 index 0000000..fda8145 --- /dev/null +++ b/pkg/datetime/conversion.go @@ -0,0 +1,57 @@ +package datetime + +import "time" + +type theTime struct { + unix int64 +} + +// NewUnixNow 返回当前时间的unix时间戳 +func NewUnixNow() *theTime { + return &theTime{unix: time.Now().Unix()} +} + +// NewUnix 返回指定时间的unix时间戳 +func NewUnix(unix int64) *theTime { + return &theTime{unix: unix} +} + +// NewFormat 返回指定时间字符串的unix时间戳,t应该是 "yyyy-mm-dd hh:mm:ss" +func NewFormat(t string) (*theTime, error) { + timeLayout := "2006-01-02 15:04:05" + loc := time.FixedZone("CST", 8*3600) + tt, err := time.ParseInLocation(timeLayout, t, loc) + if err != nil { + return nil, err + } + return &theTime{unix: tt.Unix()}, nil +} + +// NewISO8601 返回指定的iso8601时间字符串的unix时间戳 +func NewISO8601(iso8601 string) (*theTime, error) { + t, err := time.ParseInLocation(time.RFC3339, iso8601, time.UTC) + if err != nil { + return nil, err + } + return &theTime{unix: t.Unix()}, nil +} + +// ToUnix 返回unix时间戳 +func (t *theTime) ToUnix() int64 { + return t.unix +} + +// ToFormat 返回unix时间的时间字符串'yyyy-mm-dd hh:mm:ss' +func (t *theTime) ToFormat() string { + return time.Unix(t.unix, 0).Format("2006-01-02 15:04:05") +} + +// ToFormatForTpl 返回指定格式的时间字符串 tpl +func (t *theTime) ToFormatForTpl(tpl string) string { + return time.Unix(t.unix, 0).Format(tpl) +} + +// ToIso8601 返回 iso8601 时间字符串 +func (t *theTime) ToIso8601() string { + return time.Unix(t.unix, 0).Format(time.RFC3339) +} diff --git a/pkg/datetime/conversion_test.go b/pkg/datetime/conversion_test.go new file mode 100644 index 0000000..8d14eea --- /dev/null +++ b/pkg/datetime/conversion_test.go @@ -0,0 +1,45 @@ +package datetime + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestToUnix(t *testing.T) { + tm1 := NewUnixNow() + unixTimestamp := tm1.ToUnix() + tm2 := NewUnix(unixTimestamp) + + require.Equal(t, tm1, tm2) +} + +func TestToFormat(t *testing.T) { + _, err := NewFormat("2022/03/18 17:04:05") + require.NotNil(t, err) + + tm, err := NewFormat("2022-03-18 17:04:05") + require.Nil(t, err) + + t.Log("ToFormat -> ", tm.ToFormat()) +} + +func TestToFormatForTpl(t *testing.T) { + _, err := NewFormat("2022/03/18 17:04:05") + require.NotNil(t, err) + + tm, err := NewFormat("2022-03-18 17:04:05") + require.Nil(t, err) + + t.Log("ToFormatForTpl -> ", tm.ToFormatForTpl("2006/01/02 15:04:05")) +} + +func TestToIso8601(t *testing.T) { + _, err := NewISO8601("2022-03-18 17:04:05") + require.NotNil(t, err) + + tm, err := NewISO8601("2006-01-02T15:04:05.999Z") + require.Nil(t, err) + + t.Log("ToIso8601 -> ", tm.ToIso8601()) +} diff --git a/pkg/datetime/datetime.go b/pkg/datetime/datetime.go new file mode 100644 index 0000000..328d27a --- /dev/null +++ b/pkg/datetime/datetime.go @@ -0,0 +1,179 @@ +// Package datetime 实现了一些格式化日期和时间的功能 +// Note: +// 1. `format` param in FormatTimeToStr function should be as flow: +//"yyyy-mm-dd hh:mm:ss" +//"yyyy-mm-dd hh:mm" +//"yyyy-mm-dd hh" +//"yyyy-mm-dd" +//"yyyy-mm" +//"mm-dd" +//"dd-mm-yy hh:mm:ss" +//"yyyy/mm/dd hh:mm:ss" +//"yyyy/mm/dd hh:mm" +//"yyyy/mm/dd hh" +//"yyyy/mm/dd" +//"yyyy/mm" +//"mm/dd" +//"dd/mm/yy hh:mm:ss" +//"yyyy" +//"mm" +//"hh:mm:ss" +//"mm:ss" +package datetime + +import ( + "fmt" + "time" +) + +var timeFormat map[string]string + +func init() { + timeFormat = map[string]string{ + "yyyy-mm-dd hh:mm:ss": "2006-01-02 15:04:05", + "yyyy-mm-dd hh:mm": "2006-01-02 15:04", + "yyyy-mm-dd hh": "2006-01-02 15:04", + "yyyy-mm-dd": "2006-01-02", + "yyyy-mm": "2006-01", + "mm-dd": "01-02", + "dd-mm-yy hh:mm:ss": "02-01-06 15:04:05", + "yyyy/mm/dd hh:mm:ss": "2006/01/02 15:04:05", + "yyyy/mm/dd hh:mm": "2006/01/02 15:04", + "yyyy/mm/dd hh": "2006/01/02 15", + "yyyy/mm/dd": "2006/01/02", + "yyyy/mm": "2006/01", + "mm/dd": "01/02", + "dd/mm/yy hh:mm:ss": "02/01/06 15:04:05", + "yyyy": "2006", + "mm": "01", + "hh:mm:ss": "15:04:05", + "mm:ss": "04:05", + } +} + +// AddMinute 加或减分钟的时间 +func AddMinute(t time.Time, minute int64) time.Time { + return t.Add(time.Minute * time.Duration(minute)) +} + +// AddHour 加或减小时的时间 +func AddHour(t time.Time, hour int64) time.Time { + return t.Add(time.Hour * time.Duration(hour)) +} + +// AddDay 加或减天的时间 +func AddDay(t time.Time, day int64) time.Time { + return t.Add(24 * time.Hour * time.Duration(day)) +} + +// GetNowDate 返回当前日期的格式yyy-mm-dd +func GetNowDate() string { + return time.Now().Format("2006-01-02") +} + +// GetNowTime 返回当前时间的格式 hh-mm-ss +func GetNowTime() string { + return time.Now().Format("15:04:05") +} + +// GetNowDateTime 返回当前日期时间的格式yyy-mm-dd hh-mm-ss +func GetNowDateTime() string { + return time.Now().Format("2006-01-02 15:04:05") +} + +// GetZeroHourTimestamp 返回零时的时间戳 (时间戳为00:00) +func GetZeroHourTimestamp() int64 { + ts := time.Now().Format("2006-01-02") + t, _ := time.Parse("2006-01-02", ts) + return t.UTC().Unix() - 8*3600 +} + +// GetNightTimestamp 返回零时的时间戳 (时间戳为23:59) +func GetNightTimestamp() int64 { + return GetZeroHourTimestamp() + 86400 - 1 +} + +// FormatTimeToStr 将时间转换为字符串 +func FormatTimeToStr(t time.Time, format string) string { + return t.Format(timeFormat[format]) +} + +// FormatStrToTime 将字符串转换为时间 +func FormatStrToTime(str, format string) (time.Time, error) { + v, ok := timeFormat[format] + if !ok { + return time.Time{}, fmt.Errorf("format %s not found", format) + } + + return time.Parse(v, str) +} + +// BeginOfMinute 返回起始分钟的时间 +func BeginOfMinute(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, t.Hour(), t.Minute(), 0, 0, t.Location()) +} + +// EndOfMinute 返回结束时的时间 +func EndOfMinute(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, t.Hour(), t.Minute(), 59, int(time.Second-time.Nanosecond), t.Location()) +} + +// BeginOfHour 返回开始时间 一天中的时间 +func BeginOfHour(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, t.Hour(), 0, 0, 0, t.Location()) +} + +// EndOfHour 返回结束时间 一天中的时间 +func EndOfHour(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, t.Hour(), 59, 59, int(time.Second-time.Nanosecond), t.Location()) +} + +// BeginOfDay 返回开始时间 一天中的时间 +func BeginOfDay(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) +} + +// EndOfDay 一天中的返回结束时间 +func EndOfDay(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location()) +} + +// BeginOfWeek 回归周,从周日开始的一周 +func BeginOfWeek(t time.Time) time.Time { + y, m, d := t.AddDate(0, 0, 0-int(BeginOfDay(t).Weekday())).Date() + return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) +} + +// EndOfWeek 周末返程时间,周末有周六 +func EndOfWeek(t time.Time) time.Time { + y, m, d := BeginOfWeek(t).AddDate(0, 0, 7).Add(-time.Nanosecond).Date() + return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), t.Location()) +} + +// BeginOfMonth 月初返回 +func BeginOfMonth(t time.Time) time.Time { + y, m, _ := t.Date() + return time.Date(y, m, 1, 0, 0, 0, 0, t.Location()) +} + +// EndOfMonth 月末返回 +func EndOfMonth(t time.Time) time.Time { + return BeginOfMonth(t).AddDate(0, 1, 0).Add(-time.Nanosecond) +} + +// BeginOfYear 年初回报 +func BeginOfYear(t time.Time) time.Time { + y, _, _ := t.Date() + return time.Date(y, time.January, 1, 0, 0, 0, 0, t.Location()) +} + +// EndOfYear 年终回报 +func EndOfYear(t time.Time) time.Time { + return BeginOfYear(t).AddDate(1, 0, 0).Add(-time.Nanosecond) +} diff --git a/pkg/datetime/datetime_test.go b/pkg/datetime/datetime_test.go new file mode 100644 index 0000000..c305d41 --- /dev/null +++ b/pkg/datetime/datetime_test.go @@ -0,0 +1,196 @@ +package datetime + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestAddDay(t *testing.T) { + now := time.Now() + after2Days := AddDay(now, 2) + diff1 := after2Days.Sub(now) + require.Equal(t, float64(48), diff1.Hours()) + + before2Days := AddDay(now, -2) + diff2 := before2Days.Sub(now) + require.Equal(t, float64(-48), diff2.Hours()) +} + +func TestAddHour(t *testing.T) { + now := time.Now() + after2Hours := AddHour(now, 2) + diff1 := after2Hours.Sub(now) + require.Equal(t, float64(2), diff1.Hours()) + + before2Hours := AddHour(now, -2) + diff2 := before2Hours.Sub(now) + require.Equal(t, float64(-2), diff2.Hours()) +} + +func TestAddMinute(t *testing.T) { + now := time.Now() + after2Minutes := AddMinute(now, 2) + diff1 := after2Minutes.Sub(now) + require.Equal(t, float64(2), diff1.Minutes()) + + before2Minutes := AddMinute(now, -2) + diff2 := before2Minutes.Sub(now) + require.Equal(t, float64(-2), diff2.Minutes()) +} + +func TestGetNowDate(t *testing.T) { + expected := time.Now().Format("2006-01-02") + require.Equal(t, expected, GetNowDate()) +} + +func TestGetNotTime(t *testing.T) { + expected := time.Now().Format("15:04:05") + require.Equal(t, expected, GetNowTime()) +} + +func TestGetNowDateTime(t *testing.T) { + expected := time.Now().Format("2006-01-02 15:04:05") + require.Equal(t, expected, GetNowDateTime()) +} + +func TestFormatTimeToStr(t *testing.T) { + datetime, _ := time.Parse("2006-01-02 15:04:05", "2021-01-02 16:04:08") + cases := []string{ + "yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd", + "dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss", + "hh:mm:ss", "yyyy/mm"} + + expected := []string{ + "2021-01-02 16:04:08", "2021-01-02", + "02-01-21 16:04:08", "2021/01/02 16:04:08", + "16:04:08", "2021/01"} + + for i := 0; i < len(cases); i++ { + actual := FormatTimeToStr(datetime, cases[i]) + require.Equal(t, expected[i], actual) + + } +} + +func TestFormatStrToTime(t *testing.T) { + formats := []string{ + "2006-01-02 15:04:05", "2006-01-02", + "02-01-06 15:04:05", "2006/01/02 15:04:05", + "2006/01"} + cases := []string{ + "yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd", + "dd-mm-yy hh:mm:ss", "yyyy/mm/dd hh:mm:ss", + "yyyy/mm"} + + datetimeStr := []string{ + "2021-01-02 16:04:08", "2021-01-02", + "02-01-21 16:04:08", "2021/01/02 16:04:08", + "2021/01"} + + for i := 0; i < len(cases); i++ { + actual, err := FormatStrToTime(datetimeStr[i], cases[i]) + if err != nil { + t.Fatal(err) + } + expected, _ := time.Parse(formats[i], datetimeStr[i]) + require.Equal(t, expected, actual) + } +} + +func TestBeginOfMinute(t *testing.T) { + expected := time.Date(2022, 2, 15, 15, 48, 0, 0, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := BeginOfMinute(td) + + require.Equal(t, expected, actual) +} + +func TestEndOfMinute(t *testing.T) { + expected := time.Date(2022, 2, 15, 15, 48, 59, 999999999, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := EndOfMinute(td) + + require.Equal(t, expected, actual) +} + +func TestBeginOfHour(t *testing.T) { + expected := time.Date(2022, 2, 15, 15, 0, 0, 0, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := BeginOfHour(td) + + require.Equal(t, expected, actual) +} + +func TestEndOfHour(t *testing.T) { + expected := time.Date(2022, 2, 15, 15, 59, 59, 999999999, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := EndOfHour(td) + + require.Equal(t, expected, actual) +} + +func TestBeginOfDay(t *testing.T) { + expected := time.Date(2022, 2, 15, 0, 0, 0, 0, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := BeginOfDay(td) + + require.Equal(t, expected, actual) +} + +func TestEndOfDay(t *testing.T) { + expected := time.Date(2022, 2, 15, 23, 59, 59, 999999999, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := EndOfDay(td) + + require.Equal(t, expected, actual) +} + +func TestBeginOfWeek(t *testing.T) { + expected := time.Date(2022, 2, 13, 0, 0, 0, 0, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := BeginOfWeek(td) + + require.Equal(t, expected, actual) +} + +func TestEndOfWeek(t *testing.T) { + expected := time.Date(2022, 2, 19, 23, 59, 59, 999999999, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := EndOfWeek(td) + + require.Equal(t, expected, actual) +} + +func TestBeginOfMonth(t *testing.T) { + expected := time.Date(2022, 2, 1, 0, 0, 0, 0, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := BeginOfMonth(td) + + require.Equal(t, expected, actual) +} + +func TestEndOfMonth(t *testing.T) { + expected := time.Date(2022, 2, 28, 23, 59, 59, 999999999, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := EndOfMonth(td) + + require.Equal(t, expected, actual) +} + +func TestBeginOfYear(t *testing.T) { + expected := time.Date(2022, 1, 1, 0, 0, 0, 0, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := BeginOfYear(td) + + require.Equal(t, expected, actual) +} + +func TestEndOfYear(t *testing.T) { + expected := time.Date(2022, 12, 31, 23, 59, 59, 999999999, time.Local) + td := time.Date(2022, 2, 15, 15, 48, 40, 112, time.Local) + actual := EndOfYear(td) + + require.Equal(t, expected, actual) +} diff --git a/pkg/dt/time.go b/pkg/dt/time.go deleted file mode 100644 index 7505b65..0000000 --- a/pkg/dt/time.go +++ /dev/null @@ -1,12 +0,0 @@ -package dt - -import "time" - -func GetTime(t string) (time.Time, error) { - now := time.Now() - d, err := time.ParseDuration(t) - if err != nil { - return now, err - } - return now.Add(d), nil -}