cozy-labs/cozy-desktop

View on GitHub
core/utils/timestamp.js

Summary

Maintainability
A
2 hrs
Test Coverage
/** Date/time helpers
 *
 * @module core/utils/timestamp
 * @flow
 */

/*::
export type Timestamp = Date
*/

module.exports = {
  build,
  spread,
  current,
  after,
  fromDate,
  sameDate,
  almostSameDate,
  maxDate,
  stringify,
  roundedRemoteDate
}

function build(
  year /*: number */,
  month /*: number */,
  day /*: number */,
  hour /*: number */,
  minute /*: number */,
  second /*: number */
) /*: Timestamp */ {
  return new Date(Date.UTC(year, month - 1, day, hour, minute, second))
}

function spread(date /*: string|number|Date|Timestamp */) /*: number[] */ {
  const timestamp = new Date(date)
  const year = timestamp.getUTCFullYear()
  const month = timestamp.getUTCMonth() + 1 // Months start with 0 in javascript
  const day = timestamp.getUTCDate()
  const hours = timestamp.getUTCHours()
  const minutes = timestamp.getUTCMinutes()
  const seconds = timestamp.getUTCSeconds()
  const milliseconds = timestamp.getUTCMilliseconds()

  return [year, month, day, hours, minutes, seconds, milliseconds]
}

function current() /*: Timestamp */ {
  return fromDate(new Date())
}

function after(date /*: string|number|Date|Timestamp */) /*: Timestamp */ {
  return fromDate(new Date(date).getTime() + 1000)
}

function fromDate(date /*: string|number|Date|Timestamp */) /*: Timestamp */ {
  let timestamp /*: Date */ = new Date(date)
  timestamp.setMilliseconds(0)
  return timestamp
}

function sameDate(t1 /*: Timestamp */, t2 /*: Timestamp */) /*: boolean */ {
  return t1.getTime() === t2.getTime()
}

// Return true if the two dates are the same, +/- 3 seconds
function almostSameDate(
  one /*: string|Date|Timestamp */,
  two /*: string|Date|Timestamp */
) {
  const oneT = fromDate(one).getTime()
  const twoT = fromDate(two).getTime()
  return Math.abs(twoT - oneT) <= 3000
}

function maxDate(isoDate1 /*: string */, isoDate2 /*: string */) /*: string */ {
  const one = roundedRemoteDate(isoDate1)
  const two = roundedRemoteDate(isoDate2)
  return new Date(one).getTime() > new Date(two).getTime() ? one : two
}

function stringify(t /*: Timestamp */) {
  return t.toISOString().substring(0, 19) + 'Z'
}

/* Format a remote date as an ISO formatted date string
 *
 * The remote documents' dates have a format very close to the ISO string format
 * with 2 main differences:
 * - if generated by cozy-stack, the date will have a nanosecond precision,
 *   making its milliseconds part 8 digits long
 * - if the milliseconds are a round number (i.e. 0, x0 or xx0), the trailing
 *   zeros will be missing
 *
 * This function helps us make sure all dates saved in PouchDB have the expected
 * ISO format.
 */
function roundedRemoteDate(isoDate /*: string */) /*: string */ {
  const date = new Date(isoDate)

  const [hasMilliseconds, milliseconds] = /\.(\d+)/.exec(isoDate) || [false, '']
  if (hasMilliseconds && milliseconds.length > 3) {
    // In this situation (i.e. more than 3 milliseconds digits) we need to
    // "truncate" the date to fit our 3 milliseconds digits standard.
    //
    // However, simply truncating is not an option because cozy-stack does not
    // allow changing a Date attribute with an older value and truncating would
    // mean, in most cases, getting a lower value (with the very rare exception
    // of a 0 milliseconds date).
    //
    // So we make sure we have a greater value that's close enough by adding 1
    // millisecond to the rounded date
    return new Date(date.getTime() + 1).toISOString()
  } else {
    return date.toISOString()
  }
}