timeago.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package api
  2. import (
  3. "fmt"
  4. "math"
  5. "strings"
  6. "time"
  7. )
  8. // Internationalization (i18n)
  9. var Internationalization = map[string]map[string]string{}
  10. func init() {
  11. Internationalization["pt-BR"] = map[string]string{
  12. "future": "em %s",
  13. "past": "%s atrás",
  14. "s": "alguns segundos",
  15. "ss": "%d segundos",
  16. "m": "um minuto",
  17. "mm": "%d minutos",
  18. "h": "uma hora",
  19. "hh": "%d horas",
  20. "d": "um dia",
  21. "dd": "%d dias",
  22. "M": "um mês",
  23. "MM": "%d meses",
  24. "y": "acerca de um ano",
  25. "yy": "%d anos",
  26. }
  27. }
  28. func repeatCount(test bool) int {
  29. if test {
  30. return 1
  31. } else {
  32. return 2
  33. }
  34. }
  35. // FromDuration returns a friendly string representing an approximation of the
  36. // given duration
  37. func TimeAgoFromDuration(d time.Duration, locale ...string) string {
  38. var (
  39. loc = localeparse(locale...)
  40. seconds = int(math.Floor(d.Seconds() + .50))
  41. templates = Internationalization[loc]
  42. minutes, hours, days, months, years int
  43. //
  44. format = func(prefix string, value int) string {
  45. id := strings.Repeat(prefix, repeatCount(value == 1))
  46. return fmt.Sprintf(
  47. templates[id],
  48. value,
  49. )
  50. }
  51. )
  52. switch {
  53. case seconds < 30:
  54. return templates["s"]
  55. case seconds < 60:
  56. return templates["ss"]
  57. }
  58. if minutes = div(seconds, 60); minutes < 60 {
  59. return format("m", minutes)
  60. }
  61. if hours = div(minutes, 60); hours < 24 {
  62. return format("h", hours)
  63. }
  64. if days = div(hours, 24); days < 31 {
  65. return format("d", days)
  66. }
  67. if months = div(days, 31); months < 12 {
  68. return format("M", months)
  69. }
  70. years = div(months, 12)
  71. return format("y", years)
  72. }
  73. func localeparse(locale ...string) (loc string) {
  74. if len(locale) > 0 {
  75. loc = locale[0]
  76. } else {
  77. loc = "pt-BR"
  78. }
  79. return
  80. }
  81. // FromTime returns a friendly string representing the approximate difference
  82. // from the given time and time.Now()
  83. func TimeAgoFromTime(t time.Time, locale ...string) string {
  84. var (
  85. loc = localeparse(locale...)
  86. now = time.Now()
  87. d time.Duration
  88. id string
  89. templates = Internationalization[loc]
  90. )
  91. if t.Before(now) {
  92. d = now.Sub(t)
  93. id = "past"
  94. } else {
  95. d = t.Sub(now)
  96. id = "future"
  97. }
  98. return fmt.Sprintf(templates[id], TimeAgoFromDuration(d, locale...))
  99. }
  100. func div(numerator int, denominator int) int {
  101. rem := numerator % denominator
  102. result := numerator / denominator
  103. if rem >= (denominator / 2) {
  104. result++
  105. }
  106. return result
  107. }
  108. // func round(f float64) int {
  109. // return
  110. // }
  111. // func pluralize(i int, s string) string {
  112. // var buf bytes.Buffer
  113. // buf.WriteString(fmt.Sprintf("%d %s", i, s))
  114. // if i != 1 {
  115. // buf.WriteString("s")
  116. // }
  117. // return buf.String()
  118. // }
  119. // type TimeAgo struct {
  120. // Locale string
  121. // templates map[string]string
  122. // }
  123. // func NewTimeAgo(locale ...string) *TimeAgo {
  124. // return &TimeAgo{
  125. // Locale: L,
  126. // templates: Internationalization[L],
  127. // }
  128. // }
  129. // const (
  130. // minute = 1
  131. // hour = minute * 60
  132. // day = hour * 24
  133. // month = day * 30
  134. // year = day * 365
  135. // // quarter = year / 4
  136. // )