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  }