dates.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import moment from 'moment';
  2. import ConfigStore from 'sentry/stores/configStore';
  3. import type {TableDataRow} from './discover/discoverQuery';
  4. // TODO(billy): Move to TimeRangeSelector specific utils
  5. export const DEFAULT_DAY_START_TIME = '00:00:00';
  6. export const DEFAULT_DAY_END_TIME = '23:59:59';
  7. const DATE_FORMAT_NO_TIMEZONE = 'YYYY/MM/DD HH:mm:ss';
  8. function getParser(local: boolean = false): typeof moment | typeof moment.utc {
  9. return local ? moment : moment.utc;
  10. }
  11. /**
  12. * Checks if string is valid time. Only accepts 24 hour format.
  13. *
  14. * Chrome's time input will (at least for US locale), allow you to input 12
  15. * hour format with AM/PM but the raw value is in 24 hour.
  16. *
  17. * Safari does not do any validation so you could get a value of > 24 hours
  18. */
  19. export function isValidTime(str: string): boolean {
  20. return moment(str, 'HH:mm', true).isValid();
  21. }
  22. /**
  23. * Given a date object, format in datetime in UTC
  24. * given: Tue Oct 09 2018 00:00:00 GMT-0700 (Pacific Daylight Time)
  25. * returns: "2018-10-09T07:00:00.000"
  26. */
  27. export function getUtcDateString(dateObj: moment.MomentInput): string {
  28. return moment.utc(dateObj).format(moment.HTML5_FMT.DATETIME_LOCAL_SECONDS);
  29. }
  30. /**
  31. * Given a date field from a table row, return a timestamp
  32. * given: '2024-04-01T20:15:18+00:00'
  33. * returns: 1712002518
  34. */
  35. export function getTimeStampFromTableDateField(
  36. date: keyof TableDataRow | undefined
  37. ): number | undefined {
  38. if (!date) {
  39. return undefined;
  40. }
  41. if (typeof date === 'number') {
  42. return date;
  43. }
  44. const timestamp = new Date(date).getTime();
  45. if (isNaN(timestamp)) {
  46. throw new Error('Invalid timestamp: NaN');
  47. }
  48. return timestamp / 1000;
  49. }
  50. export function getFormattedDate(
  51. dateObj: moment.MomentInput,
  52. format: string,
  53. {local}: {local?: boolean} = {}
  54. ): string {
  55. return getParser(local)(dateObj).format(format);
  56. }
  57. /**
  58. * Returns user timezone from their account preferences
  59. */
  60. export function getUserTimezone(): string {
  61. return ConfigStore.get('user')?.options?.timezone;
  62. }
  63. /**
  64. * Given a UTC date, return a Date object in local time
  65. */
  66. export function getUtcToLocalDateObject(date: moment.MomentInput): Date {
  67. return moment.utc(date).local().toDate();
  68. }
  69. /**
  70. * Sets time (hours + minutes) of the current date object
  71. *
  72. * @param timeStr Time in 24hr format (HH:mm)
  73. */
  74. export function setDateToTime(
  75. dateObj: string | Date,
  76. timeStr: string,
  77. {local}: {local?: boolean} = {}
  78. ): Date {
  79. const [hours, minutes, seconds] = timeStr.split(':').map(t => parseInt(t, 10));
  80. const date = new Date(+dateObj);
  81. if (local) {
  82. date.setHours(hours, minutes);
  83. } else {
  84. date.setUTCHours(hours, minutes);
  85. }
  86. if (typeof seconds !== 'undefined') {
  87. date.setSeconds(seconds);
  88. }
  89. return date;
  90. }
  91. /**
  92. * Given a UTC timestamp, return a system date object with the same date
  93. * e.g. given: system is -0700 (PST),
  94. * 1/1/2001 @ 22:00 UTC, return: 1/1/2001 @ 22:00 -0700 (PST)
  95. */
  96. export function getUtcToSystem(dateObj: moment.MomentInput): Date {
  97. // This is required because if your system timezone !== user configured timezone
  98. // then there will be a mismatch of dates with `react-date-picker`
  99. //
  100. // We purposely strip the timezone when formatting from the utc timezone
  101. return new Date(moment.utc(dateObj).format(DATE_FORMAT_NO_TIMEZONE));
  102. }
  103. /**
  104. * Given a timestamp, format to user preference timezone, and strip timezone to
  105. * return a system date object with the same date
  106. *
  107. * e.g. given: system is -0700 (PST) and user preference is -0400 (EST),
  108. * 1/1/2001 @ 22:00 UTC --> 1/1/2001 @ 18:00 -0400 (EST) -->
  109. * return: 1/1/2001 @ 18:00 -0700 (PST)
  110. */
  111. export function getLocalToSystem(dateObj: moment.MomentInput): Date {
  112. // This is required because if your system timezone !== user configured timezone
  113. // then there will be a mismatch of dates with `react-date-picker`
  114. //
  115. // We purposely strip the timezone when formatting from the utc timezone
  116. return new Date(moment(dateObj).format(DATE_FORMAT_NO_TIMEZONE));
  117. }
  118. /**
  119. * Get the beginning of day (e.g. midnight)
  120. */
  121. export function getStartOfDay(date: moment.MomentInput): Date {
  122. return moment(date)
  123. .startOf('day')
  124. .startOf('hour')
  125. .startOf('minute')
  126. .startOf('second')
  127. .local()
  128. .toDate();
  129. }
  130. /**
  131. * Get tomorrow at midnight so that default endtime is inclusive of today
  132. */
  133. export function getEndOfDay(date: moment.MomentInput): Date {
  134. return moment(date)
  135. .add(1, 'day')
  136. .startOf('hour')
  137. .startOf('minute')
  138. .startOf('second')
  139. .subtract(1, 'second')
  140. .local()
  141. .toDate();
  142. }
  143. export function getPeriodAgo(
  144. period: moment.unitOfTime.DurationConstructor,
  145. unit: number
  146. ): moment.Moment {
  147. return moment().local().subtract(unit, period);
  148. }
  149. /**
  150. * Get the start of the day (midnight) for a period ago
  151. *
  152. * e.g. 2 weeks ago at midnight
  153. */
  154. export function getStartOfPeriodAgo(
  155. period: moment.unitOfTime.DurationConstructor,
  156. unit: number
  157. ): Date {
  158. return getStartOfDay(getPeriodAgo(period, unit));
  159. }
  160. /**
  161. * Does the user prefer a 24 hour clock?
  162. */
  163. export function shouldUse24Hours() {
  164. return ConfigStore.get('user')?.options?.clock24Hours;
  165. }
  166. /**
  167. * Get a common date format
  168. */
  169. export function getDateFormat({year}: {year?: boolean}) {
  170. // "Jan 1, 2022" or "Jan 1"
  171. return year ? 'MMM D, YYYY' : 'MMM D';
  172. }
  173. /**
  174. * Get a common time format
  175. */
  176. export function getTimeFormat({
  177. clock24Hours,
  178. seconds,
  179. timeZone,
  180. }: {
  181. clock24Hours?: boolean;
  182. seconds?: boolean;
  183. timeZone?: boolean;
  184. } = {}) {
  185. let format = '';
  186. if (clock24Hours ?? shouldUse24Hours()) {
  187. format = seconds ? 'HH:mm:ss' : 'HH:mm';
  188. } else {
  189. format = seconds ? 'LTS' : 'LT';
  190. }
  191. if (timeZone) {
  192. format += ' z';
  193. }
  194. return format;
  195. }
  196. interface FormatProps {
  197. /**
  198. * Should show a 24hour clock? If not set the users preference will be used
  199. */
  200. clock24Hours?: boolean;
  201. /**
  202. * If true, will only return the date part, e.g. "Jan 1".
  203. */
  204. dateOnly?: boolean;
  205. /**
  206. * Whether to show the seconds.
  207. */
  208. seconds?: boolean;
  209. /**
  210. * If true, will only return the time part, e.g. "2:50 PM"
  211. */
  212. timeOnly?: boolean;
  213. /**
  214. * Whether to show the time zone.
  215. */
  216. timeZone?: boolean;
  217. /**
  218. * Whether to show the year. If not specified, the returned date string will
  219. * not contain the year _if_ the date is not in the current calendar year.
  220. * For example: "Feb 1" (2022), "Jan 1" (2022), "Dec 31, 2021".
  221. */
  222. year?: boolean;
  223. }
  224. export function getFormat({
  225. dateOnly,
  226. timeOnly,
  227. year,
  228. seconds,
  229. timeZone,
  230. clock24Hours,
  231. }: FormatProps = {}) {
  232. if (dateOnly) {
  233. return getDateFormat({year});
  234. }
  235. if (timeOnly) {
  236. return getTimeFormat({clock24Hours, seconds, timeZone});
  237. }
  238. const dateFormat = getDateFormat({year});
  239. const timeFormat = getTimeFormat({
  240. clock24Hours,
  241. seconds,
  242. timeZone,
  243. });
  244. // If the year is shown, then there's already a comma in dateFormat ("Jan 1, 2020"),
  245. // so we don't need to add another comma between the date and time
  246. return year ? `${dateFormat} ${timeFormat}` : `${dateFormat}, ${timeFormat}`;
  247. }
  248. export function getInternalDate(date: string | Date, utc?: boolean | null) {
  249. if (utc) {
  250. return getUtcToSystem(date);
  251. }
  252. return new Date(
  253. moment.tz(moment.utc(date), getUserTimezone()).format('YYYY/MM/DD HH:mm:ss')
  254. );
  255. }
  256. /**
  257. * Strips timezone from local date, creates a new moment date object with timezone
  258. * returns the moment as a Date object
  259. */
  260. export function getDateWithTimezoneInUtc(date?: Date, utc?: boolean | null) {
  261. return moment
  262. .tz(
  263. moment(date).local().format('YYYY-MM-DD HH:mm:ss'),
  264. utc ? 'UTC' : getUserTimezone()
  265. )
  266. .utc()
  267. .toDate();
  268. }