github.com/webx-top/com@v1.2.12/time.go (about) 1 // Copyright 2013 com authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 package com 16 17 import ( 18 "errors" 19 "fmt" 20 "regexp" 21 "strconv" 22 "strings" 23 "time" 24 ) 25 26 // Date Format unix time int64 to string 27 func Date(ti int64, format string) string { 28 t := time.Unix(int64(ti), 0) 29 return DateT(t, format) 30 } 31 32 // DateS Format unix time string to string 33 func DateS(ts string, format string) string { 34 i, _ := strconv.ParseInt(ts, 10, 64) 35 return Date(i, format) 36 } 37 38 // DateT Format time.Time struct to string 39 // MM - month - 01 40 // M - month - 1, single bit 41 // DD - day - 02 42 // D - day 2 43 // YYYY - year - 2006 44 // YY - year - 06 45 // HH - 24 hours - 03 46 // H - 24 hours - 3 47 // hh - 12 hours - 03 48 // h - 12 hours - 3 49 // mm - minute - 04 50 // m - minute - 4 51 // ss - second - 05 52 // s - second = 5 53 func DateT(t time.Time, format string) string { 54 res := strings.Replace(format, "MM", t.Format("01"), -1) 55 res = strings.Replace(res, "M", t.Format("1"), -1) 56 res = strings.Replace(res, "DD", t.Format("02"), -1) 57 res = strings.Replace(res, "D", t.Format("2"), -1) 58 res = strings.Replace(res, "YYYY", t.Format("2006"), -1) 59 res = strings.Replace(res, "YY", t.Format("06"), -1) 60 res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1) 61 res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1) 62 res = strings.Replace(res, "hh", t.Format("03"), -1) 63 res = strings.Replace(res, "h", t.Format("3"), -1) 64 res = strings.Replace(res, "mm", t.Format("04"), -1) 65 res = strings.Replace(res, "m", t.Format("4"), -1) 66 res = strings.Replace(res, "ss", t.Format("05"), -1) 67 res = strings.Replace(res, "s", t.Format("5"), -1) 68 return res 69 } 70 71 // DateFormat pattern rules. 72 var datePatterns = []string{ 73 // year 74 "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 75 "y", "06", //A two digit representation of a year Examples: 99 or 03 76 // month 77 "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 78 "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 79 "M", "Jan", // A short textual representation of a month, three letters Jan through Dec 80 "F", "January", // A full textual representation of a month, such as January or March January through December 81 // day 82 "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 83 "j", "2", // Day of the month without leading zeros 1 to 31 84 // week 85 "D", "Mon", // A textual representation of a day, three letters Mon through Sun 86 "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday 87 // time 88 "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 89 "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 90 "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 91 "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 92 "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm 93 "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM 94 "i", "04", // Minutes with leading zeros 00 to 59 95 "s", "05", // Seconds, with leading zeros 00 through 59 96 // time zone 97 "T", "MST", 98 "P", "-07:00", 99 "O", "-0700", 100 // RFC 2822 101 "r", time.RFC1123Z, 102 } 103 104 // DateFormatReplacer . 105 var DateFormatReplacer = strings.NewReplacer(datePatterns...) 106 107 // DateParse Parse Date use PHP time format. 108 func DateParse(dateString, format string) (time.Time, error) { 109 return time.ParseInLocation(ConvDateFormat(format), dateString, time.Local) 110 } 111 112 // ConvDateFormat Convert PHP time format. 113 func ConvDateFormat(format string) string { 114 format = DateFormatReplacer.Replace(format) 115 return format 116 } 117 118 // DateFormat 将时间戳格式化为日期字符窜 119 func DateFormat(format string, timestamp interface{}) (t string) { // timestamp 120 switch format { 121 case "Y-m-d H:i:s", "": 122 format = "2006-01-02 15:04:05" 123 case "Y-m-d H:i": 124 format = "2006-01-02 15:04" 125 case "y-m-d H:i": 126 format = "06-01-02 15:04" 127 case "m-d H:i": 128 format = "01-02 15:04" 129 case "Y-m-d": 130 format = "2006-01-02" 131 case "y-m-d": 132 format = "06-01-02" 133 case "m-d": 134 format = "01-02" 135 default: 136 format = ConvDateFormat(format) 137 } 138 sd := Int64(timestamp) 139 t = time.Unix(sd, 0).Format(format) 140 return 141 } 142 143 // StrToTime 日期字符窜转为时间戳数字 144 func StrToTime(str string, args ...string) (unixtime int) { 145 layout := "2006-01-02 15:04:05" 146 if len(args) > 0 { 147 layout = args[0] 148 } 149 t, err := time.ParseInLocation(layout, str, time.Local) 150 if err == nil { 151 unixtime = int(t.Unix()) 152 } else { 153 fmt.Println(err, str) 154 } 155 return 156 } 157 158 // RestoreTime 从字符串还原时间 159 // RestoreTime(`2001-01-01T00:00:03Z`).Format(`2006-01-02 15:04:05`) => 2001-01-01 00:00:03 160 func RestoreTime(str string, args ...string) time.Time { 161 layout := time.RFC3339 162 if len(args) > 0 { 163 layout = args[0] 164 } 165 t, _ := time.ParseInLocation(layout, str, time.Local) 166 return t 167 } 168 169 // FormatByte 兼容以前的版本,FormatBytes别名 170 // @param float64 size 171 // @param int precision 172 // @param bool trimRightZero 173 func FormatByte(args ...interface{}) string { 174 return FormatBytes(args...) 175 } 176 177 var sizeUnits = [...]string{"YB", "ZB", "EB", "PB", "TB", "GB", "MB", "KB", "B"} 178 179 // FormatBytes 格式化字节。 FormatBytes(字节整数,保留小数位数, 是否裁剪掉小数中的后缀0) 180 // 不设置 参数 2 和 参数 3 时,默认保留2为小数并裁剪小数中的后缀0 181 // @param float64 size 182 // @param int precision 183 // @param bool trimRightZero 184 func FormatBytes(args ...interface{}) string { 185 var ( 186 total = len(sizeUnits) 187 size float64 188 precision int 189 trimRightZero bool 190 ) 191 switch len(args) { 192 case 3: 193 trimRightZero = Bool(args[2]) 194 fallthrough 195 case 2: 196 precision = Int(args[1]) 197 size = Float64(args[0]) 198 case 1: 199 trimRightZero = true 200 precision = 2 201 size = Float64(args[0]) 202 } 203 for total--; total > 0 && size > 1024.0; total-- { 204 size /= 1024.0 205 } 206 r := fmt.Sprintf("%.*f", precision, size) 207 if trimRightZero { 208 r = NumberTrimZero(r) 209 } 210 return r + sizeUnits[total] 211 } 212 213 // DateFormatShort 格式化耗时 214 func DateFormatShort(timestamp interface{}) string { 215 now := time.Now() 216 year := now.Year() 217 month := now.Month() 218 day := now.Day() 219 cTime := StrToTime(fmt.Sprintf(`%d-%.2d-%.2d 00:00:00`, year, month, day)) //月、日始终保持两位 220 timestamp2 := Int(timestamp) 221 if cTime < timestamp2 { 222 return DateFormat("15:04", timestamp) 223 } 224 cTime = StrToTime(fmt.Sprintf(`%d-01-01 00:00:00`, year)) 225 if cTime < timestamp2 { 226 return DateFormat("01-02", timestamp) 227 } 228 return DateFormat("06-01-02", timestamp) 229 } 230 231 // FormatPastTime 格式化耗时 232 // @param number timestamp 233 // @param string args[0] 时间格式 234 // @param string args[1] 已过去时间后缀 235 // @param string args[2] 语种 236 func FormatPastTime(timestamp interface{}, args ...string) string { 237 argSize := len(args) 238 duration := time.Since(time.Unix(Int64(timestamp), 0)) 239 if duration >= time.Hour*24 { 240 format := "Y-m-d H:i:s" 241 if argSize > 0 && len(args[0]) > 0 { 242 format = args[0] 243 } 244 return DateFormat(format, timestamp) 245 } 246 var suffix string 247 var language string 248 if argSize > 1 { 249 suffix = args[1] 250 if len(suffix) > 0 && !HasChineseFirst(suffix) { 251 suffix = ` ` + suffix 252 } 253 if argSize > 2 { 254 language = args[2] 255 } 256 } 257 return FriendlyTime(duration, suffix, 0, true, language) 258 } 259 260 // FriendlyTime 对人类友好的经历时间格式 261 // @param time.Duration d 262 // @param string suffix 263 // @param int precision 264 // @param bool trimRightZero 265 // @param string language 266 func FriendlyTime(d time.Duration, args ...interface{}) (r string) { 267 var suffix string 268 var trimRightZero bool 269 var language string 270 precision := 2 271 switch len(args) { 272 case 4: 273 language = String(args[3]) 274 fallthrough 275 case 3: 276 trimRightZero = Bool(args[2]) 277 fallthrough 278 case 2: 279 precision = Int(args[1]) 280 fallthrough 281 case 1: 282 suffix = String(args[0]) 283 } 284 var unit string 285 var divisor float64 286 u := uint64(d) 287 if u < uint64(time.Second) { 288 switch { 289 case u == 0: 290 unit = `s` 291 case u < uint64(time.Microsecond): 292 unit = `ns` //纳秒 293 divisor = 1 294 case u < uint64(time.Millisecond): 295 unit = `us` //微秒 296 divisor = 1000 297 default: 298 unit = `ms` //毫秒 299 divisor = 1000 * 1000 300 } 301 } else { 302 divisor = 1000 * 1000 * 1000 303 switch { 304 case u < uint64(time.Minute): 305 unit = `s` //秒 306 case u < uint64(time.Hour): 307 unit = `m` //分钟 308 divisor *= 60 309 case u < uint64(time.Hour)*24: 310 unit = `h` //小时 311 divisor *= 60 * 60 312 case u < uint64(time.Hour)*24*7: 313 unit = `d` //天 314 divisor *= 60 * 60 * 24 315 default: 316 unit = `w` //周 317 divisor *= 60 * 60 * 24 * 7 318 } 319 } 320 if divisor > 0 { 321 r = fmt.Sprintf("%.*f", precision, float64(u)/divisor) 322 if trimRightZero { 323 r = NumberTrimZero(r) 324 } 325 } else { 326 r = `0` 327 } 328 if len(language) > 0 { 329 units, ok := TimeShortUnits[language] 330 if ok { 331 if ut, ok := units[unit]; ok { 332 unit = ut 333 } 334 } 335 } 336 r += unit + suffix 337 return 338 } 339 340 // StartTime 开始时间 341 var StartTime = time.Now() 342 343 // TotalRunTime 总运行时长 344 func TotalRunTime() string { 345 return FriendlyTime(time.Since(StartTime)) 346 } 347 348 var ( 349 timeUnits = []string{"years", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"} 350 timeUnitsZhCN = map[string]string{"years": "年", "weeks": "周", "days": "天", "hours": "小时", "minutes": "分", "seconds": "秒", "milliseconds": "毫秒"} 351 // TimeUnits 多语言时间单位 352 TimeUnits = map[string]map[string]string{`zh-cn`: timeUnitsZhCN} 353 354 // TimeShortUnits 时间单位(简写) 355 TimeShortUnits = map[string]map[string]string{ 356 `zh-cn`: {`s`: `秒`, `ns`: `纳秒`, `us`: `微秒`, `ms`: `毫秒`, `m`: `分钟`, `h`: `小时`, `d`: `天`, `w`: `周`}, 357 } 358 ) 359 360 // Durafmt holds the parsed duration and the original input duration. 361 type Durafmt struct { 362 duration time.Duration 363 input string // Used as reference. 364 units map[string]string 365 } 366 367 func getDurationUnits(args []interface{}) map[string]string { 368 var units map[string]string 369 if len(args) > 0 { 370 switch v := args[0].(type) { 371 case map[string]string: 372 units = v 373 case string: 374 var ok bool 375 units, ok = TimeUnits[v] 376 if ok { 377 return units 378 } 379 switch strings.ToLower(v) { 380 case `zh_cn`, `zh-cn`: 381 units = timeUnitsZhCN 382 } 383 } 384 } 385 return units 386 } 387 388 // ParseDuration creates a new *Durafmt struct, returns error if input is invalid. 389 func ParseDuration(dinput time.Duration, args ...interface{}) *Durafmt { 390 input := dinput.String() 391 return &Durafmt{dinput, input, getDurationUnits(args)} 392 } 393 394 // ParseDurationString creates a new *Durafmt struct from a string. 395 // returns an error if input is invalid. 396 func ParseDurationString(input string, args ...interface{}) (*Durafmt, error) { 397 if input == "0" || input == "-0" { 398 return nil, errors.New("durafmt: missing unit in duration " + input) 399 } 400 duration, err := time.ParseDuration(input) 401 if err != nil { 402 return nil, err 403 } 404 return &Durafmt{duration, input, getDurationUnits(args)}, nil 405 } 406 407 func (d *Durafmt) Duration() time.Duration { 408 return d.duration 409 } 410 411 // String parses d *Durafmt into a human readable duration. 412 func (d *Durafmt) String() string { 413 var duration string 414 415 // Check for minus durations. 416 if string(d.input[0]) == "-" { 417 duration += "-" 418 d.duration = -d.duration 419 } 420 421 // Convert duration. 422 seconds := int64(d.duration.Seconds()) % 60 423 minutes := int64(d.duration.Minutes()) % 60 424 hours := int64(d.duration.Hours()) % 24 425 days := int64(d.duration/(24*time.Hour)) % 365 % 7 426 weeks := int64(d.duration/(24*time.Hour)) / 7 % 52 427 years := int64(d.duration/(24*time.Hour)) / 365 428 milliseconds := int64(d.duration/time.Millisecond) - 429 (seconds * 1000) - (minutes * 60000) - (hours * 3600000) - 430 (days * 86400000) - (weeks * 604800000) - (years * 31536000000) 431 432 // Create a map of the converted duration time. 433 durationMap := map[string]int64{ 434 "milliseconds": milliseconds, 435 "seconds": seconds, 436 "minutes": minutes, 437 "hours": hours, 438 "days": days, 439 "weeks": weeks, 440 "years": years, 441 } 442 443 // Construct duration string. 444 for _, u := range timeUnits { 445 v := durationMap[u] 446 if customLable, ok := d.units[u]; ok { 447 u = customLable 448 } 449 strval := strconv.FormatInt(v, 10) 450 switch { 451 // add to the duration string if v > 1. 452 case v > 1: 453 duration += strval + " " + u + " " 454 // remove the plural 's', if v is 1. 455 case v == 1: 456 duration += strval + " " + strings.TrimRight(u, "s") + " " 457 // omit any value with 0s or 0. 458 case d.duration.String() == "0" || d.duration.String() == "0s": 459 // note: milliseconds and minutes have the same suffix (m) 460 // so we have to check if the units match with the suffix. 461 462 // check for a suffix that is NOT the milliseconds suffix. 463 if strings.HasSuffix(d.input, string(u[0])) && !strings.Contains(d.input, "ms") { 464 // if it happens that the units are milliseconds, skip. 465 if u == "milliseconds" { 466 continue 467 } 468 duration += strval + " " + u 469 } 470 // process milliseconds here. 471 if u == "milliseconds" { 472 if strings.Contains(d.input, "ms") { 473 duration += strval + " " + u 474 break 475 } 476 } 477 break 478 // omit any value with 0. 479 case v == 0: 480 continue 481 } 482 } 483 // trim any remaining spaces. 484 duration = strings.TrimSpace(duration) 485 return duration 486 } 487 488 // NowStr 当前时间字符串 489 func NowStr() string { 490 return time.Now().Format(`2006-01-02 15:04:05`) 491 } 492 493 func NewTime(t time.Time) *Time { 494 return &Time{Time: t} 495 } 496 497 type Time struct { 498 time.Time 499 } 500 501 func (t Time) ParseTimestamp(timestamp interface{}) time.Time { 502 return time.Unix(Int64(timestamp), 0) 503 } 504 505 func (t Time) SubTimestamp(timestamp interface{}) time.Duration { 506 return t.Sub(t.ParseTimestamp(timestamp)) 507 } 508 509 func (t Time) IsToday(timestamp interface{}) bool { 510 st := t.ParseTimestamp(timestamp) 511 return st.Day() == t.Day() && st.Month() == t.Month() && st.Year() == t.Year() 512 } 513 514 func (t Time) IsThisMonth(timestamp interface{}) bool { 515 st := t.ParseTimestamp(timestamp) 516 return st.Month() == t.Month() && st.Year() == t.Year() 517 } 518 519 func (t Time) IsThisYear(timestamp interface{}) bool { 520 st := t.ParseTimestamp(timestamp) 521 return st.Year() == t.Year() 522 } 523 524 func (t Time) IsAgo(timestamp interface{}, days int, units ...int) bool { 525 unit := 86400 526 if len(units) > 0 { 527 unit = units[0] 528 } 529 return t.SubTimestamp(timestamp) > time.Second*time.Duration(days*unit) 530 } 531 532 func (t Time) IsFuture(timestamp interface{}, days int, units ...int) bool { 533 unit := 86400 534 if len(units) > 0 { 535 unit = units[0] 536 } 537 st := t.ParseTimestamp(timestamp) 538 return t.Unix()+int64(days*unit) <= st.Unix() 539 } 540 541 func (t Time) IsAfter(timestamp interface{}, agoDays int, units ...int) bool { 542 unit := 86400 543 if len(units) > 0 { 544 unit = units[0] 545 } 546 st := t.ParseTimestamp(timestamp) 547 return t.Unix()-int64(agoDays*unit) <= st.Unix() 548 } 549 550 var numberSpitRule = regexp.MustCompile(`[^\d]+`) 551 var MinYear = 1000 552 var MaxYear = 9999 553 554 func FixDateString(dateStr string) string { 555 parts := numberSpitRule.Split(dateStr, 3) 556 dateArr := make([]string, 3) 557 switch len(parts) { 558 case 3: 559 // day 560 if len(parts[2]) < 2 { 561 parts[2] = `0` + parts[2] 562 } else { 563 day, _ := strconv.Atoi(strings.TrimPrefix(parts[2], `0`)) 564 if day < 0 || day > 31 { 565 return `` 566 } 567 } 568 dateArr[2] = parts[2] 569 fallthrough 570 case 2: 571 // month 572 if len(parts[1]) < 2 { 573 parts[1] = `0` + parts[1] 574 } else { 575 month, _ := strconv.Atoi(strings.TrimPrefix(parts[1], `0`)) 576 if month <= 0 || month > 12 { 577 return `` 578 } 579 } 580 dateArr[1] = parts[1] 581 fallthrough 582 case 1: 583 // year 584 year, _ := strconv.Atoi(parts[0]) 585 if year <= MinYear || year > MaxYear { 586 return `` 587 } 588 dateArr[0] = parts[0] 589 } 590 if len(dateArr[1]) == 0 { 591 dateArr[1] = `01` 592 } 593 if len(dateArr[2]) == 0 { 594 dateArr[2] = `01` 595 } 596 return strings.Join(dateArr, `-`) 597 } 598 599 func FixTimeString(timeStr string) string { 600 parts := numberSpitRule.Split(timeStr, 3) 601 timeArr := make([]string, 3) 602 switch len(parts) { 603 case 3: 604 // second 605 if len(parts[2]) < 2 { 606 parts[2] = `0` + parts[2] 607 } else { 608 seconds, _ := strconv.Atoi(strings.TrimPrefix(parts[2], `0`)) 609 if seconds < 0 || seconds > 59 { 610 return `` 611 } 612 } 613 timeArr[2] = parts[2] 614 fallthrough 615 case 2: 616 // minute 617 if len(parts[1]) < 2 { 618 parts[1] = `0` + parts[1] 619 } else { 620 minutes, _ := strconv.Atoi(strings.TrimPrefix(parts[1], `0`)) 621 if minutes < 0 || minutes > 59 { 622 return `` 623 } 624 } 625 timeArr[1] = parts[1] 626 fallthrough 627 case 1: 628 // hour 629 if len(parts[0]) < 2 { 630 parts[0] = `0` + parts[0] 631 } else { 632 hour, _ := strconv.Atoi(strings.TrimPrefix(parts[0], `0`)) 633 if hour < 0 || hour > 23 { 634 return `` 635 } 636 } 637 timeArr[0] = parts[0] 638 } 639 if len(timeArr[1]) == 0 { 640 timeArr[1] = `00` 641 } 642 if len(timeArr[2]) == 0 { 643 timeArr[2] = `00` 644 } 645 return strings.Join(timeArr, `:`) 646 } 647 648 func FixDateTimeString(dateTimeStr string) []string { 649 parts := strings.SplitN(dateTimeStr, ` `, 2) 650 var dateStr string 651 if len(parts) == 2 { 652 dateStr = FixDateString(parts[0]) 653 if len(dateStr) == 0 { 654 return nil 655 } 656 timeStr := FixTimeString(parts[1]) 657 if len(timeStr) == 0 { 658 return []string{dateStr} 659 } 660 return []string{dateStr, timeStr} 661 } 662 dateStr = FixDateString(parts[0]) 663 if len(dateStr) == 0 { 664 return nil 665 } 666 return []string{dateStr} 667 }