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;
 |