core/utils/timestamp.js
/** 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()
}
}