time.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package util
  2. import (
  3. "errors"
  4. "github.com/olebedev/when"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "time"
  9. )
  10. var (
  11. errUnparsableTime = errors.New("unable to parse time")
  12. durationStrRegex = regexp.MustCompile(`(?i)^(\d+)\s*(d|days?|h|hours?|m|mins?|minutes?|s|secs?|seconds?)$`)
  13. )
  14. func ParseFutureTime(s string, now time.Time) (time.Time, error) {
  15. s = strings.TrimSpace(s)
  16. t, err := parseUnixTime(s, now)
  17. if err == nil {
  18. return t, nil
  19. }
  20. t, err = parseFromDuration(s, now)
  21. if err == nil {
  22. return t, nil
  23. }
  24. t, err = parseNaturalTime(s, now)
  25. if err == nil {
  26. return t, nil
  27. }
  28. return time.Time{}, errUnparsableTime
  29. }
  30. func parseFromDuration(s string, now time.Time) (time.Time, error) {
  31. d, err := parseDuration(s)
  32. if err == nil {
  33. return now.Add(d), nil
  34. }
  35. return time.Time{}, errUnparsableTime
  36. }
  37. func parseDuration(s string) (time.Duration, error) {
  38. d, err := time.ParseDuration(s)
  39. if err == nil {
  40. return d, nil
  41. }
  42. matches := durationStrRegex.FindStringSubmatch(s)
  43. if matches != nil {
  44. number, err := strconv.Atoi(matches[1])
  45. if err != nil {
  46. return 0, errUnparsableTime
  47. }
  48. switch unit := matches[2][0:1]; unit {
  49. case "d":
  50. return time.Duration(number) * 24 * time.Hour, nil
  51. case "h":
  52. return time.Duration(number) * time.Hour, nil
  53. case "m":
  54. return time.Duration(number) * time.Minute, nil
  55. case "s":
  56. return time.Duration(number) * time.Second, nil
  57. default:
  58. return 0, errUnparsableTime
  59. }
  60. }
  61. return 0, errUnparsableTime
  62. }
  63. func parseUnixTime(s string, now time.Time) (time.Time, error) {
  64. t, err := strconv.Atoi(s)
  65. if err != nil {
  66. return time.Time{}, err
  67. } else if int64(t) < now.Unix() {
  68. return time.Time{}, errUnparsableTime
  69. }
  70. return time.Unix(int64(t), 0).UTC(), nil
  71. }
  72. func parseNaturalTime(s string, now time.Time) (time.Time, error) {
  73. r, err := when.EN.Parse(s, now) // returns "nil, nil" if no matches!
  74. if err != nil || r == nil {
  75. return time.Time{}, errUnparsableTime
  76. } else if r.Time.After(now) {
  77. return r.Time, nil
  78. }
  79. // Hack: If the time is parsable, but not in the future,
  80. // simply append "tomorrow, " to it.
  81. r, err = when.EN.Parse("tomorrow, "+s, now) // returns "nil, nil" if no matches!
  82. if err != nil || r == nil {
  83. return time.Time{}, errUnparsableTime
  84. } else if r.Time.After(now) {
  85. return r.Time, nil
  86. }
  87. return time.Time{}, errUnparsableTime
  88. }