// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. // 时间管理 package gtime import ( "errors" "gitee.com/johng/gf/g/util/gregex" "gitee.com/johng/gf/g/util/gstr" "regexp" "strconv" "strings" "time" ) const ( // 常用时间格式正则匹配,支持的标准时间格式: // "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{2,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/18 11:50:28 // 01/Nov/18 11:50:28 // 01.Nov.18 11:50:28 // 日期连接符号支持'-'、'/'、'.' TIME_REAGEX_PATTERN2 = `(\d{1,2}[-/\.][A-Za-z]{3,}[-/\.]\d{2,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, } ) // 类似与js中的SetTimeout,一段时间后执行回调函数 func SetTimeout(t time.Duration, callback func()) { go func() { time.Sleep(t) callback() }() } // 类似与js中的SetInterval,每隔一段时间后执行回调函数,当回调函数返回true,那么继续执行,否则终止执行,该方法是异步的 // 注意:由于采用的是循环而不是递归操作,因此间隔时间将会以上一次回调函数执行完成的时间来计算 func SetInterval(t time.Duration, callback func() bool) { go func() { for { time.Sleep(t) if !callback() { break } } }() } // 设置当前进程全局的默认时区,如: Asia/Shanghai func SetTimeZone(zone string) error { location, err := time.LoadLocation(zone) if err == nil { time.Local = location } return err } // 获取当前的纳秒数 func Nanosecond() int64 { return time.Now().UnixNano() } // 获取当前的微秒数 func Microsecond() int64 { return time.Now().UnixNano()/1e3 } // 获取当前的毫秒数 func Millisecond() int64 { return time.Now().UnixNano()/1e6 } // 获取当前的秒数(时间戳) func Second() int64 { return time.Now().UnixNano()/1e9 } // 获得当前的日期(例如:2006-01-02) func Date() string { 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") } // 解析日期字符串(日期支持'-'或'/'或'.'连接符号) 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 gstr.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 } // 字符串转换为时间对象,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") } // 时间 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 } } // 字符串转换为时间对象,指定字符串时间格式,format格式形如:Y-m-d H:i:s func StrToTimeFormat(str string, format string) (*Time, error) { 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 } } // 从字符串内容中(也可以是文件名称等等)解析时间,并返回解析成功的时间对象,否则返回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 } // 计算函数f执行的时间,单位纳秒 func FuncCost(f func()) int64 { t := Nanosecond() f() return Nanosecond() - t }