Source: timeunit.jsm

/**
 * bluebox.js - A webcomponent to display timelines
 *
 * Copyright (c) 2019, Luis Panadero Guardeño <luis.panadero(at)gmail.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/**
 * Pseudo enum that represent a Time Unit stored on a Number
 * @readonly
 */
class TimeUnit {


  #name;
  #intlDateTimeFormat;

  /**
   * Creates a new TimeUnit
   * @param {string} name - Name
   * @param {Object} intlDateTimeFormat - Intl.DateTimeFormat format options object
   */
  constructor(name, intlDateTimeFormat) {
    this.#name = name;
    this.#intlDateTimeFormat = intlDateTimeFormat;
    Object.freeze(this);
  }

  static Millisecond = new TimeUnit('millisecond', {timeStyle: "short"} );
  static Second = new TimeUnit('second', {timeStyle: "short"});
  static Minute = new TimeUnit('minute', {timeStyle: "short"});
  static Hour = new TimeUnit('hour', {timeStyle: "short"});
  static Day = new TimeUnit('day', {dateStyle: "short"});
  static Year = new TimeUnit('year', {year: "numeric", 'era': "short"});

  /**
   * Gets a TimeUnit from a string value
   * @param {string} value - String name of a TimeUnit
   * @return {TimeUnit} A TimeUnit instance or null
   */
  static fromName(value) {
    for (let timeUnit of [TimeUnit.Millisecond, TimeUnit.Second, TimeUnit.Minute, TimeUnit.Hour, TimeUnit.Day, TimeUnit.Year]) {
      if (timeUnit.#name === value) {
        return timeUnit;
      }
    }
    return null;
  }

  /**
   * Convert a date to a Number using the time unit
   * @param {Date} date - Input Date
   * @return {number} - A number, that using this TimeUnit, represents the same Date
   */
  convertFromDate(date) {
    if (this == TimeUnit.Millisecond) {
      return date.getTime();
    } else if (this == TimeUnit.Second) {
      return date.getTime() / 1000;
    } else if (this == TimeUnit.Minute) {
      return (date.getTime() / 1000) / 60;
    } else if (this == TimeUnit.Hour) {
      return (date.getTime() / 1000) / 3600;
    } else if (this == TimeUnit.Day) {
      return (date.getTime() / 1000) / 3600 / 24;
    } else if (this == TimeUnit.Year) {
      return date.getUTCFullYear();
    }

    return date.getTime();
  }

  /**
   * Convert a date to a Number using the time unit
   * @param {number} value - Input value in TimeUnits
   * @return {Date} - A equivalent Date to the value in TimeUnits.
   */
  convertToDate(value) {
    if (this == TimeUnit.Millisecond) {
      return new Date(value);
    } else if (this == TimeUnit.Second) {
      return new Date(value * 1000);
    } else if (this == TimeUnit.Minute) {
      return new Date(value * 1000 * 60);
    } else if (this == TimeUnit.Hour) {
      return new Date(value * 1000 * 3600);
    } else if (this == TimeUnit.Day) {
      return new Date(value * 1000 * 3600 * 24);
    } else if (this == TimeUnit.Year) {
      const date = new Date(Date.UTC(value, 0, 0));
      // Hack to handle correctly yers between 0 and 999
      date.setUTCFullYear(value);
      return date;
    }

    return new Date(value);
  }

  /**
   * Returns a functions that formats a value in TimeUnits to a String representation
   * @param {string} locale - String locale conden
   * @param {Object} [formatOptions] - Intl.DateTimeFormat format options object
   * @return {Function(zoomFactor, value)} A format function for the TimeUnit values
   */
  defaultFormatFunction(locale, formatOptions) {
    const timeUnit = this;
    const defaultIntlFormatFunction = new Intl.DateTimeFormat(locale, formatOptions || this.#intlDateTimeFormat).format;
    return function(zoomFactor, value) { return defaultIntlFormatFunction(timeUnit.convertToDate(value))};
  }

  /**
   * @return {string} String representation of a TimeUnit for debuing porpuses
   */
  toString() {
    return `TimeUnit.${this.#name}`;
  }

  /**
   * Name of a TimeUnit. Aka, his string value representation
   */
  get name () {
    return this.#name;
  }

}

export default TimeUnit;