mikaelvesavuori/chrono-utils

View on GitHub
src/getTimestampForInputDate.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { InvalidIsoDateConversionError } from './errors';

/**
 * @description Gets a corresponding Unix timestamp for a `YYYYMMDD` date.
 *
 * @param date Date in YYYYMMDD format
 * @param offsetInHours Optional timezone offset in hours, using the format `0` (UTC; default), `-4` (behind UTC), or `7` (before UTC)
 *
 * @example getTimestampForInputDate('20230101');
 * @example getTimestampForInputDate('20230101', 4);
 * @example getTimestampForInputDate('20230101', -11);
 *
 * @returns `1672531200`
 */
export function getTimestampForInputDate(
  date: string,
  offsetInHours = 0,
  lastPossibleTime = false
) {
  const formatted = convertToIsoDate(date);
  const timestamp = getTimestampForISODate(formatted, offsetInHours, lastPossibleTime);

  return `${timestamp}`;
}

/**
 * @description Converts a `YYYYMMDD` date string to ISO format.
 *
 * @returns `20230101`
 */
function convertToIsoDate(input: string) {
  if (!input || input.length !== 8) throw new InvalidIsoDateConversionError();

  const year = input.substring(0, 4);
  const month = input.substring(4, 6);
  const day = input.substring(6, 8);

  return `${year}-${month}-${day}`;
}

/**
 * @description Retrieve timestamp for an ISO date such as `2023-01-01`.
 *
 * @param formattedDate Date in the ISO format of `2023-01-01`
 * @param offsetInHours Optional timezone offset in hours, using the format `0` (UTC; default), `-4` (behind UTC), or `7` (before UTC)
 *
 * @returns `1672531200`
 */
function getTimestampForISODate(
  formattedDate: string,
  offsetInHours = 0,
  lastPossibleTime = false
) {
  const date = new Date(
    createTimezoneConvertedDateString(formattedDate, offsetInHours, lastPossibleTime)
  );
  return Math.floor(date.getTime() / 1000);
}

/**
 * @description Returns a long timezone-converted date string from a formatted date such as `2023-01-01`.
 *
 * @returns `2023-01-01T00:00:00.000+05:00`
 */
function createTimezoneConvertedDateString(
  formattedDate: string,
  offsetInHours: number,
  lastPossibleTime = false
) {
  /**
   * Note that this is "flipped" and works contrary to how we normally
   * think about timezones. We typically think of e.g. New York as
   * several hours "behind" (minus, -) London/GMT/UTC/Zulu time.
   *
   * However, for some unknown reason when adding the offset we get
   * precisely the reverse results: That's why we do the flip of +/- here.
   */
  const offsetMarker = offsetInHours.toString().includes('-') ? '+' : '-';
  const numericOffset = offsetInHours.toString().replace('-', '');
  const leadingZero = numericOffset.length === 1 ? '0' : '';
  const time = lastPossibleTime ? 'T23:59:59.999' : 'T00:00:00.000';

  return `${formattedDate}${time}${offsetMarker}${leadingZero}${numericOffset}:00`;
}