{"version":3,"file":"formatter.mjs","sources":["../../../src/datetime/formatter.ts"],"sourcesContent":["/* eslint-disable id-blacklist, no-restricted-imports */\nimport moment, { Moment } from 'moment-timezone';\n\nimport { formatDate } from '@grafana/i18n';\n\nimport { TimeZone } from '../types/time';\nimport { getFeatureToggle } from '../utils/featureToggles';\n\nimport { DateTimeOptions, getTimeZone } from './common';\nimport { systemDateFormats } from './formats';\nimport { DateTimeInput, toUtc, dateTimeAsMoment } from './moment_wrapper';\n\n/**\n * Converts a Grafana DateTimeInput to a plain Javascript Date object.\n */\nfunction toDate(dateInUtc: DateTimeInput): Date {\n  if (dateInUtc instanceof Date) {\n    return dateInUtc;\n  }\n\n  if (typeof dateInUtc === 'string' || typeof dateInUtc === 'number') {\n    return new Date(dateInUtc);\n  }\n\n  return dateTimeAsMoment(dateInUtc).toDate();\n}\n\n/**\n * Converts a Grafana timezone string to an IANA timezone string.\n */\nexport function toIANATimezone(grafanaTimezone: string) {\n  // Intl APIs will use the browser's timezone by default (if tz is undefined)\n  if (grafanaTimezone === 'browser') {\n    return undefined;\n  }\n\n  const zone = moment.tz.zone(grafanaTimezone);\n  if (!zone) {\n    // If the timezone is invalid, we default to the browser's timezone\n    return undefined;\n  }\n\n  return grafanaTimezone;\n}\n\nfunction getIntlOptions(\n  date: Date,\n  options?: DateTimeOptionsWithFormat\n): Intl.DateTimeFormatOptions & { timeZone?: string } {\n  const timeZone = getTimeZone(options);\n\n  const intlOptions: Intl.DateTimeFormatOptions = {\n    year: 'numeric', // ↔ dateStyle: 'short'\n    month: 'numeric',\n    day: 'numeric',\n    hour: 'numeric', // ↔ timeStyle: 'short'\n    minute: 'numeric',\n    timeZone: toIANATimezone(timeZone),\n  };\n\n  // If the time has seconds, ensure they're included in the format\n  const hasSeconds = date.getSeconds() !== 0;\n  if (hasSeconds) {\n    intlOptions.second = 'numeric';\n  }\n\n  if (options?.defaultWithMS) {\n    intlOptions.second = 'numeric';\n    intlOptions.fractionalSecondDigits = 3; // Include milliseconds\n  }\n\n  return intlOptions;\n}\n\n/**\n * The type describing the options that can be passed to the {@link dateTimeFormat}\n * helper function to control how the date and time value passed to the function is\n * formatted.\n *\n * @public\n */\nexport interface DateTimeOptionsWithFormat extends DateTimeOptions {\n  /**\n   * Set this value to `true` if you want to include milliseconds when formatting date and time\n   */\n  defaultWithMS?: boolean;\n}\n\ntype DateTimeFormatter<T extends DateTimeOptions = DateTimeOptions> = (dateInUtc: DateTimeInput, options?: T) => string;\n\n// NOTE:\n// These date formatting functions now just wrap the @grafana/i18n formatting functions\n// (which themselves wrap the browserIntl APIs). In the future we may deprecate these\n// in favor of using @grafana/i18n directly.\n\n/**\n * Helper function to format date and time according to the specified options.\n * If no options are supplied, then the date is formatting according to the user's locale preference.\n *\n * @param dateInUtc - date in UTC format, e.g. string formatted with UTC offset, UNIX epoch in seconds etc.\n * @param options\n *\n * @public\n */\nexport const dateTimeFormat: DateTimeFormatter<DateTimeOptionsWithFormat> = (dateInUtc, options?) => {\n  // If a custom format is provided (or the toggle isn't enabled), use the previous implementation\n  if (!getFeatureToggle('localeFormatPreference') || options?.format) {\n    return toTz(dateInUtc, getTimeZone(options)).format(getFormat(options));\n  }\n\n  const dateAsDate = toDate(dateInUtc);\n  const intlOptions = getIntlOptions(dateAsDate, options); // TODO - if invalid timezone, use browser timezone\n  return formatDate(dateAsDate, intlOptions);\n};\n\n/**\n * Helper function to format date and time according to the standard ISO format e.g. 2013-02-04T22:44:30.652Z.\n * If no options are supplied, then default values are used. For more details, see {@link DateTimeOptionsWithFormat}.\n *\n * @param dateInUtc - date in UTC format, e.g. string formatted with UTC offset, UNIX epoch in seconds etc.\n * @param options\n *\n * @public\n */\nexport const dateTimeFormatISO: DateTimeFormatter = (dateInUtc, options?) =>\n  toTz(dateInUtc, getTimeZone(options)).format();\n\n/**\n * Helper function to return elapsed time since passed date. The returned value will be formatted\n * in a human readable format e.g. 4 years ago. If no options are supplied, then default values are used.\n * For more details, see {@link DateTimeOptions}.\n *\n * @param dateInUtc - date in UTC format, e.g. string formatted with UTC offset, UNIX epoch in seconds etc.\n * @param options\n *\n * @public\n */\nexport const dateTimeFormatTimeAgo: DateTimeFormatter = (dateInUtc, options?) =>\n  toTz(dateInUtc, getTimeZone(options)).fromNow();\n\n/**\n * Helper function to format date and time according to the Grafana default formatting, but it\n * also appends the time zone abbreviation at the end e.g. 2020-05-20 13:37:00 CET. If no options\n * are supplied, then default values are used. For more details please see {@link DateTimeOptions}.\n *\n * @param dateInUtc - date in UTC format, e.g. string formatted with UTC offset, UNIX epoch in seconds etc.\n * @param options\n *\n * @public\n */\nexport const dateTimeFormatWithAbbrevation: DateTimeFormatter = (dateInUtc, options?) => {\n  // If a custom format is provided (or the toggle isn't enabled), use the previous implementation\n  if (!getFeatureToggle('localeFormatPreference') || options?.format) {\n    return toTz(dateInUtc, getTimeZone(options)).format(`${systemDateFormats.fullDate} z`);\n  }\n\n  const dateAsDate = toDate(dateInUtc);\n  const intlOptions = getIntlOptions(dateAsDate, options);\n  intlOptions.timeZoneName = 'short';\n\n  return formatDate(dateAsDate, intlOptions);\n};\n\n/**\n * Helper function to return only the time zone abbreviation for a given date and time value. If no options\n * are supplied, then default values are used. For more details please see {@link DateTimeOptions}.\n *\n * @param dateInUtc - date in UTC format, e.g. string formatted with UTC offset, UNIX epoch in seconds etc.\n * @param options\n *\n * @public\n */\nexport const timeZoneAbbrevation: DateTimeFormatter = (dateInUtc, options?) =>\n  toTz(dateInUtc, getTimeZone(options)).format('z');\n\nconst getFormat = <T extends DateTimeOptionsWithFormat>(options?: T): string => {\n  if (options?.defaultWithMS) {\n    return options?.format ?? systemDateFormats.fullDateMS;\n  }\n  return options?.format ?? systemDateFormats.fullDate;\n};\n\nconst toTz = (dateInUtc: DateTimeInput, timeZone: TimeZone): Moment => {\n  const date = dateInUtc;\n  const zone = moment.tz.zone(timeZone);\n\n  if (zone && zone.name) {\n    return dateTimeAsMoment(toUtc(date)).tz(zone.name);\n  }\n\n  switch (timeZone) {\n    case 'utc':\n      return dateTimeAsMoment(toUtc(date));\n    default:\n      return dateTimeAsMoment(toUtc(date)).local();\n  }\n};\n"],"names":[],"mappings":"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"}