From d9bd3153eaced2f9e387e725d5c07f30212bcefa Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 3 Jan 2021 23:37:45 +0800 Subject: [PATCH] improve time parsing for package gtime --- os/gtime/gtime.go | 45 +++++++++++++++++++++------- os/gtime/gtime_time.go | 18 ++++++++++- os/gtime/gtime_z_bench_test.go | 15 +++++++++- os/gtime/gtime_z_unit_basic_test.go | 32 +++++++++++++------- util/gconv/gconv_z_unit_time_test.go | 13 +++++--- 5 files changed, 97 insertions(+), 26 deletions(-) diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 500b39761..3623fd602 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -45,7 +45,7 @@ const ( // "2018/10/31 - 16:38:46" // "2018-02-09", // "2018.02.09", - TIME_REAGEX_PATTERN1 = `(\d{4}[-/\.]\d{2}[-/\.]\d{2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + timeRegexPattern1 = `(\d{4}[-/\.]\d{2}[-/\.]\d{2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` // Regular expression2(datetime separator supports '-', '/', '.'). // Eg: @@ -53,14 +53,21 @@ const ( // 01/Nov/2018 11:50:28 // 01.Nov.2018 11:50:28 // 01.Nov.2018:11:50:28 - TIME_REAGEX_PATTERN2 = `(\d{1,2}[-/\.][A-Za-z]{3,}[-/\.]\d{4})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + timeRegexPattern2 = `(\d{1,2}[-/\.][A-Za-z]{3,}[-/\.]\d{4})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + + // Regular expression3(time). + // Eg: + // 11:50:28 + // 11:50:28.897 + timeRegexPattern3 = `(\d{2}):(\d{2}):(\d{2})\.{0,1}(\d{0,9})` ) var ( // It's more high performance using regular expression // than time.ParseInLocation to parse the datetime string. - timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) - timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) + timeRegex1, _ = regexp.Compile(timeRegexPattern1) + timeRegex2, _ = regexp.Compile(timeRegexPattern2) + timeRegex3, _ = regexp.Compile(timeRegexPattern3) // Month words to arabic numerals mapping. monthMap = map[string]int{ @@ -247,15 +254,31 @@ func StrToTime(str string, format ...string) (*Time, error) { local = time.Local ) if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { - for k, v := range match { - match[k] = strings.TrimSpace(v) - } + //for k, v := range match { + // match[k] = strings.TrimSpace(v) + //} year, month, day = parseDateStr(match[1]) } else if match = timeRegex2.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { - for k, v := range match { - match[k] = strings.TrimSpace(v) - } + //for k, v := range match { + // match[k] = strings.TrimSpace(v) + //} year, month, day = parseDateStr(match[1]) + } else if match = timeRegex3.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { + //for k, v := range match { + // match[k] = strings.TrimSpace(v) + //} + s := strings.Replace(match[2], ":", "", -1) + if len(s) < 6 { + s += strings.Repeat("0", 6-len(s)) + } + hour, _ = strconv.Atoi(match[1]) + min, _ = strconv.Atoi(match[2]) + sec, _ = strconv.Atoi(match[3]) + nsec, _ = strconv.Atoi(match[4]) + for i := 0; i < 9-len(match[4]); i++ { + nsec *= 10 + } + return NewFromTime(time.Date(0, time.Month(1), 1, hour, min, sec, nsec, local)), nil } else { return nil, errors.New("unsupported time format") } @@ -387,6 +410,8 @@ func ParseTimeFromContent(content string, format ...string) *Time { return NewFromStr(strings.Trim(match[0], "./_- \n\r")) } else if match := timeRegex2.FindStringSubmatch(content); len(match) >= 1 { return NewFromStr(strings.Trim(match[0], "./_- \n\r")) + } else if match := timeRegex3.FindStringSubmatch(content); len(match) >= 1 { + return NewFromStr(strings.Trim(match[0], "./_- \n\r")) } } return nil diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 66d9d0797..9a9e51c4c 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -36,8 +36,24 @@ func New(param ...interface{}) *Time { case *Time: return r case string: + if len(param) > 1 { + switch t := param[1].(type) { + case string: + return NewFromStrFormat(r, t) + case []byte: + return NewFromStrFormat(r, string(t)) + } + } return NewFromStr(r) case []byte: + if len(param) > 1 { + switch t := param[1].(type) { + case string: + return NewFromStrFormat(string(r), t) + case []byte: + return NewFromStrFormat(string(r), string(t)) + } + } return NewFromStr(string(r)) case int: return NewFromTimeStamp(int64(r)) diff --git a/os/gtime/gtime_z_bench_test.go b/os/gtime/gtime_z_bench_test.go index 85408cf10..b6be5aead 100644 --- a/os/gtime/gtime_z_bench_test.go +++ b/os/gtime/gtime_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -8,6 +8,7 @@ package gtime_test import ( "testing" + "time" "github.com/gogf/gf/os/gtime" ) @@ -42,6 +43,18 @@ func Benchmark_StrToTime(b *testing.B) { } } +func Benchmark_StrToTime_Format(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.StrToTime("2018-02-09 20:46:17.897", "Y-m-d H:i:su") + } +} + +func Benchmark_StrToTime_Layout(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.StrToTimeLayout("2018-02-09T20:46:17.897Z", time.RFC3339) + } +} + func Benchmark_ParseTimeFromContent(b *testing.B) { for i := 0; i < b.N; i++ { gtime.ParseTimeFromContent("2018-02-09T20:46:17.897Z") diff --git a/os/gtime/gtime_z_unit_basic_test.go b/os/gtime/gtime_z_unit_basic_test.go index d4e0f8fc1..1f2e42fea 100644 --- a/os/gtime/gtime_z_unit_basic_test.go +++ b/os/gtime/gtime_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -7,6 +7,7 @@ package gtime_test import ( + "github.com/gogf/gf/frame/g" "testing" "time" @@ -86,6 +87,7 @@ func Test_RFC822(t *testing.T) { func Test_StrToTime(t *testing.T) { gtest.C(t, func(t *gtest.T) { + // Correct datetime string. var testDateTimes = []string{ "2006-01-02 15:04:05", "2006/01/02 15:04:05", @@ -103,13 +105,11 @@ func Test_StrToTime(t *testing.T) { for _, item := range testDateTimes { timeTemp, err := gtime.StrToTime(item) - if err != nil { - t.Error("test fail") - } + t.Assert(err, nil) t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") } - //正常日期列表,时间00:00:00 + // Correct date string,. var testDates = []string{ "2006.01.02", "2006.01.02 00:00", @@ -118,13 +118,25 @@ func Test_StrToTime(t *testing.T) { for _, item := range testDates { timeTemp, err := gtime.StrToTime(item) - if err != nil { - t.Error("test fail") - } + t.Assert(err, nil) t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 00:00:00") } - //测试格式化formatToStdLayout + // Correct time string. + var testTimes = g.MapStrStr{ + "16:12:01": "15:04:05", + "16:12:01.789": "15:04:05.000", + } + + for k, v := range testTimes { + time1, err := gtime.StrToTime(k) + t.Assert(err, nil) + time2, err := time.ParseInLocation(v, k, time.Local) + t.Assert(err, nil) + t.Assert(time1.Time, time2) + } + + // formatToStdLayout var testDateFormats = []string{ "Y-m-d H:i:s", "\\T\\i\\m\\e Y-m-d H:i:s", @@ -149,7 +161,7 @@ func Test_StrToTime(t *testing.T) { t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05.000"), "2007-01-02 15:04:05.000") } - //异常日期列表 + // 异常日期列表 var testDatesFail = []string{ "2006.01", "06..02", diff --git a/util/gconv/gconv_z_unit_time_test.go b/util/gconv/gconv_z_unit_time_test.go index 7f27375a6..335e599e5 100644 --- a/util/gconv/gconv_z_unit_time_test.go +++ b/util/gconv/gconv_z_unit_time_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -17,9 +17,14 @@ import ( func Test_Time(t *testing.T) { gtest.C(t, func(t *gtest.T) { - t1 := "2011-10-10 01:02:03.456" - t.AssertEQ(gconv.GTime(t1), gtime.NewFromStr(t1)) - t.AssertEQ(gconv.Time(t1), gtime.NewFromStr(t1).Time) + s := "2011-10-10 01:02:03.456" + t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) + t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) t.AssertEQ(gconv.Duration(100), 100*time.Nanosecond) }) + gtest.C(t, func(t *gtest.T) { + s := "01:02:03.456" + t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) + t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) + }) }