department-of-veterans-affairs/vets-website

View on GitHub
src/platform/mhv/downtime/utils/date.js

Summary

Maintainability
A
0 mins
Test Coverage
// Date parsing
// DowntimeNotification uses momentjs, which is deprecated.
import { formatDuration, differenceInHours } from 'date-fns';
import { format } from 'date-fns-tz';

/*
 * Attempts to coerce a momentjs object to a plain date. Returns null if it cannot return a date
 * @param {Object} d
 * @returns {(Date|null)}
 */
function coerceToDate(d) {
  if (d instanceof Object && 'toDate' in d) {
    const d1 = d.toDate();
    if (d1 instanceof Date) {
      return d1;
    }
  }
  if (d instanceof Date) {
    return d;
  }
  return null;
}

/**
 * Turns an ISO 8601 datetime string into a Date object
 * @param {string} input - ISO 8601 datetime string
 * @returns {(Date|null)}
 */
function parseDate(input) {
  const dt = Date.parse(input);
  let result = null;

  if (!Number.isNaN(dt)) {
    result = new Date(dt);
  }
  return result;
}

/**
 * Format a Date into a VA-preferred datetime string
 * <https://design.va.gov/content-style-guide/dates-and-numbers>
 * - Shortens timezone name, removing 'S'tandard or 'D'aylight
 * - Formats dayPeriod as 'a.m.' or 'p.m.'
 * - Handles 12:00 as 'noon' or 'midnight'
 * @param {Date} input - Date object instance
 * @returns {string}
 */
function formatDatetime(input) {
  const d = coerceToDate(input);
  if (d === null) return '';

  let timeString = format(d, "MMMM d, yyyy 'at' h:mm bbbb z");

  // remove S or D in timezone abbreviation
  timeString = timeString.replace(/([A-Z])(S|D)T$/, (_, p1) => `${p1}T`);

  // Handle 12:00 as noon or midnight
  timeString = timeString.replace(
    /\d{1,2}:\d{2} (noon|midnight)/,
    (_, p1) => `${p1}`,
  );

  return timeString;
}

/*
 * Create a string representing estimated elapsed hours. Rounds to nearest hour
 * @param {Date|Moment} start
 * @param {Date|Moment} end
 * @returns {?string} - Elapsed hours as a human-readable string
 */
function formatElapsedHours(start, end) {
  const startDate = coerceToDate(start);
  const endDate = coerceToDate(end);
  if (!(startDate && endDate)) return null;
  const hours = differenceInHours(endDate, startDate, {
    roundingMethod: 'round',
  });
  return formatDuration({ hours });
}

export { coerceToDate, formatDatetime, formatElapsedHours, parseDate };