date.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package sprig
  2. import (
  3. "math"
  4. "strconv"
  5. "time"
  6. )
  7. // date formats a date according to the provided format string.
  8. //
  9. // Parameters:
  10. // - fmt: A Go time format string (e.g., "2006-01-02 15:04:05")
  11. // - date: Can be a time.Time, *time.Time, or int/int32/int64 (seconds since UNIX epoch)
  12. //
  13. // If date is not one of the recognized types, the current time is used.
  14. //
  15. // Example usage in templates: {{ now | date "2006-01-02" }}
  16. func date(fmt string, date any) string {
  17. return dateInZone(fmt, date, "Local")
  18. }
  19. // htmlDate formats a date in HTML5 date format (YYYY-MM-DD).
  20. //
  21. // Parameters:
  22. // - date: Can be a time.Time, *time.Time, or int/int32/int64 (seconds since UNIX epoch)
  23. //
  24. // If date is not one of the recognized types, the current time is used.
  25. //
  26. // Example usage in templates: {{ now | htmlDate }}
  27. func htmlDate(date any) string {
  28. return dateInZone("2006-01-02", date, "Local")
  29. }
  30. // htmlDateInZone formats a date in HTML5 date format (YYYY-MM-DD) in the specified timezone.
  31. //
  32. // Parameters:
  33. // - date: Can be a time.Time, *time.Time, or int/int32/int64 (seconds since UNIX epoch)
  34. // - zone: Timezone name (e.g., "UTC", "America/New_York")
  35. //
  36. // If date is not one of the recognized types, the current time is used.
  37. // If the timezone is invalid, UTC is used.
  38. //
  39. // Example usage in templates: {{ now | htmlDateInZone "UTC" }}
  40. func htmlDateInZone(date any, zone string) string {
  41. return dateInZone("2006-01-02", date, zone)
  42. }
  43. // dateInZone formats a date according to the provided format string in the specified timezone.
  44. //
  45. // Parameters:
  46. // - fmt: A Go time format string (e.g., "2006-01-02 15:04:05")
  47. // - date: Can be a time.Time, *time.Time, or int/int32/int64 (seconds since UNIX epoch)
  48. // - zone: Timezone name (e.g., "UTC", "America/New_York")
  49. //
  50. // If date is not one of the recognized types, the current time is used.
  51. // If the timezone is invalid, UTC is used.
  52. //
  53. // Example usage in templates: {{ now | dateInZone "2006-01-02 15:04:05" "UTC" }}
  54. func dateInZone(fmt string, date any, zone string) string {
  55. var t time.Time
  56. switch date := date.(type) {
  57. default:
  58. t = time.Now()
  59. case time.Time:
  60. t = date
  61. case *time.Time:
  62. t = *date
  63. case int64:
  64. t = time.Unix(date, 0)
  65. case int:
  66. t = time.Unix(int64(date), 0)
  67. case int32:
  68. t = time.Unix(int64(date), 0)
  69. }
  70. loc, err := time.LoadLocation(zone)
  71. if err != nil {
  72. loc, _ = time.LoadLocation("UTC")
  73. }
  74. return t.In(loc).Format(fmt)
  75. }
  76. // dateModify modifies a date by adding a duration and returns the resulting time.
  77. //
  78. // Parameters:
  79. // - fmt: A duration string (e.g., "24h", "-12h30m", "1h15m30s")
  80. // - date: The time.Time to modify
  81. //
  82. // If the duration string is invalid, the original date is returned.
  83. //
  84. // Example usage in templates: {{ now | dateModify "-24h" }}
  85. func dateModify(fmt string, date time.Time) time.Time {
  86. d, err := time.ParseDuration(fmt)
  87. if err != nil {
  88. return date
  89. }
  90. return date.Add(d)
  91. }
  92. // mustDateModify modifies a date by adding a duration and returns the resulting time or an error.
  93. //
  94. // Parameters:
  95. // - fmt: A duration string (e.g., "24h", "-12h30m", "1h15m30s")
  96. // - date: The time.Time to modify
  97. //
  98. // Unlike dateModify, this function returns an error if the duration string is invalid.
  99. //
  100. // Example usage in templates: {{ now | mustDateModify "24h" }}
  101. func mustDateModify(fmt string, date time.Time) (time.Time, error) {
  102. d, err := time.ParseDuration(fmt)
  103. if err != nil {
  104. return time.Time{}, err
  105. }
  106. return date.Add(d), nil
  107. }
  108. // dateAgo returns a string representing the time elapsed since the given date.
  109. //
  110. // Parameters:
  111. // - date: Can be a time.Time, int, or int64 (seconds since UNIX epoch)
  112. //
  113. // If date is not one of the recognized types, the current time is used.
  114. //
  115. // Example usage in templates: {{ "2023-01-01" | toDate "2006-01-02" | dateAgo }}
  116. func dateAgo(date any) string {
  117. var t time.Time
  118. switch date := date.(type) {
  119. default:
  120. t = time.Now()
  121. case time.Time:
  122. t = date
  123. case int64:
  124. t = time.Unix(date, 0)
  125. case int:
  126. t = time.Unix(int64(date), 0)
  127. }
  128. return time.Since(t).Round(time.Second).String()
  129. }
  130. // duration converts seconds to a duration string.
  131. //
  132. // Parameters:
  133. // - sec: Can be a string (parsed as int64), or int64 representing seconds
  134. //
  135. // Example usage in templates: {{ 3600 | duration }} -> "1h0m0s"
  136. func duration(sec any) string {
  137. var n int64
  138. switch value := sec.(type) {
  139. default:
  140. n = 0
  141. case string:
  142. n, _ = strconv.ParseInt(value, 10, 64)
  143. case int64:
  144. n = value
  145. }
  146. return (time.Duration(n) * time.Second).String()
  147. }
  148. // durationRound formats a duration in a human-readable rounded format.
  149. //
  150. // Parameters:
  151. // - duration: Can be a string (parsed as duration), int64 (nanoseconds),
  152. // or time.Time (time since that moment)
  153. //
  154. // Returns a string with the largest appropriate unit (y, mo, d, h, m, s).
  155. //
  156. // Example usage in templates: {{ 3600 | duration | durationRound }} -> "1h"
  157. func durationRound(duration any) string {
  158. var d time.Duration
  159. switch duration := duration.(type) {
  160. default:
  161. d = 0
  162. case string:
  163. d, _ = time.ParseDuration(duration)
  164. case int64:
  165. d = time.Duration(duration)
  166. case time.Time:
  167. d = time.Since(duration)
  168. }
  169. u := uint64(math.Abs(float64(d)))
  170. var (
  171. year = uint64(time.Hour) * 24 * 365
  172. month = uint64(time.Hour) * 24 * 30
  173. day = uint64(time.Hour) * 24
  174. hour = uint64(time.Hour)
  175. minute = uint64(time.Minute)
  176. second = uint64(time.Second)
  177. )
  178. switch {
  179. case u > year:
  180. return strconv.FormatUint(u/year, 10) + "y"
  181. case u > month:
  182. return strconv.FormatUint(u/month, 10) + "mo"
  183. case u > day:
  184. return strconv.FormatUint(u/day, 10) + "d"
  185. case u > hour:
  186. return strconv.FormatUint(u/hour, 10) + "h"
  187. case u > minute:
  188. return strconv.FormatUint(u/minute, 10) + "m"
  189. case u > second:
  190. return strconv.FormatUint(u/second, 10) + "s"
  191. }
  192. return "0s"
  193. }
  194. // toDate parses a string into a time.Time using the specified format.
  195. //
  196. // Parameters:
  197. // - fmt: A Go time format string (e.g., "2006-01-02")
  198. // - str: The date string to parse
  199. //
  200. // If parsing fails, returns a zero time.Time.
  201. //
  202. // Example usage in templates: {{ "2023-01-01" | toDate "2006-01-02" }}
  203. func toDate(fmt, str string) time.Time {
  204. t, _ := time.ParseInLocation(fmt, str, time.Local)
  205. return t
  206. }
  207. // mustToDate parses a string into a time.Time using the specified format or returns an error.
  208. //
  209. // Parameters:
  210. // - fmt: A Go time format string (e.g., "2006-01-02")
  211. // - str: The date string to parse
  212. //
  213. // Unlike toDate, this function returns an error if parsing fails.
  214. //
  215. // Example usage in templates: {{ mustToDate "2006-01-02" "2023-01-01" }}
  216. func mustToDate(fmt, str string) (time.Time, error) {
  217. return time.ParseInLocation(fmt, str, time.Local)
  218. }
  219. // unixEpoch returns the Unix timestamp (seconds since January 1, 1970 UTC) for the given time.
  220. //
  221. // Parameters:
  222. // - date: A time.Time value
  223. //
  224. // Example usage in templates: {{ now | unixEpoch }}
  225. func unixEpoch(date time.Time) string {
  226. return strconv.FormatInt(date.Unix(), 10)
  227. }