gf/g/os/gtime/gtime.go

304 lines
9.6 KiB
Go
Raw Normal View History

// 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"
"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-11-05 10:29:58 +08:00
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
2018-11-05 10:29:58 +08:00
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
2018-07-19 15:33:17 +08:00
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 {
// 年是否为缩写,如果是,那么需要补上前缀
year, _ = strconv.Atoi(array[0])
if year < 100 {
year = int(time.Now().Year()/100)*100 + year
}
month, _ = strconv.Atoi(array[1])
day, _ = strconv.Atoi(array[2])
}
return
}
// 字符串转换为时间对象第二个参数指定格式的format(如: Y-m-d H:i:s)当指定第二个参数时同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 array, match []string
var local = time.Local
if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 {
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 {
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])
if len(array) >= 2 {
min, _ = strconv.Atoi(s[2 : 4])
}
if len(array) >= 3 {
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
}
2018-07-11 13:51:03 +08:00
// 时区转换
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
}
}
2018-07-11 17:06:47 +08:00
// 字符串转换为时间对象指定字符串时间格式format格式形如Y-m-d H:i:s
func StrToTimeFormat(str string, format string) (*Time, error) {
2018-07-11 17:06:47 +08:00
return StrToTimeLayout(str, formatToStdLayout(format))
}
2018-07-11 17:06:47 +08:00
// 字符串转换为时间对象通过标准库layout格式进行解析layout格式形如2006-01-02 15:04:05
func StrToTimeLayout(str string, layout string) (*Time, error) {
2018-07-11 13:51:03 +08:00
if t, err := time.ParseInLocation(layout, str, time.Local); err == nil {
return NewFromTime(t), nil
2018-07-11 13:51:03 +08:00
} else {
return nil, err
2018-07-11 13:51:03 +08:00
}
}
// 从文本内容中解析时间,并返回解析成功的时间对象。注意当文本中存在多个时间时,会解析第一个。
// 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
}