From a86d2272afa296209a04d0b464fab5fb1cb79723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=82=96=E6=9C=89=E7=9B=8A?= Date: Thu, 4 Apr 2019 16:15:50 +0800 Subject: [PATCH 01/21] gregex_unit_test --- g/text/gregex/gregex_z_unit_test.go | 151 ++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 g/text/gregex/gregex_z_unit_test.go diff --git a/g/text/gregex/gregex_z_unit_test.go b/g/text/gregex/gregex_z_unit_test.go new file mode 100644 index 000000000..88b0f7d40 --- /dev/null +++ b/g/text/gregex/gregex_z_unit_test.go @@ -0,0 +1,151 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). 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, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" + +package gregex_test + +import ( + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/text/gregex" + "strings" + "testing" +) + +func Test_Quote(t *testing.T) { + gtest.Case(t, func() { + s1 := `[foo]` //`\[foo\]` + gtest.Assert(gregex.Quote(s1), `\[foo\]`) + }) +} + +func Test_Validate(t *testing.T) { + gtest.Case(t, func() { + var s1 = `(.+):(\d+)` + gtest.Assert(gregex.Validate(s1), nil) + s1 = `((.+):(\d+)` + gtest.Assert(gregex.Validate(s1) == nil, false) + }) +} + +func Test_IsMatch(t *testing.T) { + gtest.Case(t, func() { + var pattern = `(.+):(\d+)` + s1 := []byte(`sfs:2323`) + gtest.Assert(gregex.IsMatch(pattern, s1), true) + s1 = []byte(`sfs2323`) + gtest.Assert(gregex.IsMatch(pattern, s1), false) + s1 = []byte(`sfs:`) + gtest.Assert(gregex.IsMatch(pattern, s1), false) + }) +} + +func Test_Match(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + s := []byte("acbb" + wantSubs + "dd") + subs, err := gregex.Match(re, []byte(s)) + gtest.Assert(err, nil) + if string(subs[0]) != wantSubs { + t.Fatalf("regex:%s,Match(%q)[0] = %q; want %q", re, s, subs[0], wantSubs) + } + if string(subs[1]) != "aab" { + t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1], "aab") + } + }) +} + +func Test_MatchAllString(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + s := "acbb" + wantSubs + "dd" + subs, err := gregex.MatchAllString(re, s+`其他的`+s) + gtest.Assert(err, nil) + if string(subs[0][0]) != wantSubs { + t.Fatalf("regex:%s,Match(%q)[0] = %q; want %q", re, s, subs[0][0], wantSubs) + } + if string(subs[0][1]) != "aab" { + t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[0][1], "aab") + } + + if string(subs[1][0]) != wantSubs { + t.Fatalf("regex:%s,Match(%q)[0] = %q; want %q", re, s, subs[1][0], wantSubs) + } + if string(subs[1][1]) != "aab" { + t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1][1], "aab") + } + }) +} + +func Test_ReplaceString(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + replace := "12345" + s := "acbb" + wantSubs + "dd" + wanted := "acbb" + replace + "dd" + replacedStr, err := gregex.ReplaceString(re, replace, s) + gtest.Assert(err, nil) + if replacedStr != wanted { + t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted) + } + }) +} + +func Test_ReplaceFun(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + //replace :="12345" + s := "acbb" + wantSubs + "dd" + wanted := "acbb[x" + wantSubs + "y]dd" + wanted = "acbb" + "3个a" + "dd" + replacedStr, err := gregex.ReplaceStringFunc(re, s, func(s string) string { + if strings.Index(s, "aaa") >= 0 { + return "3个a" + } + return "[x" + s + "y]" + }) + gtest.Assert(err, nil) + if replacedStr != wanted { + t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted) + } + }) +} + +func Test_Split(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + matched := "aaabb" + item0 := "acbb" + item1 := "dd" + s := item0 + matched + item1 + gtest.Assert(gregex.IsMatchString(re, matched), true) + items := gregex.Split(re, s) //split string with matched + if items[0] != item0 { + t.Fatalf("regex:%s,Split(%q) want %q", re, s, item0) + } + if items[1] != item1 { + t.Fatalf("regex:%s,Split(%q) want %q", re, s, item0) + } + }) + + gtest.Case(t, func() { + re := "a(a+b+)b" + notmatched := "aaxbb" + item0 := "acbb" + item1 := "dd" + s := item0 + notmatched + item1 + gtest.Assert(gregex.IsMatchString(re, notmatched), false) + items := gregex.Split(re, s) //split string with notmatched then nosplitting + if items[0] != s { + t.Fatalf("regex:%s,Split(%q) want %q", re, s, item0) + } + + }) +} From be0fa4d60b1552d1faa316eae7496396f2d32930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=82=96=E6=9C=89=E7=9B=8A?= Date: Thu, 4 Apr 2019 17:45:04 +0800 Subject: [PATCH 02/21] gregex_unit_test --- g/text/gregex/gregex_z_unit_test.go | 90 ++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/g/text/gregex/gregex_z_unit_test.go b/g/text/gregex/gregex_z_unit_test.go index 88b0f7d40..94acb37f6 100644 --- a/g/text/gregex/gregex_z_unit_test.go +++ b/g/text/gregex/gregex_z_unit_test.go @@ -43,11 +43,23 @@ func Test_IsMatch(t *testing.T) { }) } +func Test_IsMatchString(t *testing.T) { + gtest.Case(t, func() { + var pattern = `(.+):(\d+)` + s1 := `sfs:2323` + gtest.Assert(gregex.IsMatchString(pattern, s1), true) + s1 = `sfs2323` + gtest.Assert(gregex.IsMatchString(pattern, s1), false) + s1 = `sfs:` + gtest.Assert(gregex.IsMatchString(pattern, s1), false) + }) +} + func Test_Match(t *testing.T) { gtest.Case(t, func() { re := "a(a+b+)b" wantSubs := "aaabb" - s := []byte("acbb" + wantSubs + "dd") + s := "acbb" + wantSubs + "dd" subs, err := gregex.Match(re, []byte(s)) gtest.Assert(err, nil) if string(subs[0]) != wantSubs { @@ -59,6 +71,46 @@ func Test_Match(t *testing.T) { }) } +func Test_MatchString(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + s := "acbb" + wantSubs + "dd" + subs, err := gregex.MatchString(re, s) + gtest.Assert(err, nil) + if string(subs[0]) != wantSubs { + t.Fatalf("regex:%s,Match(%q)[0] = %q; want %q", re, s, subs[0], wantSubs) + } + if string(subs[1]) != "aab" { + t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1], "aab") + } + }) +} + +func Test_MatchAll(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + s := "acbb" + wantSubs + "dd" + s = s + `其他的` + s + subs, err := gregex.MatchAll(re, []byte(s)) + gtest.Assert(err, nil) + if string(subs[0][0]) != wantSubs { + t.Fatalf("regex:%s,Match(%q)[0] = %q; want %q", re, s, subs[0][0], wantSubs) + } + if string(subs[0][1]) != "aab" { + t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[0][1], "aab") + } + + if string(subs[1][0]) != wantSubs { + t.Fatalf("regex:%s,Match(%q)[0] = %q; want %q", re, s, subs[1][0], wantSubs) + } + if string(subs[1][1]) != "aab" { + t.Fatalf("Match(%q)[1] = %q; want %q", s, subs[1][1], "aab") + } + }) +} + func Test_MatchAllString(t *testing.T) { gtest.Case(t, func() { re := "a(a+b+)b" @@ -82,6 +134,21 @@ func Test_MatchAllString(t *testing.T) { }) } +func Test_Replace(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + replace := "12345" + s := "acbb" + wantSubs + "dd" + wanted := "acbb" + replace + "dd" + replacedStr, err := gregex.Replace(re, []byte(replace), []byte(s)) + gtest.Assert(err, nil) + if string(replacedStr) != wanted { + t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted) + } + }) +} + func Test_ReplaceString(t *testing.T) { gtest.Case(t, func() { re := "a(a+b+)b" @@ -98,6 +165,27 @@ func Test_ReplaceString(t *testing.T) { } func Test_ReplaceFun(t *testing.T) { + gtest.Case(t, func() { + re := "a(a+b+)b" + wantSubs := "aaabb" + //replace :="12345" + s := "acbb" + wantSubs + "dd" + wanted := "acbb[x" + wantSubs + "y]dd" + wanted = "acbb" + "3个a" + "dd" + replacedStr, err := gregex.ReplaceFunc(re, []byte(s), func(s []byte) []byte { + if strings.Index(string(s), "aaa") >= 0 { + return []byte("3个a") + } + return []byte("[x" + string(s) + "y]") + }) + gtest.Assert(err, nil) + if string(replacedStr) != wanted { + t.Fatalf("regex:%s,old:%s; want %q", re, s, wanted) + } + }) +} + +func Test_ReplaceStringFunc(t *testing.T) { gtest.Case(t, func() { re := "a(a+b+)b" wantSubs := "aaabb" From dc6ab820ce20da9fb721fb3077990456b69bfeb3 Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Thu, 4 Apr 2019 17:57:17 +0800 Subject: [PATCH 03/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=E5=87=BD=E6=95=B0=EF=BC=88parseDateStr?= =?UTF-8?q?=EF=BC=89=E5=AF=B9"02.jan.2006"=E6=A0=BC=E5=BC=8F=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E8=A7=A3=E6=9E=90=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82=20=E6=9B=B4=E6=AD=A3=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=88isNumeric=EF=BC=89=E7=9A=84=E6=B3=A8=E9=87=8A=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/os/gtime/gtime.go | 491 ++++++++++++++++++++++---------------------- 1 file changed, 246 insertions(+), 245 deletions(-) diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index 749a8e9fe..e66e198ef 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -5,319 +5,320 @@ // You can obtain one at https://github.com/gogf/gf. // Package gtime provides functionality for measuring and displaying time. -// +// // 时间管理. package gtime import ( - "errors" - "github.com/gogf/gf/g/text/gregex" - "regexp" - "strconv" - "strings" - "time" + "errors" + "regexp" + "strconv" + "strings" + "time" + + "github.com/gogf/gf/g/text/gregex" ) const ( - // 时间间隔缩写 - D = 24*time.Hour - H = time.Hour - M = time.Minute - S = time.Second - MS = time.Millisecond - US = time.Microsecond - NS = time.Nanosecond + // 时间间隔缩写 + D = 24 * time.Hour + H = time.Hour + M = time.Minute + S = time.Second + MS = time.Millisecond + US = time.Microsecond + NS = time.Nanosecond - // 常用时间格式正则匹配,支持的标准时间格式: - // "2017-12-14 04:51:34 +0805 LMT", - // "2017-12-14 04:51:34 +0805 LMT", - // "2006-01-02T15:04:05Z07:00", - // "2014-01-17T01:19:15+08:00", - // "2018-02-09T20:46:17.897Z", - // "2018-02-09 20:46:17.897", - // "2018-02-09T20:46:17Z", - // "2018-02-09 20:46:17", - // "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]*)` - // 01-Nov-2018 11:50:28 - // 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]*)` + // 常用时间格式正则匹配,支持的标准时间格式: + // "2017-12-14 04:51:34 +0805 LMT", + // "2017-12-14 04:51:34 +0805 LMT", + // "2006-01-02T15:04:05Z07:00", + // "2014-01-17T01:19:15+08:00", + // "2018-02-09T20:46:17.897Z", + // "2018-02-09 20:46:17.897", + // "2018-02-09T20:46:17Z", + // "2018-02-09 20:46:17", + // "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]*)` + // 01-Nov-2018 11:50:28 + // 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]*)` ) var ( - // 使用正则判断会比直接使用ParseInLocation挨个轮训判断要快很多 - timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) - timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) - // 月份英文与阿拉伯数字对应关系 - monthMap = map[string]int { - "jan" : 1, - "feb" : 2, - "mar" : 3, - "apr" : 4, - "may" : 5, - "jun" : 6, - "jul" : 7, - "aug" : 8, - "sep" : 9, - "sept" : 9, - "oct" : 10, - "nov" : 11, - "dec" : 12, - "january" : 1, - "february" : 2, - "march" : 3, - "april" : 4, - "june" : 6, - "july" : 7, - "august" : 8, - "september" : 9, - "october" : 10, - "november" : 11, - "december" : 12, - } + // 使用正则判断会比直接使用ParseInLocation挨个轮训判断要快很多 + timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) + timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) + // 月份英文与阿拉伯数字对应关系 + monthMap = map[string]int{ + "jan": 1, + "feb": 2, + "mar": 3, + "apr": 4, + "may": 5, + "jun": 6, + "jul": 7, + "aug": 8, + "sep": 9, + "sept": 9, + "oct": 10, + "nov": 11, + "dec": 12, + "january": 1, + "february": 2, + "march": 3, + "april": 4, + "june": 6, + "july": 7, + "august": 8, + "september": 9, + "october": 10, + "november": 11, + "december": 12, + } ) // 设置当前进程全局的默认时区,如: Asia/Shanghai func SetTimeZone(zone string) error { - location, err := time.LoadLocation(zone) - if err == nil { - time.Local = location - } - return err + location, err := time.LoadLocation(zone) + if err == nil { + time.Local = location + } + return err } // 获取当前的纳秒数 func Nanosecond() int64 { - return time.Now().UnixNano() + return time.Now().UnixNano() } // 获取当前的微秒数 func Microsecond() int64 { - return time.Now().UnixNano()/1e3 + return time.Now().UnixNano() / 1e3 } // 获取当前的毫秒数 func Millisecond() int64 { - return time.Now().UnixNano()/1e6 + return time.Now().UnixNano() / 1e6 } // 获取当前的秒数(时间戳) func Second() int64 { - return time.Now().Unix() + return time.Now().Unix() } // 获得当前的日期(例如:2006-01-02) func Date() string { - return time.Now().Format("2006-01-02") + return time.Now().Format("2006-01-02") } // 获得当前的时间(例如:2006-01-02 15:04:05) func Datetime() string { - return time.Now().Format("2006-01-02 15:04:05") + return time.Now().Format("2006-01-02 15:04:05") } // 解析日期字符串(日期支持'-'或'/'或'.'连接符号) func parseDateStr(s string) (year, month, day int) { - array := strings.Split(s, "-") - if len(array) < 3 { - array = strings.Split(s, "/") - } - if len(array) < 3 { - array = strings.Split(s, ".") - } - // 解析失败 - if len(array) < 3 { - return - } - // 判断年份在开头还是末尾 - if isNumeric(array[1]) { - year, _ = strconv.Atoi(array[0]) - month, _ = strconv.Atoi(array[1]) - day, _ = strconv.Atoi(array[2]) - } else { - if v, ok := monthMap[strings.ToLower(array[1])]; ok { - month = v - } else { - return - } - year, _ = strconv.Atoi(array[2]) - day, _ = strconv.Atoi(array[1]) - } - // 年是否为缩写,如果是,那么需要补上前缀 - if year < 100 { - year = int(time.Now().Year()/100)*100 + year - } - return + array := strings.Split(s, "-") + if len(array) < 3 { + array = strings.Split(s, "/") + } + if len(array) < 3 { + array = strings.Split(s, ".") + } + // 解析失败 + if len(array) < 3 { + return + } + // 判断年份在开头还是末尾 + if isNumeric(array[1]) { + year, _ = strconv.Atoi(array[0]) + month, _ = strconv.Atoi(array[1]) + day, _ = strconv.Atoi(array[2]) + } else { + if v, ok := monthMap[strings.ToLower(array[1])]; ok { + month = v + } else { + return + } + year, _ = strconv.Atoi(array[2]) + day, _ = strconv.Atoi(array[0]) + } + // 年是否为缩写,如果是,那么需要补上前缀 + if year < 100 { + year = int(time.Now().Year()/100)*100 + year + } + return } // 字符串转换为时间对象,format参数指定格式的format(如: Y-m-d H:i:s),当指定format参数时效果同StrToTimeFormat方法。 // 注意:自动解析日期时间时,必须有日期才能解析成功,如果字符串中不带有日期字段,那么解析失败。 -func StrToTime(str string, format...string) (*Time, error) { - if len(format) > 0 { - return StrToTimeFormat(str, format[0]) - } - var year, month, day int - var hour, min, sec, nsec int - var match []string - var local = time.Local - if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { - 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) - } - year, month, day = parseDateStr(match[1]) - } else { - return nil, errors.New("unsupported time format") - } +func StrToTime(str string, format ...string) (*Time, error) { + if len(format) > 0 { + return StrToTimeFormat(str, format[0]) + } + var year, month, day int + var hour, min, sec, nsec int + var match []string + var local = time.Local + if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { + 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) + } + year, month, day = parseDateStr(match[1]) + } else { + return nil, errors.New("unsupported time format") + } - // 时间 - if len(match[2]) > 0 { - s := strings.Replace(match[2], ":", "", -1) - if len(s) < 6 { - s += strings.Repeat("0", 6 - len(s)) - } - hour, _ = strconv.Atoi(s[0 : 2]) - min, _ = strconv.Atoi(s[2 : 4]) - sec, _ = strconv.Atoi(s[4 : 6]) - } - // 纳秒,检查并执行位补齐 - if len(match[3]) > 0 { - nsec, _ = strconv.Atoi(match[3]) - for i := 0; i < 9 - len(match[3]); i++ { - nsec *= 10 - } - } - // 如果字符串中有时区信息(具体时间信息),那么执行时区转换,将时区转成UTC - if match[4] != "" && match[6] == "" { - match[6] = "000000" - } - // 如果offset有值优先处理offset,否则处理后面的时区名称 - if match[6] != "" { - zone := strings.Replace(match[6], ":", "", -1) - zone = strings.TrimLeft(zone, "+-") - if len(zone) <= 6 { - zone += strings.Repeat("0", 6 - len(zone)) - h, _ := strconv.Atoi(zone[0 : 2]) - m, _ := strconv.Atoi(zone[2 : 4]) - s, _ := strconv.Atoi(zone[4 : 6]) - // 判断字符串输入的时区是否和当前程序时区相等(使用offset判断),不相等则将对象统一转换为UTC时区 - // 当前程序时区Offset(秒) - _, localOffset := time.Now().Zone() - if (h * 3600 + m * 60 + s) != localOffset { - local = time.UTC - // UTC时差转换 - operation := match[5] - if operation != "+" && operation != "-" { - operation = "-" - } - switch operation { - case "+": - if h > 0 { - hour -= h - } - if m > 0 { - min -= m - } - if s > 0 { - sec -= s - } - case "-": - if h > 0 { - hour += h - } - if m > 0 { - min += m - } - if s > 0 { - sec += s - } - } - } - } - } - // 统一生成UTC时间对象 - return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil + // 时间 + if len(match[2]) > 0 { + s := strings.Replace(match[2], ":", "", -1) + if len(s) < 6 { + s += strings.Repeat("0", 6-len(s)) + } + hour, _ = strconv.Atoi(s[0:2]) + min, _ = strconv.Atoi(s[2:4]) + sec, _ = strconv.Atoi(s[4:6]) + } + // 纳秒,检查并执行位补齐 + if len(match[3]) > 0 { + nsec, _ = strconv.Atoi(match[3]) + for i := 0; i < 9-len(match[3]); i++ { + nsec *= 10 + } + } + // 如果字符串中有时区信息(具体时间信息),那么执行时区转换,将时区转成UTC + if match[4] != "" && match[6] == "" { + match[6] = "000000" + } + // 如果offset有值优先处理offset,否则处理后面的时区名称 + if match[6] != "" { + zone := strings.Replace(match[6], ":", "", -1) + zone = strings.TrimLeft(zone, "+-") + if len(zone) <= 6 { + zone += strings.Repeat("0", 6-len(zone)) + h, _ := strconv.Atoi(zone[0:2]) + m, _ := strconv.Atoi(zone[2:4]) + s, _ := strconv.Atoi(zone[4:6]) + // 判断字符串输入的时区是否和当前程序时区相等(使用offset判断),不相等则将对象统一转换为UTC时区 + // 当前程序时区Offset(秒) + _, localOffset := time.Now().Zone() + if (h*3600 + m*60 + s) != localOffset { + local = time.UTC + // UTC时差转换 + operation := match[5] + if operation != "+" && operation != "-" { + operation = "-" + } + switch operation { + case "+": + if h > 0 { + hour -= h + } + if m > 0 { + min -= m + } + if s > 0 { + sec -= s + } + case "-": + if h > 0 { + hour += h + } + if m > 0 { + min += m + } + if s > 0 { + sec += s + } + } + } + } + } + // 统一生成UTC时间对象 + return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil } // 时区转换 -func ConvertZone(strTime string, toZone string, fromZone...string) (*Time, error) { - t, err := StrToTime(strTime) - if err != nil { - return nil, err - } - if len(fromZone) > 0 { - if l, err := time.LoadLocation(fromZone[0]); err != nil { - return nil, err - } else { - t.Time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) - } - } - if l, err := time.LoadLocation(toZone); err != nil { - return nil, err - } else { - return t.ToLocation(l), nil - } +func ConvertZone(strTime string, toZone string, fromZone ...string) (*Time, error) { + t, err := StrToTime(strTime) + if err != nil { + return nil, err + } + if len(fromZone) > 0 { + if l, err := time.LoadLocation(fromZone[0]); err != nil { + return nil, err + } else { + t.Time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) + } + } + if l, err := time.LoadLocation(toZone); err != nil { + return nil, err + } else { + return t.ToLocation(l), nil + } } // 字符串转换为时间对象,指定字符串时间格式,format格式形如:Y-m-d H:i:s func StrToTimeFormat(str string, format string) (*Time, error) { - return StrToTimeLayout(str, formatToStdLayout(format)) + return StrToTimeLayout(str, formatToStdLayout(format)) } // 字符串转换为时间对象,通过标准库layout格式进行解析,layout格式形如:2006-01-02 15:04:05 func StrToTimeLayout(str string, layout string) (*Time, error) { - if t, err := time.ParseInLocation(layout, str, time.Local); err == nil { - return NewFromTime(t), nil - } else { - return nil, err - } + if t, err := time.ParseInLocation(layout, str, time.Local); err == nil { + return NewFromTime(t), nil + } else { + return nil, err + } } // 从字符串内容中(也可以是文件名称等等)解析时间,并返回解析成功的时间对象,否则返回nil。 // 注意当内容中存在多个时间时,会解析第一个。 // format参数可以指定需要解析的时间格式。 -func ParseTimeFromContent(content string, format...string) *Time { - if len(format) > 0 { - if match, err := gregex.MatchString(formatToRegexPattern(format[0]), content); err == nil && len(match) > 0 { - return NewFromStrFormat(match[0], format[0]) - } - } else { - if match := timeRegex1.FindStringSubmatch(content); len(match) >= 1 { - 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")) - } - } - return nil +func ParseTimeFromContent(content string, format ...string) *Time { + if len(format) > 0 { + if match, err := gregex.MatchString(formatToRegexPattern(format[0]), content); err == nil && len(match) > 0 { + return NewFromStrFormat(match[0], format[0]) + } + } else { + if match := timeRegex1.FindStringSubmatch(content); len(match) >= 1 { + 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")) + } + } + return nil } // 计算函数f执行的时间,单位纳秒 func FuncCost(f func()) int64 { - t := Nanosecond() - f() - return Nanosecond() - t + t := Nanosecond() + f() + return Nanosecond() - t } -// 判断锁给字符串是否为数字 +// 判断所给字符串是否为数字 func isNumeric(s string) bool { - length := len(s) - if length == 0 { - return false - } - for i := 0; i < len(s); i++ { - if s[i] < byte('0') || s[i] > byte('9') { - return false - } - } - return true -} \ No newline at end of file + length := len(s) + if length == 0 { + return false + } + for i := 0; i < len(s); i++ { + if s[i] < byte('0') || s[i] > byte('9') { + return false + } + } + return true +} From aa4dca11f07f8d22b00c3483ab42e3ceabfa03c6 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 4 Apr 2019 22:52:56 +0800 Subject: [PATCH 04/21] fix issue in gtime; update comment for g/gtest --- g/container/container.go | 7 ---- g/crypto/crypto.go | 7 ---- g/database/database.go | 7 ---- g/database/gredis/gredis.go | 2 +- g/encoding/encoding.go | 7 ---- g/frame/frame.go | 7 ---- g/g.go | 8 ---- g/g_func.go | 22 ++--------- g/g_logger.go | 12 ++---- g/g_object.go | 33 ++++------------ g/g_setting.go | 5 +-- g/net/net.go | 1 - g/os/gtime/gtime.go | 2 +- g/os/os.go | 1 - g/test/gtest/gtest.go | 76 ++++++++++++++++++++++--------------- g/util/util.go | 1 - geg/other/test.go | 8 ++-- 17 files changed, 70 insertions(+), 136 deletions(-) delete mode 100644 g/container/container.go delete mode 100644 g/crypto/crypto.go delete mode 100644 g/database/database.go delete mode 100644 g/encoding/encoding.go delete mode 100644 g/frame/frame.go delete mode 100644 g/net/net.go delete mode 100644 g/os/os.go delete mode 100644 g/util/util.go diff --git a/g/container/container.go b/g/container/container.go deleted file mode 100644 index 065333f05..000000000 --- a/g/container/container.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). 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, -// You can obtain one at https://github.com/gogf/gf. - -package container diff --git a/g/crypto/crypto.go b/g/crypto/crypto.go deleted file mode 100644 index cbad3d077..000000000 --- a/g/crypto/crypto.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). 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, -// You can obtain one at https://github.com/gogf/gf. - -package crypto diff --git a/g/database/database.go b/g/database/database.go deleted file mode 100644 index 17dbb4497..000000000 --- a/g/database/database.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). 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, -// You can obtain one at https://github.com/gogf/gf. - -package database diff --git a/g/database/gredis/gredis.go b/g/database/gredis/gredis.go index 45d25abb3..b23bf0c42 100644 --- a/g/database/gredis/gredis.go +++ b/g/database/gredis/gredis.go @@ -107,7 +107,7 @@ func New(config Config) *Redis { // return redis instance with default group. // // 获取指定分组名称的Redis单例对象,底层根据配置信息公用的连接池(连接池单例)。 -func Instance(name...string) *Redis { +func Instance(name ...string) *Redis { group := DEFAULT_GROUP_NAME if len(name) > 0 { group = name[0] diff --git a/g/encoding/encoding.go b/g/encoding/encoding.go deleted file mode 100644 index 5faed76ca..000000000 --- a/g/encoding/encoding.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). 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, -// You can obtain one at https://github.com/gogf/gf. - -package encoding diff --git a/g/frame/frame.go b/g/frame/frame.go deleted file mode 100644 index 32d65c3de..000000000 --- a/g/frame/frame.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). 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, -// You can obtain one at https://github.com/gogf/gf. - -package frame diff --git a/g/g.go b/g/g.go index 806053be0..b83d71781 100644 --- a/g/g.go +++ b/g/g.go @@ -9,13 +9,9 @@ package g import "github.com/gogf/gf/g/container/gvar" // Universal variable type, like generics. -// -// 动态变量类型,可以用该类型替代interface{}类型 type Var = gvar.Var // Frequently-used map type alias. -// -// 常用map数据结构(使用别名) type Map = map[string]interface{} type MapAnyAny = map[interface{}]interface{} type MapAnyStr = map[interface{}]string @@ -28,8 +24,6 @@ type MapIntStr = map[int]string type MapIntInt = map[int]int // Frequently-used slice type alias. -// -// 常用list数据结构(使用别名) type List = []Map type ListAnyStr = []map[interface{}]string type ListAnyInt = []map[interface{}]int @@ -41,8 +35,6 @@ type ListIntStr = []map[int]string type ListIntInt = []map[int]int // Frequently-used slice type alias. -// -// 常用slice数据结构(使用别名) type Slice = []interface{} type SliceAny = []interface{} type SliceStr = []string diff --git a/g/g_func.go b/g/g_func.go index 78c54497b..670e28136 100644 --- a/g/g_func.go +++ b/g/g_func.go @@ -13,38 +13,28 @@ import ( "github.com/gogf/gf/g/util/gutil" ) -// NewVar creates a *Var. -// -// 动态变量 +// NewVar returns a *gvar.Var. func NewVar(i interface{}, unsafe...bool) *Var { return gvar.New(i, unsafe...) } // Wait blocks until all the web servers shutdown. -// -// 阻塞等待HTTPServer执行完成(同一进程多HTTPServer情况下) func Wait() { ghttp.Wait() } // Dump dumps a variable to stdout with more manually readable. -// -// 格式化打印变量. func Dump(i...interface{}) { gutil.Dump(i...) } // Export exports a variable to string with more manually readable. -// -// 格式化导出变量. func Export(i...interface{}) string { return gutil.Export(i...) } -// Throw throws a exception, which can be caught by Catch function. +// Throw throws a exception, which can be caught by TryCatch function. // It always be used in TryCatch function. -// -// 抛出一个异常 func Throw(exception interface{}) { gutil.Throw(exception) } @@ -55,12 +45,8 @@ func TryCatch(try func(), catch ... func(exception interface{})) { } // IsEmpty checks given value empty or not. -// false: integer(0), bool(false), slice/map(len=0), nil; -// true : other. -// -// 判断给定的变量是否为空。 -// 整型为0, 布尔为false, slice/map长度为0, 其他为nil的情况,都为空。 -// 为空时返回true,否则返回false。 +// It returns false if value is: integer(0), bool(false), slice/map(len=0), nil; +// or else true. func IsEmpty(value interface{}) bool { return empty.IsEmpty(value) } \ No newline at end of file diff --git a/g/g_logger.go b/g/g_logger.go index 551bade79..c32a2cfb8 100644 --- a/g/g_logger.go +++ b/g/g_logger.go @@ -10,23 +10,17 @@ import ( "github.com/gogf/gf/g/os/glog" ) -// Disable/Enabled debug of logging globally. -// -// 是否显示调试信息 +// SetDebug disables/enables debug level for logging globally. func SetDebug(debug bool) { glog.SetDebug(debug) } -// Set the logging level globally. -// -// 设置日志的显示等级 +// SetLogLevel sets the logging level globally. func SetLogLevel(level int) { glog.SetLevel(level) } -// Get the global logging level. -// -// 获取设置的日志显示等级 +// GetLogLevel returns the global logging level. func GetLogLevel() int { return glog.GetLevel() } \ No newline at end of file diff --git a/g/g_object.go b/g/g_object.go index f8799b02d..8bb8feaed 100644 --- a/g/g_object.go +++ b/g/g_object.go @@ -17,59 +17,42 @@ import ( "github.com/gogf/gf/g/os/gcfg" ) -// Get an instance of http server with specified name. -// -// HTTPServer单例对象 +// Server returns an instance of http server with specified name. func Server(name...interface{}) *ghttp.Server { return ghttp.GetServer(name...) } -// Get an instance of tcp server with specified name. -// -// TCPServer单例对象 +// TCPServer returns an instance of tcp server with specified name. func TCPServer(name...interface{}) *gtcp.Server { return gtcp.GetServer(name...) } -// Get an instance of udp server with specified name. -// -// UDPServer单例对象 +// UDPServer returns an instance of udp server with specified name. func UDPServer(name...interface{}) *gudp.Server { return gudp.GetServer(name...) } -// Get an instance of template engine object with specified name. -// -// 核心对象:View +// View returns an instance of template engine object with specified name. func View(name...string) *gview.View { return gins.View(name...) } -// Get an instance of config object with specified default config file name. -// -// Config配置管理对象, -// 配置文件目录查找依次为:启动参数cfgpath、当前程序运行目录 +// Config returns an instance of config object with specified name. func Config(file...string) *gcfg.Config { return gins.Config(file...) } -// Get an instance of database ORM object with specified configuration group name. -// -// 数据库操作对象,使用了连接池 +// Database returns an instance of database ORM object with specified configuration group name. func Database(name...string) gdb.DB { return gins.Database(name...) } -// Alias of Database. -// -// (别名)Database +// Alias of Database. See Database. func DB(name...string) gdb.DB { return gins.Database(name...) } -// Get an instance of redis client with specified configuration group name. -// -// Redis操作对象,使用了连接池 +// Redis returns an instance of redis client with specified configuration group name. func Redis(name...string) *gredis.Redis { return gins.Redis(name...) } \ No newline at end of file diff --git a/g/g_setting.go b/g/g_setting.go index c12f43b98..71e093a73 100644 --- a/g/g_setting.go +++ b/g/g_setting.go @@ -8,9 +8,8 @@ package g import "github.com/gogf/gf/g/net/ghttp" -// SetServerGraceful enables/disables graceful reload feature of ghttp Web Server. -// -// 是否开启WebServer的平滑重启特性。 +// SetServerGraceful enables/disables graceful/hot reload feature of http Web Server. +// This feature is disabled in default. func SetServerGraceful(enabled bool) { ghttp.SetGraceful(enabled) } diff --git a/g/net/net.go b/g/net/net.go deleted file mode 100644 index 9d9f1a11e..000000000 --- a/g/net/net.go +++ /dev/null @@ -1 +0,0 @@ -package net diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index 749a8e9fe..089806709 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -147,7 +147,7 @@ func parseDateStr(s string) (year, month, day int) { return } year, _ = strconv.Atoi(array[2]) - day, _ = strconv.Atoi(array[1]) + day, _ = strconv.Atoi(array[0]) } // 年是否为缩写,如果是,那么需要补上前缀 if year < 100 { diff --git a/g/os/os.go b/g/os/os.go deleted file mode 100644 index cf1f67e04..000000000 --- a/g/os/os.go +++ /dev/null @@ -1 +0,0 @@ -package os diff --git a/g/test/gtest/gtest.go b/g/test/gtest/gtest.go index 30d61b9a2..0b520e0a8 100644 --- a/g/test/gtest/gtest.go +++ b/g/test/gtest/gtest.go @@ -4,9 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gtest provides simple and useful test utils. -// -// 测试模块. +// Package gtest provides convenient test utils for unit testing. package gtest import ( @@ -19,7 +17,9 @@ import ( "testing" ) -// 封装一个测试用例 +// Case creates an unit test case. +// The param is the pointer to testing.T of stdlib (*testing.T). +// The param is the callback function for unit test case. func Case(t *testing.T, f func()) { defer func() { if err := recover(); err != nil { @@ -30,7 +30,7 @@ func Case(t *testing.T, f func()) { f() } -// 断言判断, 相等 +// Assert checks and EQUAL. func Assert(value, expect interface{}) { rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) @@ -50,14 +50,9 @@ func Assert(value, expect interface{}) { } } -// 断言判断, 相等, 包括数据类型 +// AssertEQ checks and EQUAL, including their TYPES. func AssertEQ(value, expect interface{}) { - // 类型判断 - t1 := reflect.TypeOf(value) - t2 := reflect.TypeOf(expect) - if t1 != t2 { - panic(fmt.Sprintf(`[ASSERT] EXPECT TYPE %v == %v`, t1, t2)) - } + // Value assert. rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) if rvValue.Kind() == reflect.Ptr { @@ -74,9 +69,15 @@ func AssertEQ(value, expect interface{}) { if fmt.Sprintf("%v", value) != fmt.Sprintf("%v", expect) { panic(fmt.Sprintf(`[ASSERT] EXPECT %v == %v`, value, expect)) } + // Type assert. + t1 := reflect.TypeOf(value) + t2 := reflect.TypeOf(expect) + if t1 != t2 { + panic(fmt.Sprintf(`[ASSERT] EXPECT TYPE %v[%v] == %v[%v]`, value, t1, expect, t2)) + } } -// 断言判断, 不相等 +// AssertNE checks and NOT EQUAL. func AssertNE(value, expect interface{}) { rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) @@ -96,7 +97,9 @@ func AssertNE(value, expect interface{}) { } } -// 断言判断, value > expect; 注意: 仅有字符串、整形、浮点型才可以比较 +// AssertGT checks is GREATER THAN . +// Notice that, only string, integer and float types can be compared by AssertGT, +// others are invalid. func AssertGT(value, expect interface{}) { passed := false switch reflect.ValueOf(expect).Kind() { @@ -117,7 +120,9 @@ func AssertGT(value, expect interface{}) { } } -// 断言判断, value >= expect; 注意: 仅有字符串、整形、浮点型才可以比较 +// AssertGTE checks is GREATER OR EQUAL THAN . +// Notice that, only string, integer and float types can be compared by AssertGTE, +// others are invalid. func AssertGTE(value, expect interface{}) { passed := false switch reflect.ValueOf(expect).Kind() { @@ -138,7 +143,9 @@ func AssertGTE(value, expect interface{}) { } } -// 断言判断, value < expect; 注意: 仅有字符串、整形、浮点型才可以比较 +// AssertLT checks is LESS EQUAL THAN . +// Notice that, only string, integer and float types can be compared by AssertLT, +// others are invalid. func AssertLT(value, expect interface{}) { passed := false switch reflect.ValueOf(expect).Kind() { @@ -159,7 +166,9 @@ func AssertLT(value, expect interface{}) { } } -// 断言判断, value <= expect; 注意: 仅有字符串、整形、浮点型才可以比较 +// AssertLTE checks is LESS OR EQUAL THAN . +// Notice that, only string, integer and float types can be compared by AssertLTE, +// others are invalid. func AssertLTE(value, expect interface{}) { passed := false switch reflect.ValueOf(expect).Kind() { @@ -180,16 +189,18 @@ func AssertLTE(value, expect interface{}) { } } - -// 断言判断, value IN expect; 注意: expect必须为slice类型。 -// 注意:value参数可以为普通变量,也可以为slice类型。 +// AssertIN checks is IN . +// The should be a slice, +// but the can be a slice or a basic type variable. +// TODO map support. func AssertIN(value, expect interface{}) { passed := true switch reflect.ValueOf(expect).Kind() { case reflect.Slice, reflect.Array: + expectSlice := gconv.Interfaces(expect) for _, v1 := range gconv.Interfaces(value) { result := false - for _, v2 := range gconv.Interfaces(expect) { + for _, v2 := range expectSlice { if v1 == v2 { result = true break @@ -206,7 +217,10 @@ func AssertIN(value, expect interface{}) { } } -// 断言判断, value NOT IN expect; 注意: expect必须为slice类型 +// AssertNI checks is NOT IN . +// The should be a slice, +// but the can be a slice or a basic type variable. +// TODO map support. func AssertNI(value, expect interface{}) { passed := true switch reflect.ValueOf(expect).Kind() { @@ -230,18 +244,18 @@ func AssertNI(value, expect interface{}) { } } -// 提示错误不退出进程执行 +// Error panics with given . func Error(message...interface{}) { panic(fmt.Sprintf("[ERROR] %s", fmt.Sprint(message...))) } -// 提示错误并退出进程执行 +// Fatal prints to stderr and exit the process. func Fatal(message...interface{}) { fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getBacktrace()) os.Exit(1) } -// Map比较,如果相等返回nil,否则返回错误信息. +// compareMap compares two maps, returns nil if they are equal, or else returns error. func compareMap(value, expect interface{}) error { rvValue := reflect.ValueOf(value) rvExpect := reflect.ValueOf(expect) @@ -253,8 +267,9 @@ func compareMap(value, expect interface{}) error { if rvExpect.Kind() == reflect.Map { if rvValue.Kind() == reflect.Map { if rvExpect.Len() == rvValue.Len() { - // 将两个map类型转换为同一个map类型, 才能执行比较, - // 直接使用 rvValue.MapIndex(key).Interface() 当key类型不一致时会报错。 + // Turn two interface maps to the same type for comparison. + // Direct use of rvValue.MapIndex(key).Interface() will panic + // when the key types are inconsistent. mValue := make(map[string]string) mExpect := make(map[string]string) ksValue := rvValue.MapKeys() @@ -280,7 +295,8 @@ func compareMap(value, expect interface{}) error { return nil } -// 获取文件调用回溯字符串,参数skip表示调用端往上多少级开始回溯 +// getBacktrace returns the caller backtrace content from getBacktrace. +// The param indicates the skip count of the caller backtrace from getBacktrace. func getBacktrace(skip...int) string { customSkip := 0 if len(skip) > 0 { @@ -289,7 +305,7 @@ func getBacktrace(skip...int) string { backtrace := "" index := 1 from := 0 - // 首先定位业务文件开始位置 + // Ignore current gtest lines and find the beginning index of caller file. for i := 0; i < 10; i++ { if _, file, _, ok := runtime.Caller(i); ok { if reg, _ := regexp.Compile(`gtest\.go$`); !reg.MatchString(file) { @@ -298,7 +314,7 @@ func getBacktrace(skip...int) string { } } } - // 从业务文件开始位置根据自定义的skip开始backtrace + // Get the caller backtrace from business caller file. goRoot := runtime.GOROOT() for i := from + customSkip; i < 10000; i++ { if _, file, cline, ok := runtime.Caller(i); ok && file != "" { diff --git a/g/util/util.go b/g/util/util.go deleted file mode 100644 index c7d868219..000000000 --- a/g/util/util.go +++ /dev/null @@ -1 +0,0 @@ -package util diff --git a/geg/other/test.go b/geg/other/test.go index 7028e0f04..e4b2f208e 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,10 +2,12 @@ package main import ( "fmt" - "github.com/gogf/gf/g/os/gcfg" + "github.com/gogf/gf/g/container/gring" ) func main() { - fmt.Println(gcfg.Instance().GetString("viewpath")) - fmt.Println(gcfg.Instance().GetString("database.default.0.host")) + r := gring.New(3) + r.Put(1) + r.Put(2) + fmt.Println(r.Val()) } \ No newline at end of file From 6863928b06381b2a105f6ebee07f75a0c3057c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E5=A2=A8=E6=9F=93=E5=B0=BD=E9=9D=92=E8=A1=A3?= =?UTF-8?q?=E9=A2=9C?= <378809360@qq.com> Date: Thu, 4 Apr 2019 22:55:13 +0800 Subject: [PATCH 05/21] =?UTF-8?q?!18=20=E9=99=90=E5=88=B6=E4=B8=8B?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=8D=95=E6=9D=A1=E8=AE=B0=E5=BD=95=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=9D=A1=E6=95=B0=20Merge=20pull=20request=20!18=20fr?= =?UTF-8?q?om=20=E4=B8=80=E5=A2=A8=E6=9F=93=E5=B0=BD=E9=9D=92=E8=A1=A3?= =?UTF-8?q?=E9=A2=9C/master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/database/gdb/gdb_model.go | 1 + 1 file changed, 1 insertion(+) diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index d87a85fd6..1626fe24f 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -456,6 +456,7 @@ func (md *Model) All() (Result, error) { // 链式操作,查询单条记录 func (md *Model) One() (Record, error) { + md.limit = 1 list, err := md.All() if err != nil { return nil, err From 4f1056298014fcc40cdd7d35041da66ec6ec16dc Mon Sep 17 00:00:00 2001 From: John Date: Thu, 4 Apr 2019 23:22:09 +0800 Subject: [PATCH 06/21] remove limit for gdb.Model.One --- g/database/gdb/gdb_model.go | 1 - 1 file changed, 1 deletion(-) diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index 1626fe24f..d87a85fd6 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -456,7 +456,6 @@ func (md *Model) All() (Result, error) { // 链式操作,查询单条记录 func (md *Model) One() (Record, error) { - md.limit = 1 list, err := md.All() if err != nil { return nil, err From 2a29483456b9e36235fe577564b7d787b3345cb2 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 4 Apr 2019 23:24:27 +0800 Subject: [PATCH 07/21] comment fix --- g/os/gtime/gtime.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index 089806709..03d551d81 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -308,7 +308,7 @@ func FuncCost(f func()) int64 { return Nanosecond() - t } -// 判断锁给字符串是否为数字 +// 判断所给字符串是否为数字 func isNumeric(s string) bool { length := len(s) if length == 0 { From dc82ce395acbcaa6e316140ca7867732a246b2ff Mon Sep 17 00:00:00 2001 From: John Date: Fri, 5 Apr 2019 00:23:59 +0800 Subject: [PATCH 08/21] comment updates for gcfg --- g/os/gcfg/gcfg.go | 62 +++++++++++++++++++++-------------------------- geg/other/test.go | 32 ++++++++++++++++++++---- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index 577a174e8..e3f11b13a 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -100,12 +100,13 @@ func (c *Config) filePath(file...string) (path string) { return path } -// 设置配置管理器的配置文件存放目录绝对路径 +// SetPath sets the configuration directory path for file search. +// The param can be absolute or relative path, but absolute path is suggested. func (c *Config) SetPath(path string) error { - // 判断绝对路径(或者工作目录下目录) + // Absolute path. realPath := gfile.RealPath(path) if realPath == "" { - // 判断相对路径 + // Relative path. c.paths.RLockFunc(func(array []string) { for _, v := range array { if path, _ := gspath.Search(v, path); path != "" { @@ -115,7 +116,7 @@ func (c *Config) SetPath(path string) error { } }) } - // 目录不存在错误处理 + // Path not exist. if realPath == "" { buffer := bytes.NewBuffer(nil) if c.paths.Len() > 0 { @@ -132,13 +133,13 @@ func (c *Config) SetPath(path string) error { glog.Error(err) return err } - // 路径必须为目录类型 + // Should be a directory. if !gfile.IsDir(realPath) { err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path)) glog.Error(err) return err } - // 重复判断 + // Repeated path check. if c.paths.Search(realPath) != -1 { return nil } @@ -149,19 +150,23 @@ func (c *Config) SetPath(path string) error { return nil } -// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。 -// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。 +// SetViolenceCheck sets whether to perform level conflict check. +// This feature needs to be enabled when there is a level symbol in the key name. +// The default is off. +// Turning on this feature is quite expensive, +// and it is not recommended to allow separators in the key names. +// It is best to avoid this on the application side. func (c *Config) SetViolenceCheck(check bool) { c.vc.Set(check) c.Clear() } -// 添加配置管理器的配置文件搜索路径 +// AddPath adds a absolute or relative path to the search paths. func (c *Config) AddPath(path string) error { - // 判断绝对路径(或者工作目录下目录) + // Absolute path. realPath := gfile.RealPath(path) if realPath == "" { - // 判断相对路径 + // Relative path. c.paths.RLockFunc(func(array []string) { for _, v := range array { if path, _ := gspath.Search(v, path); path != "" { @@ -171,7 +176,6 @@ func (c *Config) AddPath(path string) error { } }) } - // 目录不存在错误处理 if realPath == "" { buffer := bytes.NewBuffer(nil) if c.paths.Len() > 0 { @@ -188,13 +192,12 @@ func (c *Config) AddPath(path string) error { glog.Error(err) return err } - // 路径必须为目录类型 if !gfile.IsDir(realPath) { err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path)) glog.Error(err) return err } - // 重复判断 + // Repeated path check. if c.paths.Search(realPath) != -1 { return nil } @@ -203,8 +206,10 @@ func (c *Config) AddPath(path string) error { return nil } -// 查找配置文件,获取指定配置文件的绝对路径,默认获取默认的配置文件路径; -// 当指定的配置文件不存在时,返回空字符串,并且不会报错。 +// GetFilePath returns the absolute path of the specified configuration file. +// If is not passed, it returns the configuration file path of the default name. +// If the specified configuration file does not exist, +// an empty string is returned. func (c *Config) GetFilePath(file...string) (path string) { name := c.name.Val() if len(file) > 0 { @@ -225,19 +230,20 @@ func (c *Config) GetFilePath(file...string) (path string) { return } -// 设置配置管理对象的默认文件名称 +// SetFileName sets the default configuration file name. func (c *Config) SetFileName(name string) { //glog.Debug("[gcfg] SetFileName:", name) c.name.Set(name) } -// 获取配置管理对象的默认文件名称 +// GetFileName returns the default configuration file name. func (c *Config) GetFileName() string { return c.name.Val() } -// 添加配置文件到配置管理器中,第二个参数为非必须,如果不输入表示添加进入默认的配置名称中 -// 内部带缓存控制功能。 +// getJson returns a gjson.Json object for the specified content. +// It would print error if file reading fails. +// If any error occurs, it return nil. func (c *Config) getJson(file...string) *gjson.Json { name := c.name.Val() if len(file) > 0 { @@ -255,7 +261,8 @@ func (c *Config) getJson(file...string) *gjson.Json { } if j, err := gjson.LoadContent(content); err == nil { j.SetViolenceCheck(c.vc.Val()) - // 添加配置文件监听,如果有任何变化,删除文件内容缓存,下一次查询会自动更新 + // Add monitor for this configuration file, + // any changes of this file will refresh its cache in Config object. if filePath != "" { gfsnotify.Add(filePath, func(event *gfsnotify.Event) { c.jsons.Remove(name) @@ -277,7 +284,6 @@ func (c *Config) getJson(file...string) *gjson.Json { return nil } -// 获取配置项,当不存在时返回nil func (c *Config) Get(pattern string, file...string) interface{} { if j := c.getJson(file...); j != nil { return j.Get(pattern) @@ -285,7 +291,6 @@ func (c *Config) Get(pattern string, file...string) interface{} { return nil } -// 获得配置项,返回动态变量 func (c *Config) GetVar(pattern string, file...string) gvar.VarRead { if j := c.getJson(file...); j != nil { return gvar.New(j.Get(pattern), true) @@ -293,7 +298,6 @@ func (c *Config) GetVar(pattern string, file...string) gvar.VarRead { return gvar.New(nil, true) } -// 判断指定的配置项是否存在 func (c *Config) Contains(pattern string, file...string) bool { if j := c.getJson(file...); j != nil { return j.Contains(pattern) @@ -301,8 +305,6 @@ func (c *Config) Contains(pattern string, file...string) bool { return false } -// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换 -// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil func (c *Config) GetMap(pattern string, file...string) map[string]interface{} { if j := c.getJson(file...); j != nil { return j.GetMap(pattern) @@ -310,8 +312,6 @@ func (c *Config) GetMap(pattern string, file...string) map[string]interface{} { return nil } -// 获得一个数组[]interface{},方便操作,不需要自己做类型转换 -// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil func (c *Config) GetArray(pattern string, file...string) []interface{} { if j := c.getJson(file...); j != nil { return j.GetArray(pattern) @@ -319,7 +319,6 @@ func (c *Config) GetArray(pattern string, file...string) []interface{} { return nil } -// 返回指定json中的string func (c *Config) GetString(pattern string, file...string) string { if j := c.getJson(file...); j != nil { return j.GetString(pattern) @@ -341,7 +340,6 @@ func (c *Config) GetInterfaces(pattern string, file...string) []interface{} { return nil } -// 返回指定json中的bool func (c *Config) GetBool(pattern string, file...string) bool { if j := c.getJson(file...); j != nil { return j.GetBool(pattern) @@ -349,7 +347,6 @@ func (c *Config) GetBool(pattern string, file...string) bool { return false } -// 返回指定json中的float32 func (c *Config) GetFloat32(pattern string, file...string) float32 { if j := c.getJson(file...); j != nil { return j.GetFloat32(pattern) @@ -357,7 +354,6 @@ func (c *Config) GetFloat32(pattern string, file...string) float32 { return 0 } -// 返回指定json中的float64 func (c *Config) GetFloat64(pattern string, file...string) float64 { if j := c.getJson(file...); j != nil { return j.GetFloat64(pattern) @@ -372,7 +368,6 @@ func (c *Config) GetFloats(pattern string, file...string) []float64 { return nil } -// 返回指定json中的float64->int func (c *Config) GetInt(pattern string, file...string) int { if j := c.getJson(file...); j != nil { return j.GetInt(pattern) @@ -416,7 +411,6 @@ func (c *Config) GetInts(pattern string, file...string) []int { return nil } -// 返回指定json中的float64->uint func (c *Config) GetUint(pattern string, file...string) uint { if j := c.getJson(file...); j != nil { return j.GetUint(pattern) diff --git a/geg/other/test.go b/geg/other/test.go index e4b2f208e..122344942 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,12 +2,34 @@ package main import ( "fmt" - "github.com/gogf/gf/g/container/gring" + "github.com/gogf/gf/g/database/gredis" + "github.com/gogf/gf/g/util/gconv" +) + +var ( + config = gredis.Config{ + Host : "127.0.0.1", + Port : 6379, + Db : 1, + } ) func main() { - r := gring.New(3) - r.Put(1) - r.Put(2) - fmt.Println(r.Val()) + group := "test" + gredis.SetConfig(config, group) + + redis := gredis.Instance(group) + defer redis.Close() + + _, err := redis.Do("SET", "k", "v") + if err != nil { + panic(err) + } + + r, err := redis.Do("GET", "k") + if err != nil { + panic(err) + } + fmt.Println(gconv.String(r)) + } \ No newline at end of file From c56c77d3a1480146032019c40701f4b7442fe006 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 5 Apr 2019 23:13:47 +0800 Subject: [PATCH 09/21] fix issue in G&j char format --- g/os/gtime/gtime_format.go | 7 ++++--- geg/other/test.go | 29 ++--------------------------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/g/os/gtime/gtime_format.go b/g/os/gtime/gtime_format.go index 711b17de4..41622c915 100644 --- a/g/os/gtime/gtime_format.go +++ b/g/os/gtime/gtime_format.go @@ -9,7 +9,8 @@ package gtime import ( "bytes" "github.com/gogf/gf/g/text/gregex" - "strings" + "github.com/gogf/gf/g/text/gstr" + "strings" ) var ( @@ -126,8 +127,8 @@ func (t *Time) Format(format string) string { result := t.Time.Format(f) // 有几个转换的符号需要特殊处理 switch runes[i] { - case 'j': buffer.WriteString(strings.Replace(result, "=j=0", "", -1)) - case 'G': buffer.WriteString(strings.Replace(result, "=G=0", "", -1)) + case 'j': buffer.WriteString(gstr.ReplaceByArray(result, []string{"=j=0", "", "=j=", ""})) + case 'G': buffer.WriteString(gstr.ReplaceByArray(result, []string{"=G=0", "", "=G=", ""})) case 'u': buffer.WriteString(strings.Replace(result, "=u=.", "", -1)) default: buffer.WriteString(result) diff --git a/geg/other/test.go b/geg/other/test.go index 122344942..ed997310b 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -2,34 +2,9 @@ package main import ( "fmt" - "github.com/gogf/gf/g/database/gredis" - "github.com/gogf/gf/g/util/gconv" -) - -var ( - config = gredis.Config{ - Host : "127.0.0.1", - Port : 6379, - Db : 1, - } + "github.com/gogf/gf/g/os/gtime" ) func main() { - group := "test" - gredis.SetConfig(config, group) - - redis := gredis.Instance(group) - defer redis.Close() - - _, err := redis.Do("SET", "k", "v") - if err != nil { - panic(err) - } - - r, err := redis.Do("GET", "k") - if err != nil { - panic(err) - } - fmt.Println(gconv.String(r)) - + fmt.Println(gtime.Now().Format(`Y-m-j G:i:su`)) } \ No newline at end of file From 68949b69bc9e9e60a4a131150539239d04715d69 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 5 Apr 2019 23:31:14 +0800 Subject: [PATCH 10/21] update example of gtime --- geg/os/gtime/gtime_strtotime.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/geg/os/gtime/gtime_strtotime.go b/geg/os/gtime/gtime_strtotime.go index 1a64692fe..26fbdc198 100644 --- a/geg/os/gtime/gtime_strtotime.go +++ b/geg/os/gtime/gtime_strtotime.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gtime" "time" ) @@ -19,15 +20,11 @@ func main() { "2018-02-09", "2017/12/14 04:51:34 +0805 LMT", "2018/02/09 12:00:15", - "18/02/09 12:16", - "18/02/09 12", - "18/02/09 +0805 LMT", "01/Nov/2018:13:28:13 +0800", "01-Nov-2018 11:50:28 +0805 LMT", "01-Nov-2018T15:04:05Z07:00", "01-Nov-2018T01:19:15+08:00", "01-Nov-2018 11:50:28 +0805 LMT", - "01/Nov/18 11:50:28", "01/Nov/2018 11:50:28", "01/Nov/2018:11:50:28", "01.Nov.2018:11:50:28", @@ -35,12 +32,12 @@ func main() { } cstLocal, _ := time.LoadLocation("Asia/Shanghai") for _, s := range array { - fmt.Println(s) if t, err := gtime.StrToTime(s); err == nil { - fmt.Println(t.String()) + fmt.Println(s) + fmt.Println(t.UTC().String()) fmt.Println(t.In(cstLocal).String()) } else { - panic(err) + glog.Error(s, err) } fmt.Println() } From d34273abff23320e5e54bb331d2bb89425aac622 Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Fri, 5 Apr 2019 23:53:43 +0800 Subject: [PATCH 11/21] =?UTF-8?q?=E5=90=8C=E6=AD=A5master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/os/gtime/gtime.go | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index d94b4b29b..e66e198ef 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -125,7 +125,6 @@ func Datetime() string { // 解析日期字符串(日期支持'-'或'/'或'.'连接符号) func parseDateStr(s string) (year, month, day int) { -<<<<<<< HEAD array := strings.Split(s, "-") if len(array) < 3 { array = strings.Split(s, "/") @@ -156,38 +155,6 @@ func parseDateStr(s string) (year, month, day int) { year = int(time.Now().Year()/100)*100 + year } return -======= - array := strings.Split(s, "-") - if len(array) < 3 { - array = strings.Split(s, "/") - } - if len(array) < 3 { - array = strings.Split(s, ".") - } - // 解析失败 - if len(array) < 3 { - return - } - // 判断年份在开头还是末尾 - if isNumeric(array[1]) { - year, _ = strconv.Atoi(array[0]) - month, _ = strconv.Atoi(array[1]) - day, _ = strconv.Atoi(array[2]) - } else { - if v, ok := monthMap[strings.ToLower(array[1])]; ok { - month = v - } else { - return - } - year, _ = strconv.Atoi(array[2]) - day, _ = strconv.Atoi(array[0]) - } - // 年是否为缩写,如果是,那么需要补上前缀 - if year < 100 { - year = int(time.Now().Year()/100)*100 + year - } - return ->>>>>>> 68949b69bc9e9e60a4a131150539239d04715d69 } // 字符串转换为时间对象,format参数指定格式的format(如: Y-m-d H:i:s),当指定format参数时效果同StrToTimeFormat方法。 From 61f57b48959200c69105fa97adacf0445d79bd53 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 6 Apr 2019 21:31:01 +0800 Subject: [PATCH 12/21] update comments of gstr --- g/text/gstr/gstr.go | 185 +++++++++---------------------- g/text/gstr/gstr_levenshtein.go | 4 +- g/text/gstr/gstr_parse.go | 3 +- g/text/gstr/gstr_pos.go | 28 ++--- g/text/gstr/gstr_similartext.go | 4 +- g/text/gstr/gstr_soundex.go | 4 +- g/text/gstr/gstr_trim.go | 20 +--- g/text/gstr/gstr_z_bench_test.go | 53 --------- 8 files changed, 72 insertions(+), 229 deletions(-) delete mode 100644 g/text/gstr/gstr_z_bench_test.go diff --git a/g/text/gstr/gstr.go b/g/text/gstr/gstr.go index 4e56d10d8..fc03ce3bc 100644 --- a/g/text/gstr/gstr.go +++ b/g/text/gstr/gstr.go @@ -5,8 +5,6 @@ // You can obtain one at https://github.com/gogf/gf. // Package gstr provides functions for string handling. -// -// 字符串处理. package gstr import ( @@ -20,9 +18,8 @@ import ( "unicode/utf8" ) -// Replace returns a copy of the string with string replaced by . -// -// 字符串替换(大小写敏感) +// Replace returns a copy of the string +// in which string replaced by case-sensitively. func Replace(origin, search, replace string, count...int) string { n := -1 if len(count) > 0 { @@ -31,10 +28,8 @@ func Replace(origin, search, replace string, count...int) string { return strings.Replace(origin, search, replace, n) } -// Replace returns a copy of the string with string replaced by -// with case-insensitive. -// -// 字符串替换(大小写不敏感) +// Replace returns a copy of the string +// in which string replaced by case-insensitively. func ReplaceI(origin, search, replace string, count...int) string { n := -1 if len(count) > 0 { @@ -59,23 +54,20 @@ func ReplaceI(origin, search, replace string, count...int) string { return origin } -// Count counts the number of appears in . It returns 0 if no found in . -// -// 计算字符串substr在字符串s中出现的次数,如果没有在s中找到substr,那么返回0。 +// Count counts the number of appears in . +// It returns 0 if no found in . func Count(s, substr string) int { return strings.Count(s, substr) } -// Count counts the number of appears in , case-insensitive. It returns 0 if no found in . -// -// (非大小写敏感)计算字符串substr在字符串s中出现的次数,如果没有在s中找到substr,那么返回0。 +// Count counts the number of appears in , case-insensitively. +// It returns 0 if no found in . func CountI(s, substr string) int { return strings.Count(ToLower(s), ToLower(substr)) } -// Replace string by array/slice. -// -// 使用map进行字符串替换(大小写敏感) +// ReplaceByArray returns a copy of , +// which is replaced by a slice in order, case-sensitively. func ReplaceByArray(origin string, array []string) string { for i := 0; i < len(array); i += 2 { if i + 1 >= len(array) { @@ -86,9 +78,8 @@ func ReplaceByArray(origin string, array []string) string { return origin } -// Replace string by array/slice with case-insensitive. -// -// 使用map进行字符串替换(大小写不敏感) +// ReplaceIByArray returns a copy of , +// which is replaced by a slice in order, case-insensitively. func ReplaceIByArray(origin string, array []string) string { for i := 0; i < len(array); i += 2 { if i + 1 >= len(array) { @@ -99,9 +90,8 @@ func ReplaceIByArray(origin string, array []string) string { return origin } -// Replace string by map. -// -// 使用map进行字符串替换(大小写敏感) +// ReplaceByMap returns a copy of , +// which is replaced by a map in unordered way, case-sensitively. func ReplaceByMap(origin string, replaces map[string]string) string { for k, v := range replaces { origin = Replace(origin, k, v) @@ -109,9 +99,8 @@ func ReplaceByMap(origin string, replaces map[string]string) string { return origin } -// Replace string by map with case-insensitive. -// -// 使用map进行字符串替换(大小写不敏感) +// ReplaceIByMap returns a copy of , +// which is replaced by a map in unordered way, case-insensitively. func ReplaceIByMap(origin string, replaces map[string]string) string { for k, v := range replaces { origin = ReplaceI(origin, k, v) @@ -120,21 +109,16 @@ func ReplaceIByMap(origin string, replaces map[string]string) string { } // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. -// 字符串转换为小写 func ToLower(s string) string { return strings.ToLower(s) } // ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case. -// -// 字符串转换为大写 func ToUpper(s string) string { return strings.ToUpper(s) } // UcFirst returns a copy of the string s with the first letter mapped to its upper case. -// -// 字符串首字母转换为大写 func UcFirst(s string) string { if len(s) == 0 { return s @@ -146,8 +130,6 @@ func UcFirst(s string) string { } // LcFirst returns a copy of the string s with the first letter mapped to its lower case. -// -// 字符串首字母转换为小写 func LcFirst(s string) string { if len(s) == 0 { return s @@ -158,16 +140,12 @@ func LcFirst(s string) string { return s } -// Uppercase the first character of each word in a string. -// -// 大写字符串中每个单词的第一个字符。 +// UcWords uppercase the first character of each word in a string. func UcWords(str string) string { return strings.Title(str) } // IsLetterLower tests whether the given byte b is in lower case. -// -// 判断给定字符是否小写 func IsLetterLower(b byte) bool { if b >= byte('a') && b <= byte('z') { return true @@ -176,8 +154,6 @@ func IsLetterLower(b byte) bool { } // IsLetterUpper tests whether the given byte b is in upper case. -// -// 判断给定字符是否大写 func IsLetterUpper(b byte) bool { if b >= byte('A') && b <= byte('Z') { return true @@ -186,8 +162,6 @@ func IsLetterUpper(b byte) bool { } // IsNumeric tests whether the given string s is numeric. -// -// 判断锁给字符串是否为数字. func IsNumeric(s string) bool { length := len(s) if length == 0 { @@ -201,9 +175,7 @@ func IsNumeric(s string) bool { return true } -// Returns the portion of string specified by the start and length parameters. -// -// 字符串截取,支持中文 +// SubStr returns a portion of string specified by the and parameters. func SubStr(str string, start int, length...int) (substr string) { // 将字符串的转换成[]rune rs := []rune(str) @@ -229,12 +201,10 @@ func SubStr(str string, start int, length...int) (substr string) { return string(rs[start : end]) } -// Returns the portion of string specified by the parameters, -// if the length of str is greater than , -// then the will be appended to the result. -// -// 字符串长度截取限制,超过长度限制被截取并在字符串末尾追加指定的内容,支持中文 -func StrLimit(str string, length int, suffix...string) (string) { +// StrLimit returns a portion of string specified by parameters, +// if the length of is greater than , +// then the will be appended to the result string. +func StrLimit(str string, length int, suffix...string) string { rs := []rune(str) if len(str) < length { return str @@ -246,9 +216,7 @@ func StrLimit(str string, length int, suffix...string) (string) { return string(rs[0 : length]) + addStr } -// Reverse a string. -// -// 字符串反转. +// Reverse returns a string which is the reverse of . func Reverse(str string) string { runes := []rune(str) for i, j := 0, len(runes) - 1; i < j; i, j = i + 1, j - 1 { @@ -257,13 +225,11 @@ func Reverse(str string) string { return string(runes) } -// Format a number with grouped thousands. -// decimals: Sets the number of decimal points. -// decPoint: Sets the separator for the decimal point. -// thousandsSep: Sets the thousands separator. +// NumberFormat formats a number with grouped thousands. +// : Sets the number of decimal points. +// : Sets the separator for the decimal point. +// : Sets the thousands separator. // See http://php.net/manual/en/function.number-format.php. -// -// 以千位分隔符方式格式化一个数字. func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string { neg := false if number < 0 { @@ -305,14 +271,10 @@ func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) s return s } -// Split a string into smaller chunks. +// ChunkSplit splits a string into smaller chunks. // Can be used to split a string into smaller chunks which is useful for // e.g. converting BASE64 string output to match RFC 2045 semantics. // It inserts end every chunkLen characters. -// -// 将字符串分割成小块。使用此函数将字符串分割成小块非常有用。 -// 例如将BASE64的输出转换成符合RFC2045语义的字符串。 -// 它会在每 chunkLen 个字符后边插入 end。 func ChunkSplit(body string, chunkLen int, end string) string { if end == "" { end = "\r\n" @@ -336,51 +298,37 @@ func ChunkSplit(body string, chunkLen int, end string) string { // Compare returns an integer comparing two strings lexicographically. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b. -// -// 比较两个字符串。 func Compare(a, b string) int { return strings.Compare(a, b) } -// Equal reports whether s and t, interpreted as UTF-8 strings, -// are equal under Unicode case-folding, case-insensitive. -// -// 比较两个字符串是否相等(不区分大小写)。 +// Equal reports whether and , interpreted as UTF-8 strings, +// are equal under Unicode case-folding, case-insensitively. func Equal(a, b string) bool { return strings.EqualFold(a, b) } -// Return the words used in a string. -// -// 分割字符串中的单词。 +// Fields returns the words used in a string as slice. func Fields(str string) []string { return strings.Fields(str) } -// Contains reports whether substr is within str. -// -// 判断是否substr存在于str中。 +// Contains reports whether is within , case-sensitively. func Contains(str, substr string) bool { return strings.Contains(str, substr) } -// Contains reports whether substr is within str, case-insensitive. -// -// 判断是否substr存在于str中(不区分大小写)。 +// ContainsI reports whether substr is within str, case-insensitively. func ContainsI(str, substr string) bool { return PosI(str, substr) != -1 } -// ContainsAny reports whether any Unicode code points in chars are within s. -// -// 判断是否s中是否包含chars指定的任意字符。 +// ContainsAny reports whether any Unicode code points in are within . func ContainsAny(s, chars string) bool { return strings.ContainsAny(s, chars) } -// Return information about words used in a string. -// -// 返回字符串中单词的使用情况。 +// CountWords returns information about words' count used in a string. func CountWords(str string) map[string]int { m := make(map[string]int) buffer := bytes.NewBuffer(nil) @@ -400,7 +348,7 @@ func CountWords(str string) map[string]int { return m } -// Return information about words used in a string. +// CountChars returns information about chars' count used in a string. // // 返回字符串中字符的使用情况。 func CountChars(str string, noSpace...bool) map[string]int { @@ -418,10 +366,8 @@ func CountChars(str string, noSpace...bool) map[string]int { return m } -// Wraps a string to a given number of characters. +// WordWrap wraps a string to a given number of characters. // TODO: Enable cut param, see http://php.net/manual/en/function.wordwrap.php. -// -// 使用字符串断点将字符串打断为指定数量的字串。 func WordWrap(str string, width int, br string) string { if br == "" { br = "\n" @@ -479,24 +425,19 @@ func WordWrap(str string, width int, br string) string { return buf.String() } -// Get string length of unicode. -// -// UTF-8字符串长度。 +// RuneLen returns string length of unicode. func RuneLen(str string) int { return utf8.RuneCountInString(str) } // Repeat returns a new string consisting of multiplier copies of the string input. -// -// 按照指定大小创建重复的字符串。 func Repeat(input string, multiplier int) string { return strings.Repeat(input, multiplier) } -// Returns part of haystack string starting from and including the first occurrence of needle to the end of haystack. +// Str returns part of string starting from and including +// the first occurrence of to the end of . // See http://php.net/manual/en/function.strstr.php. -// -// 查找字符串的首次出现。返回 haystack 字符串从 needle 第一次出现的位置开始到 haystack 结尾的字符串。 func Str(haystack string, needle string) string { if needle == "" { return "" @@ -508,9 +449,7 @@ func Str(haystack string, needle string) string { return haystack[idx + len([]byte(needle)) - 1 : ] } -// Randomly shuffles a string. -// -// 将字符串打乱。 +// Shuffle randomly shuffles a string. func Shuffle(str string) string { runes := []rune(str) s := make([]rune, len(runes)) @@ -520,54 +459,40 @@ func Shuffle(str string) string { return string(s) } -// Split a string by a string, to an array. -// -// 此函数返回由字符串组成的数组,每个元素都是 str 的一个子串,它们被字符串 delimiter 作为边界点分割出来。 +// Split splits string by a string , to an array. func Split(str, delimiter string) []string { return strings.Split(str, delimiter) } // Join concatenates the elements of a to create a single string. The separator string // sep is placed between elements in the resulting string. -// -// 用sep将字符串数组array连接为一个字符串。 func Join(array []string, sep string) string { return strings.Join(array, sep) } -// Split a string by a string, to an array. +// Explode splits string by a string , to an array. // See http://php.net/manual/en/function.explode.php. -// -// 此函数返回由字符串组成的数组,每个元素都是 str 的一个子串,它们被字符串 delimiter 作为边界点分割出来。 func Explode(delimiter, str string) []string { return Split(str, delimiter) } -// Join array elements with a string. +// Implode joins array elements with a string . // http://php.net/manual/en/function.implode.php -// -// 用glue将字符串数组pieces连接为一个字符串。 func Implode(glue string, pieces []string) string { return strings.Join(pieces, glue) } -// Generate a single-byte string from a number. -// -// 返回相对应于 ascii 所指定的单个字符。 +// Chr return the ascii string of a number(0-255). func Chr(ascii int) string { return string(ascii) } -// Convert the first byte of a string to a value between 0 and 255. -// -// 解析 char 二进制值第一个字节为 0 到 255 范围的无符号整型类型。 +// Ord converts the first byte of a string to a value between 0 and 255. func Ord(char string) int { return int(char[0]) } -// HideStr replaces part of the the string by percentage from the middle. -// -// 按照百分比从字符串中间向两边隐藏字符(主要用于姓名、手机号、邮箱地址、身份证号等的隐藏),支持utf-8中文,支持email格式。 +// HideStr replaces part of the the string to by from the . func HideStr(str string, percent int, hide string) string { array := strings.Split(str, "@") if len(array) > 1 { @@ -593,10 +518,8 @@ func HideStr(str string, percent int, hide string) string { return buffer.String() } -// Inserts HTML line breaks before all newlines in a string. -// \n\r, \r\n, \r, \n -// -// 在字符串 string 所有新行之前插入 '
' 或 '
',并返回。 +// Nl2Br inserts HTML line breaks(
|
) before all newlines in a string: +// \n\r, \r\n, \r, \n. func Nl2Br(str string, isXhtml...bool) string { r, n, runes := '\r', '\n', []rune(str) var br []byte @@ -628,9 +551,7 @@ func Nl2Br(str string, isXhtml...bool) string { return buf.String() } -// Quote string with slashes. -// -// 转义字符串中的单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)。 +// AddSlashes quotes chars('"\) with slashes. func AddSlashes(str string) string { var buf bytes.Buffer for _, char := range str { @@ -643,9 +564,7 @@ func AddSlashes(str string) string { return buf.String() } -// Un-quotes a quoted string. -// -// 反转义字符串。 +// StripSlashes un-quotes a quoted string by AddSlashes. func StripSlashes(str string) string { var buf bytes.Buffer l, skip := len(str), false @@ -665,8 +584,6 @@ func StripSlashes(str string) string { // Returns a version of str with a backslash character (\) before every character that is among: // .\+*?[^]($) -// -// 转义字符串,转义的特殊字符包括:.\+*?[^]($)。 func QuoteMeta(str string) string { var buf bytes.Buffer for _, char := range str { diff --git a/g/text/gstr/gstr_levenshtein.go b/g/text/gstr/gstr_levenshtein.go index e69e4b652..c88c5b2dc 100644 --- a/g/text/gstr/gstr_levenshtein.go +++ b/g/text/gstr/gstr_levenshtein.go @@ -6,13 +6,11 @@ package gstr -// Calculate Levenshtein distance between two strings. +// Levenshtein calculates Levenshtein distance between two strings. // costIns: Defines the cost of insertion. // costRep: Defines the cost of replacement. // costDel: Defines the cost of deletion. // See http://php.net/manual/en/function.levenshtein.php. -// -// 计算两个字符串之间的编辑距离(Levenshtein distance)。 func Levenshtein(str1, str2 string, costIns, costRep, costDel int) int { var maxLen = 255 l1 := len(str1) diff --git a/g/text/gstr/gstr_parse.go b/g/text/gstr/gstr_parse.go index 951055dfe..1ead33a78 100644 --- a/g/text/gstr/gstr_parse.go +++ b/g/text/gstr/gstr_parse.go @@ -12,7 +12,7 @@ import ( "strings" ) -// Parses the string into map[string]interface{}. +// Parse parses the string into map[string]interface{}. // // f1=m&f2=n -> map[f1:m f2:n] // f[a]=m&f[b]=n -> map[f:map[a:m b:n]] @@ -23,7 +23,6 @@ import ( // f=m&f[a]=n -> error // a .[[b=c -> map[a___[b:c] // -// 将字符串解析成Map。 func Parse(s string) (result map[string]interface{}, err error) { result = make(map[string]interface{}) parts := strings.Split(s, "&") diff --git a/g/text/gstr/gstr_pos.go b/g/text/gstr/gstr_pos.go index 1225e5332..db57c6e63 100644 --- a/g/text/gstr/gstr_pos.go +++ b/g/text/gstr/gstr_pos.go @@ -8,10 +8,9 @@ package gstr import "strings" -// Find the position of the first occurrence of a substring in a string. -// It returns -1, if none found. -// -// 返回 needle 在 haystack 中首次出现的数字位置,找不到返回-1。 +// Pos returns the position of the first occurrence of +// in from , case-sensitively. +// It returns -1, if not found. func Pos(haystack, needle string, startOffset...int) int { length := len(haystack) offset := 0 @@ -32,10 +31,9 @@ func Pos(haystack, needle string, startOffset...int) int { return pos + offset } -// Find the position of the first occurrence of a case-insensitive substring in a string. -// It returns -1, if none found. -// -// 返回在字符串 haystack 中 needle 首次出现的数字位置(不区分大小写),找不到返回-1。 +// PosI returns the position of the first occurrence of +// in from , case-insensitively. +// It returns -1, if not found. func PosI(haystack, needle string, startOffset...int) int { length := len(haystack) offset := 0 @@ -56,10 +54,9 @@ func PosI(haystack, needle string, startOffset...int) int { return pos + offset } -// Find the position of the last occurrence of a substring in a string. -// It returns -1, if none found. -// -// 查找指定字符串在目标字符串中最后一次出现的位置,找不到返回-1。 +// PosR returns the position of the last occurrence of +// in from , case-sensitively. +// It returns -1, if not found. func PosR(haystack, needle string, startOffset...int) int { offset := 0 if len(startOffset) > 0 { @@ -82,10 +79,9 @@ func PosR(haystack, needle string, startOffset...int) int { return pos } -// Find the position of the last occurrence of a case-insensitive substring in a string. -// It returns -1, if none found. -// -// 以不区分大小写的方式查找指定字符串在目标字符串中最后一次出现的位置,找不到返回-1。 +// PosR returns the position of the last occurrence of +// in from , case-insensitively. +// It returns -1, if not found. func PosRI(haystack, needle string, startOffset...int) int { offset := 0 if len(startOffset) > 0 { diff --git a/g/text/gstr/gstr_similartext.go b/g/text/gstr/gstr_similartext.go index e8aead20c..9af324c4f 100644 --- a/g/text/gstr/gstr_similartext.go +++ b/g/text/gstr/gstr_similartext.go @@ -6,10 +6,8 @@ package gstr -// Calculate the similarity between two strings. +// SimilarText calculates the similarity between two strings. // See http://php.net/manual/en/function.similar-text.php. -// -// 计算两个字符串的相似度。 func SimilarText(first, second string, percent *float64) int { var similarText func(string, string, int, int) int similarText = func(str1, str2 string, len1, len2 int) int { diff --git a/g/text/gstr/gstr_soundex.go b/g/text/gstr/gstr_soundex.go index 82cf81d57..dfcf8ad62 100644 --- a/g/text/gstr/gstr_soundex.go +++ b/g/text/gstr/gstr_soundex.go @@ -6,10 +6,8 @@ package gstr -// Calculate the soundex key of a string. +// Soundex calculates the soundex key of a string. // See http://php.net/manual/en/function.soundex.php. -// -// 计算字符串的SOUNDEX值,SOUNDEX为由四个字符组成的代码以评估两个字符串的相似性。 func Soundex(str string) string { if str == "" { panic("str: cannot be an empty string") diff --git a/g/text/gstr/gstr_trim.go b/g/text/gstr/gstr_trim.go index 833280f1c..9902e83d6 100644 --- a/g/text/gstr/gstr_trim.go +++ b/g/text/gstr/gstr_trim.go @@ -8,9 +8,7 @@ package gstr import "strings" -// Strip whitespace (or other characters) from the beginning and end of a string. -// -// 去除字符串首尾处的空白字符(或者其他字符)。 +// Trim strips whitespace (or other characters) from the beginning and end of a string. func Trim(str string, characterMask ...string) string { if len(characterMask) > 0 { return strings.Trim(str, characterMask[0]) @@ -19,9 +17,7 @@ func Trim(str string, characterMask ...string) string { } } -// Strip whitespace (or other characters) from the beginning of a string. -// -// 去除字符串首的空白字符(或者其他字符)。 +// TrimLeft strips whitespace (or other characters) from the beginning of a string. func TrimLeft(str string, characterMask ...string) string { mask := "" if len(characterMask) == 0 { @@ -32,9 +28,7 @@ func TrimLeft(str string, characterMask ...string) string { return strings.TrimLeft(str, mask) } -// Strip all of the given string from the beginning of a string. -// -// 去除字符串首的给定字符串。 +// TrimLeftStr strips all of the given string from the beginning of a string. func TrimLeftStr(str string, cut string) string { for str[0 : len(cut)] == cut { str = str[len(cut) : ] @@ -42,9 +36,7 @@ func TrimLeftStr(str string, cut string) string { return str } -// Strip whitespace (or other characters) from the end of a string. -// -// 去除字符串尾的空白字符(或者其他字符)。 +// TrimRight strips whitespace (or other characters) from the end of a string. func TrimRight(str string, characterMask ...string) string { mask := "" if len(characterMask) == 0 { @@ -55,9 +47,7 @@ func TrimRight(str string, characterMask ...string) string { return strings.TrimRight(str, mask) } -// Strip all of the given string from the end of a string. -// -// 去除字符串尾的给定字符串。 +// TrimRightStr strips all of the given string from the end of a string. func TrimRightStr(str string, cut string) string { for { length := len(str) diff --git a/g/text/gstr/gstr_z_bench_test.go b/g/text/gstr/gstr_z_bench_test.go deleted file mode 100644 index 404ce372f..000000000 --- a/g/text/gstr/gstr_z_bench_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). 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, -// You can obtain one at https://github.com/gogf/gf. - -// go test *.go -bench=".*" - -package gstr_test - -import ( - "github.com/gogf/gf/g/text/gstr" - "testing" -) - -var ( - str = "This is the test string for gstr." - bytes = []byte(str) -) - -func Benchmark_StringToBytes(b *testing.B) { - for i := 0; i < b.N; i++ { - if []byte(str) != nil { - - } - } -} - -func Benchmark_BytesToString(b *testing.B) { - for i := 0; i < b.N; i++ { - if string(bytes) != "" { - - } - } -} - -func Benchmark_Parse1(b *testing.B) { - for i := 0; i < b.N; i++ { - gstr.Parse("a=1&b=2") - } -} - -func Benchmark_Parse2(b *testing.B) { - for i := 0; i < b.N; i++ { - gstr.Parse("m[]=1&m[]=2") - } -} - -func Benchmark_Parse3(b *testing.B) { - for i := 0; i < b.N; i++ { - gstr.Parse("m[a1][b1][c1][d1]=1&m[a2][b2]=2&m[a3][b3][c3]=3") - } -} \ No newline at end of file From 2107061a46181e51a33b79630b0d7ebfcc71a444 Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Sat, 6 Apr 2019 22:48:47 +0800 Subject: [PATCH 13/21] Add gtime unit test --- g/os/gtime/gtime_z_bench_test.go | 68 ++++--- g/os/gtime/gtime_z_unit_basic_test.go | 238 +++++++++++++++++++++++++ g/os/gtime/gtime_z_unit_format_test.go | 65 +++++++ g/os/gtime/gtime_z_unit_time_test.go | 189 ++++++++++++++++++++ 4 files changed, 536 insertions(+), 24 deletions(-) create mode 100644 g/os/gtime/gtime_z_unit_basic_test.go create mode 100644 g/os/gtime/gtime_z_unit_format_test.go create mode 100644 g/os/gtime/gtime_z_unit_time_test.go diff --git a/g/os/gtime/gtime_z_bench_test.go b/g/os/gtime/gtime_z_bench_test.go index 4375df43d..1b6e60c54 100644 --- a/g/os/gtime/gtime_z_bench_test.go +++ b/g/os/gtime/gtime_z_bench_test.go @@ -4,50 +4,70 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gtime +package gtime_test import ( - "testing" + "testing" + + "github.com/gogf/gf/g/os/gtime" ) func Benchmark_Second(b *testing.B) { - for i := 0; i < b.N; i++ { - Second() - } + for i := 0; i < b.N; i++ { + gtime.Second() + } } func Benchmark_Millisecond(b *testing.B) { - for i := 0; i < b.N; i++ { - Millisecond() - } + for i := 0; i < b.N; i++ { + gtime.Millisecond() + } } func Benchmark_Microsecond(b *testing.B) { - for i := 0; i < b.N; i++ { - Microsecond() - } + for i := 0; i < b.N; i++ { + gtime.Microsecond() + } } func Benchmark_Nanosecond(b *testing.B) { - for i := 0; i < b.N; i++ { - Nanosecond() - } + for i := 0; i < b.N; i++ { + gtime.Nanosecond() + } } func Benchmark_StrToTime(b *testing.B) { - for i := 0; i < b.N; i++ { - StrToTime("2018-02-09T20:46:17.897Z") - } + for i := 0; i < b.N; i++ { + gtime.StrToTime("2018-02-09T20:46:17.897Z") + } } func Benchmark_ParseTimeFromContent(b *testing.B) { - for i := 0; i < b.N; i++ { - ParseTimeFromContent("2018-02-09T20:46:17.897Z") - } + for i := 0; i < b.N; i++ { + gtime.ParseTimeFromContent("2018-02-09T20:46:17.897Z") + } } func Benchmark_NewFromTimeStamp(b *testing.B) { - for i := 0; i < b.N; i++ { - NewFromTimeStamp(1542674930) - } -} \ No newline at end of file + for i := 0; i < b.N; i++ { + gtime.NewFromTimeStamp(1542674930) + } +} + +func Benchmark_Date(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.Date() + } +} + +func Benchmark_Datetime(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.Datetime() + } +} + +func Benchmark_SetTimeZone(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.SetTimeZone("Asia/Shanghai") + } +} diff --git a/g/os/gtime/gtime_z_unit_basic_test.go b/g/os/gtime/gtime_z_unit_basic_test.go new file mode 100644 index 000000000..9499b3ac5 --- /dev/null +++ b/g/os/gtime/gtime_z_unit_basic_test.go @@ -0,0 +1,238 @@ +package gtime_test + +import ( + "testing" + "time" + + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" +) + +func Test_SetTimeZone(t *testing.T) { + gtest.Case(t, func() { + gtime.SetTimeZone("Asia/Shanghai") + gtest.Assert(time.Local.String(), "Asia/Shanghai") + }) +} + +func Test_Nanosecond(t *testing.T) { + gtest.Case(t, func() { + nanos := gtime.Nanosecond() + timeTemp := time.Unix(0, nanos) + gtest.Assert(nanos, timeTemp.UnixNano()) + }) +} + +func Test_Microsecond(t *testing.T) { + gtest.Case(t, func() { + micros := gtime.Microsecond() + timeTemp := time.Unix(0, micros*1e3) + gtest.Assert(micros, timeTemp.UnixNano()/1e3) + }) +} + +func Test_Millisecond(t *testing.T) { + gtest.Case(t, func() { + millis := gtime.Millisecond() + timeTemp := time.Unix(0, millis*1e6) + gtest.Assert(millis, timeTemp.UnixNano()/1e6) + }) +} + +func Test_Second(t *testing.T) { + gtest.Case(t, func() { + s := gtime.Second() + timeTemp := time.Unix(s, 0) + gtest.Assert(s, timeTemp.Unix()) + }) +} + +func Test_Date(t *testing.T) { + gtest.Case(t, func() { + gtest.Assert(gtime.Date(), time.Now().Format("2006-01-02")) + }) +} + +func Test_Datetime(t *testing.T) { + gtest.Case(t, func() { + datetime := gtime.Datetime() + timeTemp, err := gtime.StrToTime(datetime, "Y-m-d H:i:s") + if err != nil { + t.Error("test fail") + } + gtest.Assert(datetime, timeTemp.Time.Format("2006-01-02 15:04:05")) + }) +} + +/* +func Test_parseDateStr(t *testing.T) { + gtest.Case(t, func() { + //正常日期列表 + var testDates = []string{ + "2006-01-02", + "2006/01/02", + "2006.01.02", + "06.01.02", + "02.jan.2006", + } + + for _, item := range testDates { + year, month, day := parseDateStr(item) + gtest.Assert(year, 2006) + gtest.Assert(month, 1) + gtest.Assert(day, 2) + } + + //异常日期列表 + var testDatesFail = []string{ + "2006.01", + "06..02", + } + + for _, item := range testDatesFail { + year, month, day := parseDateStr(item) + gtest.Assert(year, 0) + gtest.Assert(month, 0) + gtest.Assert(day, 0) + } + + }) +} +*/ +// +func Test_ConvertZone(t *testing.T) { + gtest.Case(t, func() { + //现行时间 + nowUTC := time.Now().UTC() + testZone := "America/Los_Angeles" + + //转换为洛杉矶时间 + t1, err := gtime.ConvertZone(nowUTC.Format("2006-01-02 15:04:05"), testZone, "") + if err != nil { + t.Error("test fail") + } + + //使用洛杉矶时区解析上面转换后的时间 + laStr := t1.Time.Format("2006-01-02 15:04:05") + loc, err := time.LoadLocation(testZone) + t2, err := time.ParseInLocation("2006-01-02 15:04:05", laStr, loc) + + //判断是否与现行时间匹配 + gtest.Assert(t2.UTC().Unix(), nowUTC.Unix()) + + }) + + //test err + gtest.Case(t, func() { + //现行时间 + nowUTC := time.Now().UTC() + //t.Log(nowUTC.Unix()) + testZone := "errZone" + + //错误时间输入 + _, err := gtime.ConvertZone(nowUTC.Format("06..02 15:04:05"), testZone, "") + if err == nil { + t.Error("test fail") + } + //错误时区输入 + _, err = gtime.ConvertZone(nowUTC.Format("2006-01-02 15:04:05"), testZone, "") + if err == nil { + t.Error("test fail") + } + //错误时区输入 + _, err = gtime.ConvertZone(nowUTC.Format("2006-01-02 15:04:05"), testZone, testZone) + if err == nil { + t.Error("test fail") + } + }) +} + +func Test_StrToTime(t *testing.T) { + gtest.Case(t, func() { + //正常日期列表 + var testDatetimes = []string{ + "2006-01-02 15:04:05", + "2006/01/02 15:04:05", + "2006.01.02 15:04:05.000", + "2006.01.02 - 15:04:05", + "2006.01.02 15:04:05 +0800 CST", + "2006-01-02T20:05:06+05:01:01", + "2006-01-02T14:03:04Z01:01:01", + "2006-01-02T15:04:05Z", + "02-jan-2006 15:04:05", + "02/jan/2006 15:04:05", + "02.jan.2006 15:04:05", + "02.jan.2006:15:04:05", + } + + for _, item := range testDatetimes { + timeTemp, err := gtime.StrToTime(item) + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") + } + + //正常日期列表,时间00:00:00 + var testDates = []string{ + "2006.01.02", + "2006.01.02 00:00", + "2006.01.02 00:00:00.000", + } + + for _, item := range testDates { + timeTemp, err := gtime.StrToTime(item) + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 00:00:00") + } + + //异常日期列表 + var testDatesFail = []string{ + "2006.01", + "06..02", + "20060102", + } + + for _, item := range testDatesFail { + _, err := gtime.StrToTime(item) + if err == nil { + t.Error("test fail") + } + } + + //test err + _, err := gtime.StrToTime("2006-01-02 15:04:05", "aabbccdd") + if err == nil { + t.Error("test fail") + } + }) +} + +func Test_ParseTimeFromContent(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.ParseTimeFromContent("我是中文2006-01-02 15:04:05我也是中文", "Y-m-d H:i:s") + gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") + + timeTemp1 := gtime.ParseTimeFromContent("我是中文2006-01-02 15:04:05我也是中文") + gtest.Assert(timeTemp1.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") + + timeTemp2 := gtime.ParseTimeFromContent("我是中文02.jan.2006 15:04:05我也是中文") + gtest.Assert(timeTemp2.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") + + //test err + timeTempErr := gtime.ParseTimeFromContent("我是中文", "Y-m-d H:i:s") + if timeTempErr != nil { + t.Error("test fail") + } + }) +} + +func Test_FuncCost(t *testing.T) { + gtest.Case(t, func() { + gtime.FuncCost(func() { + + }) + }) +} diff --git a/g/os/gtime/gtime_z_unit_format_test.go b/g/os/gtime/gtime_z_unit_format_test.go new file mode 100644 index 000000000..09f5e9cb3 --- /dev/null +++ b/g/os/gtime/gtime_z_unit_format_test.go @@ -0,0 +1,65 @@ +package gtime_test + +import ( + "testing" + + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" +) + +/* +func Test_formatToStdLayout(t *testing.T) { + gtest.Case(t, func() { + gtest.Assert(formatToStdLayout("Y-m-d H:i:s"), "2006-01-02 15:04:05") + gtest.Assert(formatToStdLayout("\\T\\i\\m\\e Y-m-d H:i:s"), "Time 2006-01-02 15:04:05") + gtest.Assert(formatToStdLayout("Y-m-d H:i:s\\"), "2006-01-02 15:04:05") + gtest.Assert(formatToStdLayout("Y-m-j G:i:s.u"), "2006-01-02 15:04:05.000") + gtest.Assert(formatToStdLayout("Y-m-j G:i:su"), "2006-01-02 15:04:05.000") + }) +} +*/ +func Test_Format(t *testing.T) { + gtest.Case(t, func() { + timeTemp, err := gtime.StrToTime("2006-01-11 15:04:05", "Y-m-d H:i:s") + timeTemp.ToZone("Asia/Shanghai") + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp.Format("\\T\\i\\m\\e中文Y-m-j G:i:s.u\\"), "Time中文2006-01-11 15:04:05.000") + + gtest.Assert(timeTemp.Format("d D j l"), "11 Wed 11 Wednesday") + + gtest.Assert(timeTemp.Format("F m M n"), "January 01 Jan 1") + + gtest.Assert(timeTemp.Format("Y y"), "2006 06") + + gtest.Assert(timeTemp.Format("a A g G h H i s u .u"), "pm PM 3 15 03 15 04 05 000 .000") + + gtest.Assert(timeTemp.Format("O P T"), "+0800 +08:00 CST") + + gtest.Assert(timeTemp.Format("r"), "Wed, 11 Jan 06 15:04 CST") + + gtest.Assert(timeTemp.Format("c"), "2006-01-11T15:04:05+08:00") + + //补零 + timeTemp1, err := gtime.StrToTime("2006-01-02 03:04:05", "Y-m-d H:i:s") + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp1.Format("Y-m-d h:i:s"), "2006-01-02 03:04:05") + //不补零 + timeTemp2, err := gtime.StrToTime("2006-01-02 03:04:05", "Y-m-d H:i:s") + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp2.Format("Y-n-j G:i:s"), "2006-1-2 3:04:05") + + }) +} + +func Test_Layout(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.Layout("2006-01-02 15:04:05"), timeTemp.Time.Format("2006-01-02 15:04:05")) + }) +} diff --git a/g/os/gtime/gtime_z_unit_time_test.go b/g/os/gtime/gtime_z_unit_time_test.go new file mode 100644 index 000000000..22dbe5e46 --- /dev/null +++ b/g/os/gtime/gtime_z_unit_time_test.go @@ -0,0 +1,189 @@ +package gtime_test + +import ( + "testing" + "time" + + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" +) + +func Test_New(t *testing.T) { + gtest.Case(t, func() { + timeNow := time.Now() + timeTemp := gtime.New(timeNow) + gtest.Assert(timeTemp.Time.UnixNano(), timeNow.UnixNano()) + + timeTemp1 := gtime.New() + gtest.Assert(timeTemp1.Time, time.Time{}) + }) +} + +func Test_NewFromStr(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.NewFromStr("2006-01-02 15:04:05") + gtest.Assert(timeTemp.Format("Y-m-d H:i:s"), "2006-01-02 15:04:05") + + timeTemp1 := gtime.NewFromStr("20060102") + if timeTemp1 != nil { + t.Error("test fail") + } + }) +} + +func Test_NewFromStrFormat(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.NewFromStrFormat("2006-01-02 15:04:05", "Y-m-d H:i:s") + gtest.Assert(timeTemp.Format("Y-m-d H:i:s"), "2006-01-02 15:04:05") + + timeTemp1 := gtime.NewFromStrFormat("2006-01-02 15:04:05", "aabbcc") + if timeTemp1 != nil { + t.Error("test fail") + } + }) +} + +func Test_NewFromStrLayout(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.NewFromStrLayout("2006-01-02 15:04:05", "2006-01-02 15:04:05") + gtest.Assert(timeTemp.Format("Y-m-d H:i:s"), "2006-01-02 15:04:05") + + timeTemp1 := gtime.NewFromStrLayout("2006-01-02 15:04:05", "aabbcc") + if timeTemp1 != nil { + t.Error("test fail") + } + }) +} + +func Test_NewFromTimeStamp(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.NewFromTimeStamp(1554459846000) + gtest.Assert(timeTemp.Format("Y-m-d H:i:s"), "2019-04-05 18:24:06") + timeTemp1 := gtime.NewFromTimeStamp(0) + gtest.Assert(timeTemp1.Format("Y-m-d H:i:s"), "0001-01-01 00:00:00") + }) +} + +func Test_tSecond(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.Second(), timeTemp.Time.Unix()) + }) +} + +func Test_tNanosecond(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.Nanosecond(), timeTemp.Time.UnixNano()) + }) +} + +func Test_tMicrosecond(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.Microsecond(), timeTemp.Time.UnixNano()/1e3) + }) +} + +func Test_tMillisecond(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.Millisecond(), timeTemp.Time.UnixNano()/1e6) + }) +} + +func Test_String(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + gtest.Assert(timeTemp.String(), timeTemp.Time.Format("2006-01-02 15:04:05")) + }) +} + +func Test_Clone(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + timeTemp1 := timeTemp.Clone() + gtest.Assert(timeTemp.Time.Unix(), timeTemp1.Time.Unix()) + }) +} + +func Test_ToTime(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + timeTemp1 := timeTemp.Time + gtest.Assert(timeTemp.ToTime().UnixNano(), timeTemp1.UnixNano()) + }) +} + +func Test_Add(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.NewFromStr("2006-01-02 15:04:05") + timeTemp.Add(time.Second) + gtest.Assert(timeTemp.Format("Y-m-d H:i:s"), "2006-01-02 15:04:06") + }) +} + +func Test_ToZone(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + // + timeTemp.ToZone("America/Los_Angeles") + gtest.Assert(timeTemp.Time.Location().String(), "America/Los_Angeles") + + loc, err := time.LoadLocation("Asia/Shanghai") + if err != nil { + t.Error("test fail") + } + timeTemp.ToLocation(loc) + gtest.Assert(timeTemp.Time.Location().String(), "Asia/Shanghai") + + timeTemp1 := timeTemp.ToZone("errZone") + if timeTemp1 != nil { + t.Error("test fail") + } + }) +} + +func Test_AddDate(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.NewFromStr("2006-01-02 15:04:05") + timeTemp.AddDate(1, 2, 3) + gtest.Assert(timeTemp.Format("Y-m-d H:i:s"), "2007-03-05 15:04:05") + }) +} + +func Test_UTC(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + timeTemp1 := timeTemp.Time + timeTemp.UTC() + gtest.Assert(timeTemp.UnixNano(), timeTemp1.UTC().UnixNano()) + }) +} + +func Test_Local(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + timeTemp1 := timeTemp.Time + timeTemp.Local() + gtest.Assert(timeTemp.UnixNano(), timeTemp1.Local().UnixNano()) + }) +} + +func Test_Round(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + timeTemp1 := timeTemp.Time + timeTemp.Round(time.Hour) + gtest.Assert(timeTemp.UnixNano(), timeTemp1.Round(time.Hour).UnixNano()) + }) +} + +func Test_Truncate(t *testing.T) { + gtest.Case(t, func() { + timeTemp := gtime.Now() + timeTemp1 := timeTemp.Time + timeTemp.Truncate(time.Hour) + gtest.Assert(timeTemp.UnixNano(), timeTemp1.Truncate(time.Hour).UnixNano()) + }) +} From e50b8d963248f8731c11e82de92fb06b8d1758ee Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Sat, 6 Apr 2019 22:50:37 +0800 Subject: [PATCH 14/21] =?UTF-8?q?=E7=A7=BB=E9=99=A4gtime=20unit=20test?= =?UTF-8?q?=E4=B8=AD=E6=B3=A8=E9=87=8A=E6=8E=89=E7=9A=84=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/os/gtime/gtime_z_unit_basic_test.go | 36 -------------------------- g/os/gtime/gtime_z_unit_format_test.go | 11 -------- 2 files changed, 47 deletions(-) diff --git a/g/os/gtime/gtime_z_unit_basic_test.go b/g/os/gtime/gtime_z_unit_basic_test.go index 9499b3ac5..ecf0b5910 100644 --- a/g/os/gtime/gtime_z_unit_basic_test.go +++ b/g/os/gtime/gtime_z_unit_basic_test.go @@ -64,42 +64,6 @@ func Test_Datetime(t *testing.T) { }) } -/* -func Test_parseDateStr(t *testing.T) { - gtest.Case(t, func() { - //正常日期列表 - var testDates = []string{ - "2006-01-02", - "2006/01/02", - "2006.01.02", - "06.01.02", - "02.jan.2006", - } - - for _, item := range testDates { - year, month, day := parseDateStr(item) - gtest.Assert(year, 2006) - gtest.Assert(month, 1) - gtest.Assert(day, 2) - } - - //异常日期列表 - var testDatesFail = []string{ - "2006.01", - "06..02", - } - - for _, item := range testDatesFail { - year, month, day := parseDateStr(item) - gtest.Assert(year, 0) - gtest.Assert(month, 0) - gtest.Assert(day, 0) - } - - }) -} -*/ -// func Test_ConvertZone(t *testing.T) { gtest.Case(t, func() { //现行时间 diff --git a/g/os/gtime/gtime_z_unit_format_test.go b/g/os/gtime/gtime_z_unit_format_test.go index 09f5e9cb3..9d6fb6750 100644 --- a/g/os/gtime/gtime_z_unit_format_test.go +++ b/g/os/gtime/gtime_z_unit_format_test.go @@ -7,17 +7,6 @@ import ( "github.com/gogf/gf/g/test/gtest" ) -/* -func Test_formatToStdLayout(t *testing.T) { - gtest.Case(t, func() { - gtest.Assert(formatToStdLayout("Y-m-d H:i:s"), "2006-01-02 15:04:05") - gtest.Assert(formatToStdLayout("\\T\\i\\m\\e Y-m-d H:i:s"), "Time 2006-01-02 15:04:05") - gtest.Assert(formatToStdLayout("Y-m-d H:i:s\\"), "2006-01-02 15:04:05") - gtest.Assert(formatToStdLayout("Y-m-j G:i:s.u"), "2006-01-02 15:04:05.000") - gtest.Assert(formatToStdLayout("Y-m-j G:i:su"), "2006-01-02 15:04:05.000") - }) -} -*/ func Test_Format(t *testing.T) { gtest.Case(t, func() { timeTemp, err := gtime.StrToTime("2006-01-11 15:04:05", "Y-m-d H:i:s") From 352ad17715d901eafb440107e7becb6bcf2af54e Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Sat, 6 Apr 2019 22:56:47 +0800 Subject: [PATCH 15/21] =?UTF-8?q?=E9=81=BF=E5=85=8Dpr=E6=97=B6=E9=80=A0?= =?UTF-8?q?=E6=88=90gtime=E6=96=87=E4=BB=B6=E7=9A=84=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/os/gtime/gtime.go | 489 ++++++++++++++++++++++---------------------- 1 file changed, 244 insertions(+), 245 deletions(-) diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index e66e198ef..03d551d81 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -5,320 +5,319 @@ // You can obtain one at https://github.com/gogf/gf. // Package gtime provides functionality for measuring and displaying time. -// +// // 时间管理. package gtime import ( - "errors" - "regexp" - "strconv" - "strings" - "time" - - "github.com/gogf/gf/g/text/gregex" + "errors" + "github.com/gogf/gf/g/text/gregex" + "regexp" + "strconv" + "strings" + "time" ) const ( - // 时间间隔缩写 - D = 24 * time.Hour - H = time.Hour - M = time.Minute - S = time.Second - MS = time.Millisecond - US = time.Microsecond - NS = time.Nanosecond + // 时间间隔缩写 + D = 24*time.Hour + H = time.Hour + M = time.Minute + S = time.Second + MS = time.Millisecond + US = time.Microsecond + NS = time.Nanosecond - // 常用时间格式正则匹配,支持的标准时间格式: - // "2017-12-14 04:51:34 +0805 LMT", - // "2017-12-14 04:51:34 +0805 LMT", - // "2006-01-02T15:04:05Z07:00", - // "2014-01-17T01:19:15+08:00", - // "2018-02-09T20:46:17.897Z", - // "2018-02-09 20:46:17.897", - // "2018-02-09T20:46:17Z", - // "2018-02-09 20:46:17", - // "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]*)` - // 01-Nov-2018 11:50:28 - // 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]*)` + // 常用时间格式正则匹配,支持的标准时间格式: + // "2017-12-14 04:51:34 +0805 LMT", + // "2017-12-14 04:51:34 +0805 LMT", + // "2006-01-02T15:04:05Z07:00", + // "2014-01-17T01:19:15+08:00", + // "2018-02-09T20:46:17.897Z", + // "2018-02-09 20:46:17.897", + // "2018-02-09T20:46:17Z", + // "2018-02-09 20:46:17", + // "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]*)` + // 01-Nov-2018 11:50:28 + // 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]*)` ) var ( - // 使用正则判断会比直接使用ParseInLocation挨个轮训判断要快很多 - timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) - timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) - // 月份英文与阿拉伯数字对应关系 - monthMap = map[string]int{ - "jan": 1, - "feb": 2, - "mar": 3, - "apr": 4, - "may": 5, - "jun": 6, - "jul": 7, - "aug": 8, - "sep": 9, - "sept": 9, - "oct": 10, - "nov": 11, - "dec": 12, - "january": 1, - "february": 2, - "march": 3, - "april": 4, - "june": 6, - "july": 7, - "august": 8, - "september": 9, - "october": 10, - "november": 11, - "december": 12, - } + // 使用正则判断会比直接使用ParseInLocation挨个轮训判断要快很多 + timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) + timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) + // 月份英文与阿拉伯数字对应关系 + monthMap = map[string]int { + "jan" : 1, + "feb" : 2, + "mar" : 3, + "apr" : 4, + "may" : 5, + "jun" : 6, + "jul" : 7, + "aug" : 8, + "sep" : 9, + "sept" : 9, + "oct" : 10, + "nov" : 11, + "dec" : 12, + "january" : 1, + "february" : 2, + "march" : 3, + "april" : 4, + "june" : 6, + "july" : 7, + "august" : 8, + "september" : 9, + "october" : 10, + "november" : 11, + "december" : 12, + } ) // 设置当前进程全局的默认时区,如: Asia/Shanghai func SetTimeZone(zone string) error { - location, err := time.LoadLocation(zone) - if err == nil { - time.Local = location - } - return err + location, err := time.LoadLocation(zone) + if err == nil { + time.Local = location + } + return err } // 获取当前的纳秒数 func Nanosecond() int64 { - return time.Now().UnixNano() + return time.Now().UnixNano() } // 获取当前的微秒数 func Microsecond() int64 { - return time.Now().UnixNano() / 1e3 + return time.Now().UnixNano()/1e3 } // 获取当前的毫秒数 func Millisecond() int64 { - return time.Now().UnixNano() / 1e6 + return time.Now().UnixNano()/1e6 } // 获取当前的秒数(时间戳) func Second() int64 { - return time.Now().Unix() + return time.Now().Unix() } // 获得当前的日期(例如:2006-01-02) func Date() string { - return time.Now().Format("2006-01-02") + return time.Now().Format("2006-01-02") } // 获得当前的时间(例如:2006-01-02 15:04:05) func Datetime() string { - return time.Now().Format("2006-01-02 15:04:05") + return time.Now().Format("2006-01-02 15:04:05") } // 解析日期字符串(日期支持'-'或'/'或'.'连接符号) func parseDateStr(s string) (year, month, day int) { - array := strings.Split(s, "-") - if len(array) < 3 { - array = strings.Split(s, "/") - } - if len(array) < 3 { - array = strings.Split(s, ".") - } - // 解析失败 - if len(array) < 3 { - return - } - // 判断年份在开头还是末尾 - if isNumeric(array[1]) { - year, _ = strconv.Atoi(array[0]) - month, _ = strconv.Atoi(array[1]) - day, _ = strconv.Atoi(array[2]) - } else { - if v, ok := monthMap[strings.ToLower(array[1])]; ok { - month = v - } else { - return - } - year, _ = strconv.Atoi(array[2]) - day, _ = strconv.Atoi(array[0]) - } - // 年是否为缩写,如果是,那么需要补上前缀 - if year < 100 { - year = int(time.Now().Year()/100)*100 + year - } - return + array := strings.Split(s, "-") + if len(array) < 3 { + array = strings.Split(s, "/") + } + if len(array) < 3 { + array = strings.Split(s, ".") + } + // 解析失败 + if len(array) < 3 { + return + } + // 判断年份在开头还是末尾 + if isNumeric(array[1]) { + year, _ = strconv.Atoi(array[0]) + month, _ = strconv.Atoi(array[1]) + day, _ = strconv.Atoi(array[2]) + } else { + if v, ok := monthMap[strings.ToLower(array[1])]; ok { + month = v + } else { + return + } + year, _ = strconv.Atoi(array[2]) + day, _ = strconv.Atoi(array[0]) + } + // 年是否为缩写,如果是,那么需要补上前缀 + if year < 100 { + year = int(time.Now().Year()/100)*100 + year + } + return } // 字符串转换为时间对象,format参数指定格式的format(如: Y-m-d H:i:s),当指定format参数时效果同StrToTimeFormat方法。 // 注意:自动解析日期时间时,必须有日期才能解析成功,如果字符串中不带有日期字段,那么解析失败。 -func StrToTime(str string, format ...string) (*Time, error) { - if len(format) > 0 { - return StrToTimeFormat(str, format[0]) - } - var year, month, day int - var hour, min, sec, nsec int - var match []string - var local = time.Local - if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { - 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) - } - year, month, day = parseDateStr(match[1]) - } else { - return nil, errors.New("unsupported time format") - } +func StrToTime(str string, format...string) (*Time, error) { + if len(format) > 0 { + return StrToTimeFormat(str, format[0]) + } + var year, month, day int + var hour, min, sec, nsec int + var match []string + var local = time.Local + if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { + 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) + } + year, month, day = parseDateStr(match[1]) + } else { + return nil, errors.New("unsupported time format") + } - // 时间 - if len(match[2]) > 0 { - s := strings.Replace(match[2], ":", "", -1) - if len(s) < 6 { - s += strings.Repeat("0", 6-len(s)) - } - hour, _ = strconv.Atoi(s[0:2]) - min, _ = strconv.Atoi(s[2:4]) - sec, _ = strconv.Atoi(s[4:6]) - } - // 纳秒,检查并执行位补齐 - if len(match[3]) > 0 { - nsec, _ = strconv.Atoi(match[3]) - for i := 0; i < 9-len(match[3]); i++ { - nsec *= 10 - } - } - // 如果字符串中有时区信息(具体时间信息),那么执行时区转换,将时区转成UTC - if match[4] != "" && match[6] == "" { - match[6] = "000000" - } - // 如果offset有值优先处理offset,否则处理后面的时区名称 - if match[6] != "" { - zone := strings.Replace(match[6], ":", "", -1) - zone = strings.TrimLeft(zone, "+-") - if len(zone) <= 6 { - zone += strings.Repeat("0", 6-len(zone)) - h, _ := strconv.Atoi(zone[0:2]) - m, _ := strconv.Atoi(zone[2:4]) - s, _ := strconv.Atoi(zone[4:6]) - // 判断字符串输入的时区是否和当前程序时区相等(使用offset判断),不相等则将对象统一转换为UTC时区 - // 当前程序时区Offset(秒) - _, localOffset := time.Now().Zone() - if (h*3600 + m*60 + s) != localOffset { - local = time.UTC - // UTC时差转换 - operation := match[5] - if operation != "+" && operation != "-" { - operation = "-" - } - switch operation { - case "+": - if h > 0 { - hour -= h - } - if m > 0 { - min -= m - } - if s > 0 { - sec -= s - } - case "-": - if h > 0 { - hour += h - } - if m > 0 { - min += m - } - if s > 0 { - sec += s - } - } - } - } - } - // 统一生成UTC时间对象 - return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil + // 时间 + if len(match[2]) > 0 { + s := strings.Replace(match[2], ":", "", -1) + if len(s) < 6 { + s += strings.Repeat("0", 6 - len(s)) + } + hour, _ = strconv.Atoi(s[0 : 2]) + min, _ = strconv.Atoi(s[2 : 4]) + sec, _ = strconv.Atoi(s[4 : 6]) + } + // 纳秒,检查并执行位补齐 + if len(match[3]) > 0 { + nsec, _ = strconv.Atoi(match[3]) + for i := 0; i < 9 - len(match[3]); i++ { + nsec *= 10 + } + } + // 如果字符串中有时区信息(具体时间信息),那么执行时区转换,将时区转成UTC + if match[4] != "" && match[6] == "" { + match[6] = "000000" + } + // 如果offset有值优先处理offset,否则处理后面的时区名称 + if match[6] != "" { + zone := strings.Replace(match[6], ":", "", -1) + zone = strings.TrimLeft(zone, "+-") + if len(zone) <= 6 { + zone += strings.Repeat("0", 6 - len(zone)) + h, _ := strconv.Atoi(zone[0 : 2]) + m, _ := strconv.Atoi(zone[2 : 4]) + s, _ := strconv.Atoi(zone[4 : 6]) + // 判断字符串输入的时区是否和当前程序时区相等(使用offset判断),不相等则将对象统一转换为UTC时区 + // 当前程序时区Offset(秒) + _, localOffset := time.Now().Zone() + if (h * 3600 + m * 60 + s) != localOffset { + local = time.UTC + // UTC时差转换 + operation := match[5] + if operation != "+" && operation != "-" { + operation = "-" + } + switch operation { + case "+": + if h > 0 { + hour -= h + } + if m > 0 { + min -= m + } + if s > 0 { + sec -= s + } + case "-": + if h > 0 { + hour += h + } + if m > 0 { + min += m + } + if s > 0 { + sec += s + } + } + } + } + } + // 统一生成UTC时间对象 + return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil } // 时区转换 -func ConvertZone(strTime string, toZone string, fromZone ...string) (*Time, error) { - t, err := StrToTime(strTime) - if err != nil { - return nil, err - } - if len(fromZone) > 0 { - if l, err := time.LoadLocation(fromZone[0]); err != nil { - return nil, err - } else { - t.Time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) - } - } - if l, err := time.LoadLocation(toZone); err != nil { - return nil, err - } else { - return t.ToLocation(l), nil - } +func ConvertZone(strTime string, toZone string, fromZone...string) (*Time, error) { + t, err := StrToTime(strTime) + if err != nil { + return nil, err + } + if len(fromZone) > 0 { + if l, err := time.LoadLocation(fromZone[0]); err != nil { + return nil, err + } else { + t.Time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) + } + } + if l, err := time.LoadLocation(toZone); err != nil { + return nil, err + } else { + return t.ToLocation(l), nil + } } // 字符串转换为时间对象,指定字符串时间格式,format格式形如:Y-m-d H:i:s func StrToTimeFormat(str string, format string) (*Time, error) { - return StrToTimeLayout(str, formatToStdLayout(format)) + return StrToTimeLayout(str, formatToStdLayout(format)) } // 字符串转换为时间对象,通过标准库layout格式进行解析,layout格式形如:2006-01-02 15:04:05 func StrToTimeLayout(str string, layout string) (*Time, error) { - if t, err := time.ParseInLocation(layout, str, time.Local); err == nil { - return NewFromTime(t), nil - } else { - return nil, err - } + if t, err := time.ParseInLocation(layout, str, time.Local); err == nil { + return NewFromTime(t), nil + } else { + return nil, err + } } // 从字符串内容中(也可以是文件名称等等)解析时间,并返回解析成功的时间对象,否则返回nil。 // 注意当内容中存在多个时间时,会解析第一个。 // format参数可以指定需要解析的时间格式。 -func ParseTimeFromContent(content string, format ...string) *Time { - if len(format) > 0 { - if match, err := gregex.MatchString(formatToRegexPattern(format[0]), content); err == nil && len(match) > 0 { - return NewFromStrFormat(match[0], format[0]) - } - } else { - if match := timeRegex1.FindStringSubmatch(content); len(match) >= 1 { - 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")) - } - } - return nil +func ParseTimeFromContent(content string, format...string) *Time { + if len(format) > 0 { + if match, err := gregex.MatchString(formatToRegexPattern(format[0]), content); err == nil && len(match) > 0 { + return NewFromStrFormat(match[0], format[0]) + } + } else { + if match := timeRegex1.FindStringSubmatch(content); len(match) >= 1 { + 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")) + } + } + return nil } // 计算函数f执行的时间,单位纳秒 func FuncCost(f func()) int64 { - t := Nanosecond() - f() - return Nanosecond() - t + t := Nanosecond() + f() + return Nanosecond() - t } // 判断所给字符串是否为数字 func isNumeric(s string) bool { - length := len(s) - if length == 0 { - return false - } - for i := 0; i < len(s); i++ { - if s[i] < byte('0') || s[i] > byte('9') { - return false - } - } - return true -} + length := len(s) + if length == 0 { + return false + } + for i := 0; i < len(s); i++ { + if s[i] < byte('0') || s[i] > byte('9') { + return false + } + } + return true +} \ No newline at end of file From f1818ed2ff7eab40c947384168c68b2269cefe57 Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Sat, 6 Apr 2019 23:53:06 +0800 Subject: [PATCH 16/21] =?UTF-8?q?=E8=A1=A5=E5=85=85gtime=5Fformat=E7=9A=84?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E6=B5=8B=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/os/gtime/gtime_z_unit_basic_test.go | 148 ++++++++++++++++---------- 1 file changed, 90 insertions(+), 58 deletions(-) diff --git a/g/os/gtime/gtime_z_unit_basic_test.go b/g/os/gtime/gtime_z_unit_basic_test.go index ecf0b5910..c3159b560 100644 --- a/g/os/gtime/gtime_z_unit_basic_test.go +++ b/g/os/gtime/gtime_z_unit_basic_test.go @@ -64,6 +64,95 @@ func Test_Datetime(t *testing.T) { }) } +func Test_StrToTime(t *testing.T) { + gtest.Case(t, func() { + //正常日期列表 + //正则的原因,日期"06.01.02","2006.01","2006..01"无法覆盖gtime.go的百分百 + var testDatetimes = []string{ + "2006-01-02 15:04:05", + "2006/01/02 15:04:05", + "2006.01.02 15:04:05.000", + "2006.01.02 - 15:04:05", + "2006.01.02 15:04:05 +0800 CST", + "2006-01-02T20:05:06+05:01:01", + "2006-01-02T14:03:04Z01:01:01", + "2006-01-02T15:04:05Z", + "02-jan-2006 15:04:05", + "02/jan/2006 15:04:05", + "02.jan.2006 15:04:05", + "02.jan.2006:15:04:05", + } + + for _, item := range testDatetimes { + timeTemp, err := gtime.StrToTime(item) + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") + } + + //正常日期列表,时间00:00:00 + var testDates = []string{ + "2006.01.02", + "2006.01.02 00:00", + "2006.01.02 00:00:00.000", + } + + for _, item := range testDates { + timeTemp, err := gtime.StrToTime(item) + if err != nil { + t.Error("test fail") + } + gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 00:00:00") + } + + //测试格式化formatToStdLayout + var testDateFormats = []string{ + "Y-m-d H:i:s", + "\\T\\i\\m\\e Y-m-d H:i:s", + "Y-m-d H:i:s\\", + "Y-m-j G:i:s.u", + "Y-m-j G:i:su", + } + + var testDateFormatsResult = []string{ + "2007-01-02 15:04:05", + "Time 2007-01-02 15:04:05", + "2007-01-02 15:04:05", + "2007-01-02 15:04:05.000", + "2007-01-02 15:04:05.000", + } + + for index, item := range testDateFormats { + timeTemp, err := gtime.StrToTime(testDateFormatsResult[index], item) + if err != nil { + t.Error("test fail") + } + gtest.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", + "20060102", + } + + for _, item := range testDatesFail { + _, err := gtime.StrToTime(item) + if err == nil { + t.Error("test fail") + } + } + + //test err + _, err := gtime.StrToTime("2006-01-02 15:04:05", "aabbccdd") + if err == nil { + t.Error("test fail") + } + }) +} + func Test_ConvertZone(t *testing.T) { gtest.Case(t, func() { //现行时间 @@ -111,66 +200,9 @@ func Test_ConvertZone(t *testing.T) { }) } -func Test_StrToTime(t *testing.T) { +func Test_StrToTimeFormat(t *testing.T) { gtest.Case(t, func() { - //正常日期列表 - var testDatetimes = []string{ - "2006-01-02 15:04:05", - "2006/01/02 15:04:05", - "2006.01.02 15:04:05.000", - "2006.01.02 - 15:04:05", - "2006.01.02 15:04:05 +0800 CST", - "2006-01-02T20:05:06+05:01:01", - "2006-01-02T14:03:04Z01:01:01", - "2006-01-02T15:04:05Z", - "02-jan-2006 15:04:05", - "02/jan/2006 15:04:05", - "02.jan.2006 15:04:05", - "02.jan.2006:15:04:05", - } - for _, item := range testDatetimes { - timeTemp, err := gtime.StrToTime(item) - if err != nil { - t.Error("test fail") - } - gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") - } - - //正常日期列表,时间00:00:00 - var testDates = []string{ - "2006.01.02", - "2006.01.02 00:00", - "2006.01.02 00:00:00.000", - } - - for _, item := range testDates { - timeTemp, err := gtime.StrToTime(item) - if err != nil { - t.Error("test fail") - } - gtest.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 00:00:00") - } - - //异常日期列表 - var testDatesFail = []string{ - "2006.01", - "06..02", - "20060102", - } - - for _, item := range testDatesFail { - _, err := gtime.StrToTime(item) - if err == nil { - t.Error("test fail") - } - } - - //test err - _, err := gtime.StrToTime("2006-01-02 15:04:05", "aabbccdd") - if err == nil { - t.Error("test fail") - } }) } From 1fc85d49bd0207625acd73c2b6a479fd6e099503 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 7 Apr 2019 21:49:24 +0800 Subject: [PATCH 17/21] gtime updates; README updates --- README.MD | 18 ++++++++++-------- g/os/gtime/gtime.go | 4 ---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.MD b/README.MD index 208499e62..f83e8035e 100644 --- a/README.MD +++ b/README.MD @@ -67,17 +67,19 @@ func main() { # Contributors -- [johng](https://gitee.com/johng) -- [zhaopengme](https://github.com/zhaopengme) -- [wenzi1](https://gitee.com/wenzi1) -- [zseeker](https://gitee.com/zseeker) -- [ymrjqyy](https://gitee.com/ymrjqyy) - [chenyang351](https://github.com/chenyang351) -- [wxkj](https://gitee.com/wxkj) -- [wxkj001](https://github.com/wxkj001) -- [zhangjinfu](https://gitee.com/zhangjinfu) - [garfieldkwong](https://gitee.com/garfieldkwong) +- [hailaz](https://gitee.com/hailaz) +- [johng](https://gitee.com/johng) +- [pibigstar](https://github.com/pibigstar) - [qq1054000800](https://gitee.com/qq1054000800) +- [wenzi1](https://gitee.com/wenzi1) +- [wxkj001](https://github.com/wxkj001) +- [ymrjqyy](https://gitee.com/ymrjqyy) +- [youyixiao](https://github.com/youyixiao) +- [zhangjinfu](https://gitee.com/zhangjinfu) +- [zhaopengme](https://github.com/zhaopengme) +- [zseeker](https://gitee.com/zseeker) # Donators diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go index 03d551d81..5e45231a9 100644 --- a/g/os/gtime/gtime.go +++ b/g/os/gtime/gtime.go @@ -149,10 +149,6 @@ func parseDateStr(s string) (year, month, day int) { year, _ = strconv.Atoi(array[2]) day, _ = strconv.Atoi(array[0]) } - // 年是否为缩写,如果是,那么需要补上前缀 - if year < 100 { - year = int(time.Now().Year()/100)*100 + year - } return } From 85606e3e7e56599da119213d9652382bfbd28a8d Mon Sep 17 00:00:00 2001 From: John Date: Sun, 7 Apr 2019 21:59:28 +0800 Subject: [PATCH 18/21] README updates --- README.MD | 18 +++++++++--------- README_ZH.MD | 33 +++++++++++++++++---------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/README.MD b/README.MD index f83e8035e..9d9707e5b 100644 --- a/README.MD +++ b/README.MD @@ -5,9 +5,7 @@ [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) [![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) -[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) -[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/gogf/gf) -[![Release](https://img.shields.io/github/release/gogf/gf.svg?style=flat)](https://github.com/gogf/gf/releases) +[![Language](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)