dates.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. import type { Translator } from './translator'
  3. const formatNumber = (num: number, digits: number): string => {
  4. let result = num.toString()
  5. while (result.length < digits) {
  6. result = `0${result}`
  7. }
  8. return result
  9. }
  10. export const absoluteDateTime = (
  11. dateTimeString: string,
  12. template: string,
  13. ): string => {
  14. let date = new Date(dateTimeString)
  15. // On firefox the Date constructor does not recongise date format that
  16. // ends with UTC, instead it returns a NaN (Invalid Date Format) this
  17. // block serves as polyfill to support time format that ends UTC in firefox
  18. if (Number.isNaN(date.getDate())) {
  19. // works for only time string with this format: 2021-02-08 09:13:20 UTC
  20. const timeArray = dateTimeString.match(/\d+/g) || []
  21. const [y, m, d, H, M] = timeArray.map((value) => {
  22. return parseInt(value, 10)
  23. })
  24. date = new Date(Date.UTC(y, m - 1, d, H, M))
  25. }
  26. const d = date.getDate()
  27. const m = date.getMonth() + 1
  28. const H = date.getHours()
  29. const yfull = date.getFullYear().toString()
  30. const yshort = yfull.substring(yfull.length - 2)
  31. const lnum = ((H + 11) % 12) + 1
  32. const l = lnum < 10 ? ` ${lnum}` : lnum
  33. return template
  34. .replace('dd', formatNumber(d, 2))
  35. .replace('d', d.toString())
  36. .replace('mm', formatNumber(m, 2))
  37. .replace('m', m.toString())
  38. .replace('yyyy', yfull)
  39. .replace('yy', yshort)
  40. .replace('SS', formatNumber(date.getSeconds(), 2))
  41. .replace('MM', formatNumber(date.getMinutes(), 2))
  42. .replace('HH', formatNumber(H, 2))
  43. .replace('l', l.toString())
  44. .replace('P', H >= 12 ? 'pm' : 'am')
  45. }
  46. const durationMinute = 60
  47. const durationHour = 60 * durationMinute
  48. const durationDay = 24 * durationHour
  49. const durationWeek = 7 * durationDay
  50. const durationMonth = 30 * durationDay
  51. const durationYear = 356 * durationDay
  52. enum Direction {
  53. Past,
  54. Future,
  55. }
  56. type DurationMessages = {
  57. duration: number
  58. pastSingular: string
  59. pastPlural: string
  60. futureSingular: string
  61. futurePlural: string
  62. }
  63. const durations: DurationMessages[] = [
  64. {
  65. duration: durationYear,
  66. pastSingular: __('1 year ago'),
  67. pastPlural: __('%s years ago'),
  68. futureSingular: __('in 1 year'),
  69. futurePlural: __('in %s years'),
  70. },
  71. {
  72. duration: durationMonth,
  73. pastSingular: __('1 month ago'),
  74. pastPlural: __('%s months ago'),
  75. futureSingular: __('in 1 month'),
  76. futurePlural: __('in %s months'),
  77. },
  78. {
  79. duration: durationWeek,
  80. pastSingular: __('1 week ago'),
  81. pastPlural: __('%s weeks ago'),
  82. futureSingular: __('in 1 week'),
  83. futurePlural: __('in %s weeks'),
  84. },
  85. {
  86. duration: durationDay,
  87. pastSingular: __('1 day ago'),
  88. pastPlural: __('%s days ago'),
  89. futureSingular: __('in 1 day'),
  90. futurePlural: __('in %s days'),
  91. },
  92. {
  93. duration: durationHour,
  94. pastSingular: __('1 hour ago'),
  95. pastPlural: __('%s hours ago'),
  96. futureSingular: __('in 1 hour'),
  97. futurePlural: __('in %s hours'),
  98. },
  99. {
  100. duration: durationMinute,
  101. pastSingular: __('1 minute ago'),
  102. pastPlural: __('%s minutes ago'),
  103. futureSingular: __('in 1 minute'),
  104. futurePlural: __('in %s minutes'),
  105. },
  106. ]
  107. export const relativeDateTime = (
  108. dateTimeString: string,
  109. baseDate: Date,
  110. translator: Translator,
  111. ): string => {
  112. const date = new Date(dateTimeString)
  113. let diffSeconds = (baseDate.getTime() - date.getTime()) / 1000
  114. const direction: Direction =
  115. diffSeconds > -1 ? Direction.Past : Direction.Future
  116. diffSeconds = Math.abs(diffSeconds)
  117. for (const duration of durations) {
  118. if (diffSeconds >= duration.duration) {
  119. const count = Math.floor(diffSeconds / duration.duration)
  120. if (direction === Direction.Past) {
  121. return count === 1
  122. ? translator.translate(duration.pastSingular)
  123. : translator.translate(duration.pastPlural, count)
  124. }
  125. return count === 1
  126. ? translator.translate(duration.futureSingular)
  127. : translator.translate(duration.futurePlural, count)
  128. }
  129. }
  130. return translator.translate('just now')
  131. }
  132. export const getDateFormat = (translator: Translator) => {
  133. return translator.lookup('FORMAT_DATE') || 'yyyy-mm-dd'
  134. }
  135. export const getDateTimeFormat = (translator: Translator) => {
  136. return translator.lookup('FORMAT_DATETIME') || 'yyyy-mm-dd HH:MM'
  137. }