gtime.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. // Copyright GoFrame Author(https://goframe.org). 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/gogf/gf.
  6. // Package gtime provides functionality for measuring and displaying time.
  7. //
  8. // This package should keep much less dependencies with other packages.
  9. package gtime
  10. import (
  11. "fmt"
  12. "github.com/gogf/gf/errors/gcode"
  13. "github.com/gogf/gf/errors/gerror"
  14. "github.com/gogf/gf/internal/utils"
  15. "os"
  16. "regexp"
  17. "strconv"
  18. "strings"
  19. "time"
  20. "github.com/gogf/gf/text/gregex"
  21. )
  22. const (
  23. // Short writes for common usage durations.
  24. D = 24 * time.Hour
  25. H = time.Hour
  26. M = time.Minute
  27. S = time.Second
  28. MS = time.Millisecond
  29. US = time.Microsecond
  30. NS = time.Nanosecond
  31. // Regular expression1(datetime separator supports '-', '/', '.').
  32. // Eg:
  33. // "2017-12-14 04:51:34 +0805 LMT",
  34. // "2017-12-14 04:51:34 +0805 LMT",
  35. // "2006-01-02T15:04:05Z07:00",
  36. // "2014-01-17T01:19:15+08:00",
  37. // "2018-02-09T20:46:17.897Z",
  38. // "2018-02-09 20:46:17.897",
  39. // "2018-02-09T20:46:17Z",
  40. // "2018-02-09 20:46:17",
  41. // "2018/10/31 - 16:38:46"
  42. // "2018-02-09",
  43. // "2018.02.09",
  44. timeRegexPattern1 = `(\d{4}[-/\.]\d{1,2}[-/\.]\d{1,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]*)`
  45. // Regular expression2(datetime separator supports '-', '/', '.').
  46. // Eg:
  47. // 01-Nov-2018 11:50:28
  48. // 01/Nov/2018 11:50:28
  49. // 01.Nov.2018 11:50:28
  50. // 01.Nov.2018:11:50:28
  51. timeRegexPattern2 = `(\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]*)`
  52. // Regular expression3(time).
  53. // Eg:
  54. // 11:50:28
  55. // 11:50:28.897
  56. timeRegexPattern3 = `(\d{2}):(\d{2}):(\d{2})\.{0,1}(\d{0,9})`
  57. )
  58. var (
  59. // It's more high performance using regular expression
  60. // than time.ParseInLocation to parse the datetime string.
  61. timeRegex1, _ = regexp.Compile(timeRegexPattern1)
  62. timeRegex2, _ = regexp.Compile(timeRegexPattern2)
  63. timeRegex3, _ = regexp.Compile(timeRegexPattern3)
  64. // Month words to arabic numerals mapping.
  65. monthMap = map[string]int{
  66. "jan": 1,
  67. "feb": 2,
  68. "mar": 3,
  69. "apr": 4,
  70. "may": 5,
  71. "jun": 6,
  72. "jul": 7,
  73. "aug": 8,
  74. "sep": 9,
  75. "sept": 9,
  76. "oct": 10,
  77. "nov": 11,
  78. "dec": 12,
  79. "january": 1,
  80. "february": 2,
  81. "march": 3,
  82. "april": 4,
  83. "june": 6,
  84. "july": 7,
  85. "august": 8,
  86. "september": 9,
  87. "october": 10,
  88. "november": 11,
  89. "december": 12,
  90. }
  91. )
  92. // SetTimeZone sets the time zone for current whole process.
  93. // The parameter <zone> is an area string specifying corresponding time zone,
  94. // eg: Asia/Shanghai.
  95. //
  96. // This should be called before package "time" import.
  97. // Please refer to issue: https://github.com/golang/go/issues/34814
  98. func SetTimeZone(zone string) error {
  99. location, err := time.LoadLocation(zone)
  100. if err != nil {
  101. return err
  102. }
  103. return os.Setenv("TZ", location.String())
  104. }
  105. // Timestamp retrieves and returns the timestamp in seconds.
  106. func Timestamp() int64 {
  107. return Now().Timestamp()
  108. }
  109. // TimestampMilli retrieves and returns the timestamp in milliseconds.
  110. func TimestampMilli() int64 {
  111. return Now().TimestampMilli()
  112. }
  113. // TimestampMicro retrieves and returns the timestamp in microseconds.
  114. func TimestampMicro() int64 {
  115. return Now().TimestampMicro()
  116. }
  117. // TimestampNano retrieves and returns the timestamp in nanoseconds.
  118. func TimestampNano() int64 {
  119. return Now().TimestampNano()
  120. }
  121. // TimestampStr is a convenience method which retrieves and returns
  122. // the timestamp in seconds as string.
  123. func TimestampStr() string {
  124. return Now().TimestampStr()
  125. }
  126. // TimestampMilliStr is a convenience method which retrieves and returns
  127. // the timestamp in milliseconds as string.
  128. func TimestampMilliStr() string {
  129. return Now().TimestampMilliStr()
  130. }
  131. // TimestampMicroStr is a convenience method which retrieves and returns
  132. // the timestamp in microseconds as string.
  133. func TimestampMicroStr() string {
  134. return Now().TimestampMicroStr()
  135. }
  136. // TimestampNanoStr is a convenience method which retrieves and returns
  137. // the timestamp in nanoseconds as string.
  138. func TimestampNanoStr() string {
  139. return Now().TimestampNanoStr()
  140. }
  141. // Second returns the timestamp in seconds.
  142. // Deprecated, use Timestamp instead.
  143. func Second() int64 {
  144. return Timestamp()
  145. }
  146. // Millisecond returns the timestamp in milliseconds.
  147. // Deprecated, use TimestampMilli instead.
  148. func Millisecond() int64 {
  149. return TimestampMilli()
  150. }
  151. // Microsecond returns the timestamp in microseconds.
  152. // Deprecated, use TimestampMicro instead.
  153. func Microsecond() int64 {
  154. return TimestampMicro()
  155. }
  156. // Nanosecond returns the timestamp in nanoseconds.
  157. // Deprecated, use TimestampNano instead.
  158. func Nanosecond() int64 {
  159. return TimestampNano()
  160. }
  161. // Date returns current date in string like "2006-01-02".
  162. func Date() string {
  163. return time.Now().Format("2006-01-02")
  164. }
  165. // Datetime returns current datetime in string like "2006-01-02 15:04:05".
  166. func Datetime() string {
  167. return time.Now().Format("2006-01-02 15:04:05")
  168. }
  169. // ISO8601 returns current datetime in ISO8601 format like "2006-01-02T15:04:05-07:00".
  170. func ISO8601() string {
  171. return time.Now().Format("2006-01-02T15:04:05-07:00")
  172. }
  173. // RFC822 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST".
  174. func RFC822() string {
  175. return time.Now().Format("Mon, 02 Jan 06 15:04 MST")
  176. }
  177. // parseDateStr parses the string to year, month and day numbers.
  178. func parseDateStr(s string) (year, month, day int) {
  179. array := strings.Split(s, "-")
  180. if len(array) < 3 {
  181. array = strings.Split(s, "/")
  182. }
  183. if len(array) < 3 {
  184. array = strings.Split(s, ".")
  185. }
  186. // Parsing failed.
  187. if len(array) < 3 {
  188. return
  189. }
  190. // Checking the year in head or tail.
  191. if utils.IsNumeric(array[1]) {
  192. year, _ = strconv.Atoi(array[0])
  193. month, _ = strconv.Atoi(array[1])
  194. day, _ = strconv.Atoi(array[2])
  195. } else {
  196. if v, ok := monthMap[strings.ToLower(array[1])]; ok {
  197. month = v
  198. } else {
  199. return
  200. }
  201. year, _ = strconv.Atoi(array[2])
  202. day, _ = strconv.Atoi(array[0])
  203. }
  204. return
  205. }
  206. // StrToTime converts string to *Time object. It also supports timestamp string.
  207. // The parameter <format> is unnecessary, which specifies the format for converting like "Y-m-d H:i:s".
  208. // If <format> is given, it acts as same as function StrToTimeFormat.
  209. // If <format> is not given, it converts string as a "standard" datetime string.
  210. // Note that, it fails and returns error if there's no date string in <str>.
  211. func StrToTime(str string, format ...string) (*Time, error) {
  212. if str == "" {
  213. return &Time{wrapper{time.Time{}}}, nil
  214. }
  215. if len(format) > 0 {
  216. return StrToTimeFormat(str, format[0])
  217. }
  218. if isTimestampStr(str) {
  219. timestamp, _ := strconv.ParseInt(str, 10, 64)
  220. return NewFromTimeStamp(timestamp), nil
  221. }
  222. var (
  223. year, month, day int
  224. hour, min, sec, nsec int
  225. match []string
  226. local = time.Local
  227. )
  228. if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" {
  229. //for k, v := range match {
  230. // match[k] = strings.TrimSpace(v)
  231. //}
  232. year, month, day = parseDateStr(match[1])
  233. } else if match = timeRegex2.FindStringSubmatch(str); len(match) > 0 && match[1] != "" {
  234. //for k, v := range match {
  235. // match[k] = strings.TrimSpace(v)
  236. //}
  237. year, month, day = parseDateStr(match[1])
  238. } else if match = timeRegex3.FindStringSubmatch(str); len(match) > 0 && match[1] != "" {
  239. //for k, v := range match {
  240. // match[k] = strings.TrimSpace(v)
  241. //}
  242. s := strings.Replace(match[2], ":", "", -1)
  243. if len(s) < 6 {
  244. s += strings.Repeat("0", 6-len(s))
  245. }
  246. hour, _ = strconv.Atoi(match[1])
  247. min, _ = strconv.Atoi(match[2])
  248. sec, _ = strconv.Atoi(match[3])
  249. nsec, _ = strconv.Atoi(match[4])
  250. for i := 0; i < 9-len(match[4]); i++ {
  251. nsec *= 10
  252. }
  253. return NewFromTime(time.Date(0, time.Month(1), 1, hour, min, sec, nsec, local)), nil
  254. } else {
  255. return nil, gerror.NewCode(gcode.CodeInvalidParameter, "unsupported time format")
  256. }
  257. // Time
  258. if len(match[2]) > 0 {
  259. s := strings.Replace(match[2], ":", "", -1)
  260. if len(s) < 6 {
  261. s += strings.Repeat("0", 6-len(s))
  262. }
  263. hour, _ = strconv.Atoi(s[0:2])
  264. min, _ = strconv.Atoi(s[2:4])
  265. sec, _ = strconv.Atoi(s[4:6])
  266. }
  267. // Nanoseconds, check and perform bit filling
  268. if len(match[3]) > 0 {
  269. nsec, _ = strconv.Atoi(match[3])
  270. for i := 0; i < 9-len(match[3]); i++ {
  271. nsec *= 10
  272. }
  273. }
  274. // If there's zone information in the string,
  275. // it then performs time zone conversion, which converts the time zone to UTC.
  276. if match[4] != "" && match[6] == "" {
  277. match[6] = "000000"
  278. }
  279. // If there's offset in the string, it then firstly processes the offset.
  280. if match[6] != "" {
  281. zone := strings.Replace(match[6], ":", "", -1)
  282. zone = strings.TrimLeft(zone, "+-")
  283. if len(zone) <= 6 {
  284. zone += strings.Repeat("0", 6-len(zone))
  285. h, _ := strconv.Atoi(zone[0:2])
  286. m, _ := strconv.Atoi(zone[2:4])
  287. s, _ := strconv.Atoi(zone[4:6])
  288. if h > 24 || m > 59 || s > 59 {
  289. return nil, gerror.NewCodef(gcode.CodeInvalidParameter, "invalid zone string: %s", match[6])
  290. }
  291. // Comparing the given time zone whether equals to current time zone,
  292. // it converts it to UTC if they does not equal.
  293. _, localOffset := time.Now().Zone()
  294. // Comparing in seconds.
  295. if (h*3600 + m*60 + s) != localOffset {
  296. local = time.UTC
  297. // UTC conversion.
  298. operation := match[5]
  299. if operation != "+" && operation != "-" {
  300. operation = "-"
  301. }
  302. switch operation {
  303. case "+":
  304. if h > 0 {
  305. hour -= h
  306. }
  307. if m > 0 {
  308. min -= m
  309. }
  310. if s > 0 {
  311. sec -= s
  312. }
  313. case "-":
  314. if h > 0 {
  315. hour += h
  316. }
  317. if m > 0 {
  318. min += m
  319. }
  320. if s > 0 {
  321. sec += s
  322. }
  323. }
  324. }
  325. }
  326. }
  327. if month <= 0 || day <= 0 {
  328. return nil, gerror.NewCodef(gcode.CodeInvalidParameter, "invalid time string:%s", str)
  329. }
  330. return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil
  331. }
  332. // ConvertZone converts time in string <strTime> from <fromZone> to <toZone>.
  333. // The parameter <fromZone> is unnecessary, it is current time zone in default.
  334. func ConvertZone(strTime string, toZone string, fromZone ...string) (*Time, error) {
  335. t, err := StrToTime(strTime)
  336. if err != nil {
  337. return nil, err
  338. }
  339. if len(fromZone) > 0 {
  340. if l, err := time.LoadLocation(fromZone[0]); err != nil {
  341. return nil, err
  342. } else {
  343. t.Time = time.Date(t.Year(), time.Month(t.Month()), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l)
  344. }
  345. }
  346. if l, err := time.LoadLocation(toZone); err != nil {
  347. return nil, err
  348. } else {
  349. return t.ToLocation(l), nil
  350. }
  351. }
  352. // StrToTimeFormat parses string <str> to *Time object with given format <format>.
  353. // The parameter <format> is like "Y-m-d H:i:s".
  354. func StrToTimeFormat(str string, format string) (*Time, error) {
  355. return StrToTimeLayout(str, formatToStdLayout(format))
  356. }
  357. // StrToTimeLayout parses string <str> to *Time object with given format <layout>.
  358. // The parameter <layout> is in stdlib format like "2006-01-02 15:04:05".
  359. func StrToTimeLayout(str string, layout string) (*Time, error) {
  360. if t, err := time.ParseInLocation(layout, str, time.Local); err == nil {
  361. return NewFromTime(t), nil
  362. } else {
  363. return nil, err
  364. }
  365. }
  366. // ParseTimeFromContent retrieves time information for content string, it then parses and returns it
  367. // as *Time object.
  368. // It returns the first time information if there're more than one time string in the content.
  369. // It only retrieves and parses the time information with given <format> if it's passed.
  370. func ParseTimeFromContent(content string, format ...string) *Time {
  371. if len(format) > 0 {
  372. if match, err := gregex.MatchString(formatToRegexPattern(format[0]), content); err == nil && len(match) > 0 {
  373. return NewFromStrFormat(match[0], format[0])
  374. }
  375. } else {
  376. if match := timeRegex1.FindStringSubmatch(content); len(match) >= 1 {
  377. return NewFromStr(strings.Trim(match[0], "./_- \n\r"))
  378. } else if match := timeRegex2.FindStringSubmatch(content); len(match) >= 1 {
  379. return NewFromStr(strings.Trim(match[0], "./_- \n\r"))
  380. } else if match := timeRegex3.FindStringSubmatch(content); len(match) >= 1 {
  381. return NewFromStr(strings.Trim(match[0], "./_- \n\r"))
  382. }
  383. }
  384. return nil
  385. }
  386. // ParseDuration parses a duration string.
  387. // A duration string is a possibly signed sequence of
  388. // decimal numbers, each with optional fraction and a unit suffix,
  389. // such as "300ms", "-1.5h", "1d" or "2h45m".
  390. // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d".
  391. //
  392. // Very note that it supports unit "d" more than function time.ParseDuration.
  393. func ParseDuration(s string) (time.Duration, error) {
  394. if utils.IsNumeric(s) {
  395. v, err := strconv.ParseInt(s, 10, 64)
  396. if err != nil {
  397. return 0, err
  398. }
  399. return time.Duration(v), nil
  400. }
  401. match, err := gregex.MatchString(`^([\-\d]+)[dD](.*)$`, s)
  402. if err != nil {
  403. return 0, err
  404. }
  405. if len(match) == 3 {
  406. v, err := strconv.ParseInt(match[1], 10, 64)
  407. if err != nil {
  408. return 0, err
  409. }
  410. return time.ParseDuration(fmt.Sprintf(`%dh%s`, v*24, match[2]))
  411. }
  412. return time.ParseDuration(s)
  413. }
  414. // FuncCost calculates the cost time of function <f> in nanoseconds.
  415. func FuncCost(f func()) int64 {
  416. t := TimestampNano()
  417. f()
  418. return TimestampNano() - t
  419. }
  420. // isTimestampStr checks and returns whether given string a timestamp string.
  421. func isTimestampStr(s string) bool {
  422. length := len(s)
  423. if length == 0 {
  424. return false
  425. }
  426. for i := 0; i < len(s); i++ {
  427. if s[i] < '0' || s[i] > '9' {
  428. return false
  429. }
  430. }
  431. return true
  432. }