github.com/zhongdalu/gf@v1.0.0/g/os/gtime/gtime.go (about) 1 // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 // Package gtime provides functionality for measuring and displaying time. 8 package gtime 9 10 import ( 11 "errors" 12 "github.com/zhongdalu/gf/g/text/gregex" 13 "regexp" 14 "strconv" 15 "strings" 16 "time" 17 ) 18 19 const ( 20 // 时间间隔缩写 21 D = 24 * time.Hour 22 H = time.Hour 23 M = time.Minute 24 S = time.Second 25 MS = time.Millisecond 26 US = time.Microsecond 27 NS = time.Nanosecond 28 29 // 常用时间格式正则匹配,支持的标准时间格式: 30 // "2017-12-14 04:51:34 +0805 LMT", 31 // "2017-12-14 04:51:34 +0805 LMT", 32 // "2006-01-02T15:04:05Z07:00", 33 // "2014-01-17T01:19:15+08:00", 34 // "2018-02-09T20:46:17.897Z", 35 // "2018-02-09 20:46:17.897", 36 // "2018-02-09T20:46:17Z", 37 // "2018-02-09 20:46:17", 38 // "2018/10/31 - 16:38:46" 39 // "2018-02-09", 40 // "2018.02.09", 41 // 日期连接符号支持'-'、'/'、'.' 42 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]*)` 43 // 01-Nov-2018 11:50:28 44 // 01/Nov/2018 11:50:28 45 // 01.Nov.2018 11:50:28 46 // 01.Nov.2018:11:50:28 47 // 日期连接符号支持'-'、'/'、'.' 48 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]*)` 49 ) 50 51 var ( 52 // 使用正则判断会比直接使用ParseInLocation挨个轮训判断要快很多 53 timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) 54 timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) 55 // 月份英文与阿拉伯数字对应关系 56 monthMap = map[string]int{ 57 "jan": 1, 58 "feb": 2, 59 "mar": 3, 60 "apr": 4, 61 "may": 5, 62 "jun": 6, 63 "jul": 7, 64 "aug": 8, 65 "sep": 9, 66 "sept": 9, 67 "oct": 10, 68 "nov": 11, 69 "dec": 12, 70 "january": 1, 71 "february": 2, 72 "march": 3, 73 "april": 4, 74 "june": 6, 75 "july": 7, 76 "august": 8, 77 "september": 9, 78 "october": 10, 79 "november": 11, 80 "december": 12, 81 } 82 ) 83 84 // 设置当前进程全局的默认时区,如: Asia/Shanghai 85 func SetTimeZone(zone string) error { 86 location, err := time.LoadLocation(zone) 87 if err == nil { 88 time.Local = location 89 } 90 return err 91 } 92 93 // 获取当前的纳秒数 94 func Nanosecond() int64 { 95 return time.Now().UnixNano() 96 } 97 98 // 获取当前的微秒数 99 func Microsecond() int64 { 100 return time.Now().UnixNano() / 1e3 101 } 102 103 // 获取当前的毫秒数 104 func Millisecond() int64 { 105 return time.Now().UnixNano() / 1e6 106 } 107 108 // 获取当前的秒数(时间戳) 109 func Second() int64 { 110 return time.Now().Unix() 111 } 112 113 // 获得当前的日期(例如:2006-01-02) 114 func Date() string { 115 return time.Now().Format("2006-01-02") 116 } 117 118 // 获得当前的时间(例如:2006-01-02 15:04:05) 119 func Datetime() string { 120 return time.Now().Format("2006-01-02 15:04:05") 121 } 122 123 // 解析日期字符串(日期支持'-'或'/'或'.'连接符号) 124 func parseDateStr(s string) (year, month, day int) { 125 array := strings.Split(s, "-") 126 if len(array) < 3 { 127 array = strings.Split(s, "/") 128 } 129 if len(array) < 3 { 130 array = strings.Split(s, ".") 131 } 132 // 解析失败 133 if len(array) < 3 { 134 return 135 } 136 // 判断年份在开头还是末尾 137 if isNumeric(array[1]) { 138 year, _ = strconv.Atoi(array[0]) 139 month, _ = strconv.Atoi(array[1]) 140 day, _ = strconv.Atoi(array[2]) 141 } else { 142 if v, ok := monthMap[strings.ToLower(array[1])]; ok { 143 month = v 144 } else { 145 return 146 } 147 year, _ = strconv.Atoi(array[2]) 148 day, _ = strconv.Atoi(array[0]) 149 } 150 return 151 } 152 153 // 字符串转换为时间对象,format参数指定格式的format(如: Y-m-d H:i:s),当指定format参数时效果同StrToTimeFormat方法。 154 // 注意:自动解析日期时间时,必须有日期才能解析成功,如果字符串中不带有日期字段,那么解析失败。 155 func StrToTime(str string, format ...string) (*Time, error) { 156 if len(format) > 0 { 157 return StrToTimeFormat(str, format[0]) 158 } 159 var year, month, day int 160 var hour, min, sec, nsec int 161 var match []string 162 var local = time.Local 163 if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { 164 for k, v := range match { 165 match[k] = strings.TrimSpace(v) 166 } 167 year, month, day = parseDateStr(match[1]) 168 } else if match = timeRegex2.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { 169 for k, v := range match { 170 match[k] = strings.TrimSpace(v) 171 } 172 year, month, day = parseDateStr(match[1]) 173 } else { 174 return nil, errors.New("unsupported time format") 175 } 176 177 // 时间 178 if len(match[2]) > 0 { 179 s := strings.Replace(match[2], ":", "", -1) 180 if len(s) < 6 { 181 s += strings.Repeat("0", 6-len(s)) 182 } 183 hour, _ = strconv.Atoi(s[0:2]) 184 min, _ = strconv.Atoi(s[2:4]) 185 sec, _ = strconv.Atoi(s[4:6]) 186 } 187 // 纳秒,检查并执行位补齐 188 if len(match[3]) > 0 { 189 nsec, _ = strconv.Atoi(match[3]) 190 for i := 0; i < 9-len(match[3]); i++ { 191 nsec *= 10 192 } 193 } 194 // 如果字符串中有时区信息(具体时间信息),那么执行时区转换,将时区转成UTC 195 if match[4] != "" && match[6] == "" { 196 match[6] = "000000" 197 } 198 // 如果offset有值优先处理offset,否则处理后面的时区名称 199 if match[6] != "" { 200 zone := strings.Replace(match[6], ":", "", -1) 201 zone = strings.TrimLeft(zone, "+-") 202 if len(zone) <= 6 { 203 zone += strings.Repeat("0", 6-len(zone)) 204 h, _ := strconv.Atoi(zone[0:2]) 205 m, _ := strconv.Atoi(zone[2:4]) 206 s, _ := strconv.Atoi(zone[4:6]) 207 // 判断字符串输入的时区是否和当前程序时区相等(使用offset判断),不相等则将对象统一转换为UTC时区 208 // 当前程序时区Offset(秒) 209 _, localOffset := time.Now().Zone() 210 if (h*3600 + m*60 + s) != localOffset { 211 local = time.UTC 212 // UTC时差转换 213 operation := match[5] 214 if operation != "+" && operation != "-" { 215 operation = "-" 216 } 217 switch operation { 218 case "+": 219 if h > 0 { 220 hour -= h 221 } 222 if m > 0 { 223 min -= m 224 } 225 if s > 0 { 226 sec -= s 227 } 228 case "-": 229 if h > 0 { 230 hour += h 231 } 232 if m > 0 { 233 min += m 234 } 235 if s > 0 { 236 sec += s 237 } 238 } 239 } 240 } 241 } 242 // 统一生成UTC时间对象 243 return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil 244 } 245 246 // 时区转换 247 func ConvertZone(strTime string, toZone string, fromZone ...string) (*Time, error) { 248 t, err := StrToTime(strTime) 249 if err != nil { 250 return nil, err 251 } 252 if len(fromZone) > 0 { 253 if l, err := time.LoadLocation(fromZone[0]); err != nil { 254 return nil, err 255 } else { 256 t.Time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) 257 } 258 } 259 if l, err := time.LoadLocation(toZone); err != nil { 260 return nil, err 261 } else { 262 return t.ToLocation(l), nil 263 } 264 } 265 266 // 字符串转换为时间对象,指定字符串时间格式,format格式形如:Y-m-d H:i:s 267 func StrToTimeFormat(str string, format string) (*Time, error) { 268 return StrToTimeLayout(str, formatToStdLayout(format)) 269 } 270 271 // 字符串转换为时间对象,通过标准库layout格式进行解析,layout格式形如:2006-01-02 15:04:05 272 func StrToTimeLayout(str string, layout string) (*Time, error) { 273 if t, err := time.ParseInLocation(layout, str, time.Local); err == nil { 274 return NewFromTime(t), nil 275 } else { 276 return nil, err 277 } 278 } 279 280 // 从字符串内容中(也可以是文件名称等等)解析时间,并返回解析成功的时间对象,否则返回nil。 281 // 注意当内容中存在多个时间时,会解析第一个。 282 // format参数可以指定需要解析的时间格式。 283 func ParseTimeFromContent(content string, format ...string) *Time { 284 if len(format) > 0 { 285 if match, err := gregex.MatchString(formatToRegexPattern(format[0]), content); err == nil && len(match) > 0 { 286 return NewFromStrFormat(match[0], format[0]) 287 } 288 } else { 289 if match := timeRegex1.FindStringSubmatch(content); len(match) >= 1 { 290 return NewFromStr(strings.Trim(match[0], "./_- \n\r")) 291 } else if match := timeRegex2.FindStringSubmatch(content); len(match) >= 1 { 292 return NewFromStr(strings.Trim(match[0], "./_- \n\r")) 293 } 294 } 295 return nil 296 } 297 298 // 计算函数f执行的时间,单位纳秒 299 func FuncCost(f func()) int64 { 300 t := Nanosecond() 301 f() 302 return Nanosecond() - t 303 } 304 305 // 判断所给字符串是否为数字 306 func isNumeric(s string) bool { 307 length := len(s) 308 if length == 0 { 309 return false 310 } 311 for i := 0; i < len(s); i++ { 312 if s[i] < byte('0') || s[i] > byte('9') { 313 return false 314 } 315 } 316 return true 317 }