193 lines
5.1 KiB
JavaScript
193 lines
5.1 KiB
JavaScript
/**
|
|
* @license Apache-2.0
|
|
*
|
|
* Copyright (c) 2021 The Stdlib Authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
// MODULES //
|
|
|
|
var hasOwnProp = require( '@stdlib/assert/has-own-property' );
|
|
var isInteger = require( '@stdlib/assert/is-integer' );
|
|
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
|
|
var isObject = require( '@stdlib/assert/is-object' );
|
|
var floor = require( '@stdlib/math/base/special/floor' );
|
|
var round = require( '@stdlib/math/base/special/round' );
|
|
var ceil = require( '@stdlib/math/base/special/ceil' );
|
|
|
|
|
|
// VARIABLES //
|
|
|
|
var timestamp = /^\d{10}$|^\d{13}$/;
|
|
var rounders = [ 'floor', 'ceil', 'round' ];
|
|
|
|
|
|
// FUNCTIONS //
|
|
|
|
/**
|
|
* Validates a date parameter.
|
|
*
|
|
* @private
|
|
* @param {*} value - value to be validated
|
|
* @param {string} name - name to be used in error messages
|
|
* @throws {TypeError} value must either be a date string, Date object, Unix timestamp, or JavaScript timestamp
|
|
* @throws {Error} numeric date must be either a Unix or Javascript timestamp
|
|
* @returns {Date} validated date
|
|
*/
|
|
function validDate( value, name ) {
|
|
var type;
|
|
|
|
type = typeof value;
|
|
if ( type === 'string' ) {
|
|
value = Date.parse( value );
|
|
if ( value !== value ) {
|
|
throw new Error( 'invalid argument. Unable to parse ' + name.toLowerCase() + ' date.' );
|
|
}
|
|
value = new Date( value );
|
|
}
|
|
if ( type === 'number' ) {
|
|
if ( !timestamp.test( value ) ) {
|
|
throw new Error( 'invalid argument. Numeric ' + name.toLowerCase() + ' date must be either a Unix or Javascript timestamp.' );
|
|
}
|
|
if ( value.toString().length === 10 ) {
|
|
value *= 1000; // sec to ms
|
|
}
|
|
value = new Date( value );
|
|
}
|
|
if ( !(value instanceof Date) ) {
|
|
throw new TypeError( 'invalid argument. ' + name + ' date must either be a date string, Date object, Unix timestamp, or JavaScript timestamp.' );
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
// MAIN //
|
|
|
|
/**
|
|
* Generates an array of linearly spaced dates.
|
|
*
|
|
* @param {(Date|number|string)} start - start time as either a `Date` object, Unix timestamp, JavaScript timestamp, or date string
|
|
* @param {(Date|number|string)} stop - stop time as either a `Date` object, Unix timestamp, JavaScript timestamp, or date string
|
|
* @param {number} [length] - output array length (default: 100)
|
|
* @param {Object} [options] - function options
|
|
* @param {string} [options.round] - specifies how sub-millisecond times should be rounded: [ 'floor', 'ceil', 'round' ] (default: 'floor' )
|
|
* @throws {TypeError} length argument must a positive integer
|
|
* @throws {Error} must provide valid options
|
|
* @returns {Array} array of dates
|
|
*
|
|
* @example
|
|
* var stop = '2014-12-02T07:00:54.973Z';
|
|
* var start = new Date( stop ) - 60000;
|
|
*
|
|
* var arr = datespace( start, stop, 6 );
|
|
* // returns [...]
|
|
*
|
|
* @example
|
|
* // Equivalent of Math.ceil():
|
|
* var arr = datespace( 1417503655000, 1417503655001, 3, { 'round': 'ceil' } );
|
|
* // returns [...]
|
|
*
|
|
* // Equivalent of Math.round():
|
|
* arr = datespace( 1417503655000, 1417503655001, 3, { 'round': 'round' } );
|
|
* // returns [...]
|
|
*/
|
|
function datespace( start, stop, length, options ) {
|
|
var opts;
|
|
var len;
|
|
var flg;
|
|
var arr;
|
|
var end;
|
|
var fcn;
|
|
var tmp;
|
|
var d;
|
|
var i;
|
|
|
|
len = 100;
|
|
flg = true;
|
|
opts = {
|
|
'round': 'floor'
|
|
};
|
|
start = validDate( start, 'Start' );
|
|
stop = validDate( stop, 'Stop' );
|
|
if ( arguments.length > 2 ) {
|
|
if ( arguments.length === 3 ) {
|
|
if ( isObject( length ) ) {
|
|
opts = length;
|
|
} else {
|
|
len = length;
|
|
|
|
// Turn off checking the options object...
|
|
flg = false;
|
|
}
|
|
} else {
|
|
opts = options;
|
|
len = length;
|
|
}
|
|
if ( len === 0 ) {
|
|
return [];
|
|
}
|
|
if ( !isInteger( len ) || len < 0 ) {
|
|
throw new TypeError( 'invalid argument. Length must a positive integer.' );
|
|
}
|
|
if ( flg ) {
|
|
if ( !isObject( opts ) ) {
|
|
throw new TypeError( 'invalid argument. Options argument must be an object. Value: `' + opts + '`.' );
|
|
}
|
|
if ( hasOwnProp( opts, 'round' ) ) {
|
|
if ( !isString( opts.round ) ) {
|
|
throw new TypeError( 'invalid option. `round` option must be a string.' );
|
|
}
|
|
if ( rounders.indexOf( opts.round ) === -1 ) {
|
|
throw new Error( 'invalid input option. `round` option must be one of [' + rounders.join( ',' ) + '].' );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
switch ( opts.round ) {
|
|
case 'round':
|
|
fcn = round;
|
|
break;
|
|
case 'ceil':
|
|
fcn = ceil;
|
|
break;
|
|
default:
|
|
case 'floor':
|
|
fcn = floor;
|
|
break;
|
|
}
|
|
|
|
// Calculate the increment...
|
|
end = len - 1;
|
|
d = ( stop.getTime() - start.getTime() ) / end;
|
|
|
|
// Build the output array...
|
|
arr = new Array( len );
|
|
tmp = start;
|
|
arr[ 0 ] = tmp;
|
|
tmp = tmp.getTime();
|
|
for ( i = 1; i < end; i++ ) {
|
|
tmp += d;
|
|
arr[ i ] = new Date( fcn( tmp ) );
|
|
}
|
|
arr[ end ] = stop;
|
|
return arr;
|
|
}
|
|
|
|
|
|
// EXPORTS //
|
|
|
|
module.exports = datespace;
|