UNPKG

@markmyoung/timeduration

Version:

A 'TimeDuration' is essentially a 'Date' that understands it is a relative time, not an absolute time.

433 lines (417 loc) 15.2 kB
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=// // @version v0.0.1 (2016-09-02) // @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals // Examples: // // Repeating 2 (times) // // starts 2007-03-01 at 1 p.m. GMT // // ends 2008-05-11 at 3:30 p.m. GMT // let timeInterval = new TimeInterval( 'R2/2007-03-01T13:00:00Z/2008-05-11T15:30:00Z' ); //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=// let TimeInterval = (function( Date, TimeDuration, TimeIntervalIterator, undefined ) { let EPOCH_YEAR = (new Date( 0 )).getUTCFullYear(); function TimeInterval( arg0, arg1 ) { //console.log( "point break" ); let hidden = { 'encapsulatedEnd':null, 'encapsulatedStart':null, 'intervalDesignator':'/', 'repetitionCount':0, }; // Doing something like `Date.constructor.prototype.apply( this, arguments );` does not work. if( arg0 instanceof Date ) {hidden.encapsulatedStart = new Date( arg0 );} else if( arg0 instanceof TimeDuration ) {hidden.encapsulatedStart = new TimeDuration( arg0 );} else if( typeof( arg0 ) === 'string' ) { // Allows for delimiter of either '/' (solidus or slash) or '--' (double hyphen). let intervalRegExp = /^(?:(R)(\d*)(\/|\-\-))?(.*?)(?:(\/|\-\-)(.*?))?$/; let intervalMatches = (arg0.match( intervalRegExp ) || []).slice( 1 ); if( intervalMatches[ 0 ] === 'R' ) { // If the repitition indicator 'R' is specified without a repitition count, it is unbounded. hidden.repetitionCount = -1; let repetition_parsed = parseInt( intervalMatches[ 1 ], 10 ); if( !isNaN( repetition_parsed )) {hidden.repetitionCount = repetition_parsed;} if( !!intervalMatches[ 2 ]) {hidden.intervalDesignator = intervalMatches[ 2 ];} } if( TimeDuration.dateRegExp.test( intervalMatches[ 3 ])) {hidden.encapsulatedStart = new Date( intervalMatches[ 3 ]);} else if( TimeDuration.durationBasicRegExp.test( intervalMatches[ 3 ])) {hidden.encapsulatedStart = new TimeDuration( intervalMatches[ 3 ]);} else if( TimeDuration.durationExtendedRegExp.test( intervalMatches[ 3 ])) {hidden.encapsulatedStart = new TimeDuration( intervalMatches[ 3 ]);} else if( TimeDuration.durationNormalRegExp.test( intervalMatches[ 3 ])) {hidden.encapsulatedStart = new TimeDuration( intervalMatches[ 3 ]);} if( !(hidden.encapsulatedStart instanceof Date || hidden.encapsulatedStart instanceof TimeDuration)) {throw( new TypeError( "TimeInterval start must parse to a 'Date' or 'TimeDuration'." ));} if( !!intervalMatches[ 4 ]) {hidden.intervalDesignator = intervalMatches[ 4 ];} if( !!intervalMatches[ 2 ] && !!intervalMatches[ 4 ] && intervalMatches[ 2 ] !== intervalMatches[ 4 ]) {throw( Error( ''.concat( "Mixed interval designators used: '", intervalMatches[ 2 ], "' and '", intervalMatches[ 4 ], "'." )));} if( intervalMatches[ 5 ] !== undefined ) { if( TimeDuration.dateRegExp.test( intervalMatches[ 5 ])) {hidden.encapsulatedEnd = new Date( intervalMatches[ 5 ]);} else if( TimeDuration.durationBasicRegExp.test( intervalMatches[ 5 ])) {hidden.encapsulatedEnd = new TimeDuration( intervalMatches[ 5 ]);} else if( TimeDuration.durationExtendedRegExp.test( intervalMatches[ 5 ])) {hidden.encapsulatedEnd = new TimeDuration( intervalMatches[ 5 ]);} else if( TimeDuration.durationNormalRegExp.test( intervalMatches[ 5 ])) {hidden.encapsulatedEnd = new TimeDuration( intervalMatches[ 5 ]);} if( !(hidden.encapsulatedEnd instanceof Date || hidden.encapsulatedEnd instanceof TimeDuration)) {throw( new TypeError( "TimeInterval end must parse to a 'Date' or 'TimeDuration', or not specified at all." ));} } else {hidden.encapsulatedEnd = null;} } if( arg1 instanceof Date ) {hidden.encapsulatedEnd = new Date( arg1 );} else if( arg1 instanceof TimeDuration ) {hidden.encapsulatedEnd = new TimeDuration( arg1 );} if( hidden.encapsulatedStart instanceof TimeDuration && hidden.encapsulatedEnd instanceof TimeDuration ) {throw( new TypeError( "TimeInterval start and end cannot both parse to a 'TimeDuration'." ));} if( hidden.encapsulatedStart instanceof Date && hidden.encapsulatedEnd === null ) {throw( new TypeError( "TimeInterval with only one parameter must parse to a 'TimeDuration'." ));} this.getIntervalDesignator = function() {return( hidden.intervalDesignator );}; this.getRepetitionCount = function() {return( hidden.repetitionCount );}; this.setIntervalDesignator = function( delimiter ) {hidden.intervalDesignator = delimiter;}; this.setRepetitionCount = function( count ) { let parsed_count = parseInt( count, 10 ); if( parsed_count === Number( count )) {hidden.repetitionCount = parsed_count;} else {throw( new TypeError( "'count' must be an integer, -1 indicates unbounded." ));} }; /*this.toRepetition = function( requested_index ) { let repetitionDate = null; let repetition_count = this.getRepetitionCount(); if( repetition_count == 0 ) {throw( new RangeError( "TimeInterval does not repeat." ));} else if( repetition_count == -1 ) { } else if( repetition_count > 0 ) { if( requested_index >= repetition_count ) {throw( new RangeError( "Requested repetition index exceeds TimeInterval repetitions." ));} else { } } return( repetitionDate ); };*/ Object.defineProperties( this, { // Expose ecapsulated interval as a 'Date' object. 'encapsulated': { 'get':function() { let encapsulated = ((hidden.encapsulatedEnd !== null) ?(new Date( hidden.encapsulatedEnd.getTime() - hidden.encapsulatedStart.getTime())) :(new Date( hidden.encapsulatedStart.getTime())) ); return( encapsulated ); }, 'set':function( value ) {throw( new TypeError( "'encapsulated' is read-only." ));}, }, }); Object.defineProperties( this, { // Expose the ecapsulated end 'Date' or 'TimeDuration' object as read-only. 'intervalEnd': { 'get':function() {return( hidden.encapsulatedEnd );}, 'set':function( value ) //{throw( new TypeError( "'intervalEnd' is read-only." ));}, { if( !(value instanceof Date || value instanceof TimeDuration || value === null)) {throw( new TypeError( "'intervalEnd' must be a 'Date', 'TimeDuration', or null." ));} hidden.encapsulatedEnd = value; } }, // Expose the ecapsulated start 'Date' or 'TimeDuration' object as read-only. 'intervalStart': { 'get':function() {return( hidden.encapsulatedStart );}, 'set':function( value ) //{throw( new TypeError( "'intervalStart' is read-only." ));}, { if( !(value instanceof Date || value instanceof TimeDuration)) {throw( new TypeError( "'intervalStart' must be a 'Date' or 'TimeDuration'." ));} hidden.encapsulatedStart = value; } }, }); } TimeInterval.prototype = Object.create( Object.prototype ); TimeInterval.prototype.constructor = TimeInterval; // Iterators. TimeInterval.prototype[ Symbol.iterator ] = function( value_type ) {return( new TimeIntervalIterator( this, value_type ));}; TimeInterval.prototype.entries = function() {return( this[ Symbol.iterator ]( TimeIntervalIterator.ValueType.Entries ));}; TimeInterval.prototype.keys = function() {return( this[ Symbol.iterator ]( TimeIntervalIterator.ValueType.Keys ));}; TimeInterval.prototype.values = function() {return( this[ Symbol.iterator ]( TimeIntervalIterator.ValueType.Values ));}; TimeInterval.formatPrefix = function( timeInterval ) { let repetition_count = timeInterval.getRepetitionCount(); let repetition_prefix = ''; if( repetition_count != 0 ) { repetition_prefix = 'R'; if( repetition_count > 0 ) {repetition_prefix = repetition_prefix.concat( repetition_count.toString());} repetition_prefix = repetition_prefix.concat( timeInterval.getIntervalDesignator()); } return( repetition_prefix ); }; /** @override */ TimeInterval.prototype.getDate = function() { let value = this.encapsulated.getDate() - 1; return( value ); }; /** @override */ TimeInterval.prototype.getFullYear = function() { let value = this.encapsulated.getFullYear() - EPOCH_YEAR; return( value ); }; /** @override */ TimeInterval.prototype.getHours = function() { let value = this.getUTCHours(); return( value ); }; /** @override */ TimeInterval.prototype.getMilliseconds = function() { let value = this.getUTCMilliseconds(); return( value ); }; /** @override */ TimeInterval.prototype.getMinutes = function() { let value = this.getUTCMinutes(); return( value ); }; /** @override */ TimeInterval.prototype.getMonth = function() { let value = this.getUTCMonth(); return( value ); }; /** @override */ TimeInterval.prototype.getSeconds = function() { let value = this.getUTCSeconds(); return( value ); }; /** @override */ TimeInterval.prototype.getTime = function() { const milliseconds_in_a_minute = 60 * 1000; let value = this.encapsulated.getTime() - (this.encapsulated.getTimezoneOffset() * milliseconds_in_a_minute); return( value ); }; /** @override */ TimeInterval.prototype.getTimezoneOffset = function() {return( 0 );}; TimeInterval.prototype.includes = function( dateOrInterval, completely ) { //TODO handle repeating intervals completely = completely || false; if( !(dateOrInterval instanceof Date || dateOrInterval instanceof TimeInterval)) {throw( new TypeError( "'dateOrInterval' must be an instance of 'Date'." ));} let is_included = false; let dateOrIntervalTime = dateOrInterval.getTime(); let encapsulatedStartTime = this.encapsulatedStart.getTime(); let encapsulatedEndTime = null; if( this.encapsulatedEnd instanceof Date || this.encapsulatedEnd instanceof TimeInterval ) {encapsulatedEndTime = this.encapsulatedEnd.getTime();} if( dateOrInterval instanceof Date ) { if( this.encapsulatedStart instanceof Date ) { if( this.encapsulatedEnd instanceof Date ) { is_included = encapsulatedStartTime <= dateOrIntervalTime && encapsulatedEndTime >= dateOrIntervalTime; } else// if( this.encapsulatedEnd instanceOf TimeDuration ) { is_included = encapsulatedStartTime <= dateOrIntervalTime && (encapsulatedEndTime + encapsulatedStartTime) >= dateOrIntervalTime; } } else// if( this.encapsulatedStart instanceOf TimeDuration ) { if( this.encapsulatedEnd instanceof Date ) { is_included = (encapsulatedEndTime - encapsulatedStartTime) <= dateOrIntervalTime && encapsulatedEndTime >= dateOrIntervalTime; } else// if( this.encapsulatedEnd === null ) { is_included = encapsulatedStartTime <= dateOrIntervalTime; } } } else// if( dateOrInterval instanceof TimeInterval ) { //TODO } return( is_included ); }; /** @override */ TimeInterval.prototype.toDateString = function() { let date_string_end = ((this.intervalEnd !== null) ?(''.concat( this.intervalDesignator, TimeDuration.formatDate( this.intervalEnd ))) :('')); let date_string_start = ((this.intervalStart instanceof Date) ?(TimeDuration.formatDate( this.intervalStart )) :(''.concat( 'P', TimeDuration.formatDate( this.intervalStart )))); let date_string = ''.concat( date_string_start, datetime_string_end ); let repetition_prefix = TimeInterval.formatPrefix( this ); return( ''.concat( repetition_prefix, date_string )); }; /** @override */ TimeInterval.prototype.toISOString = function() { let datetime_string_end = ((this.intervalEnd !== null) ?(''.concat( this.getIntervalDesignator(), this.intervalEnd.toISOString())) :('')); let datetime_string_start = this.intervalStart.toISOString(); let datetime_string = ''.concat( datetime_string_start, datetime_string_end ); let repetition_prefix = TimeInterval.formatPrefix( this ); return( ''.concat( repetition_prefix, datetime_string )); }; /** @override */ TimeInterval.prototype.toString = function() { let as_string = this.toISOString(); return( as_string ); }; /** @override */ TimeInterval.prototype.toTimeString = function() { let time_string_end = ((this.intervalEnd !== null) ?(''.concat( this.intervalDesignator, TimeDuration.formatTime( this.intervalEnd ))) :('')); let time_string_start = ((this.intervalStart instanceof Date) ?(TimeDuration.formatTime( this.intervalStart )) :(''.concat( 'P', TimeDuration.formatTime( this.intervalStart )))); let time_string = ''.concat( time_string_start, time_string_end ); let repetition_prefix = TimeInterval.formatPrefix( this ); return( ''.concat( repetition_prefix, time_string )); }; // Getter functions. [ //'getDate', // Overridden elsewhere. //X 'getDay', // Not implemented. //'getFullYear', // Overridden elsewhere. //'getHours', // Overridden elsewhere. //'getMilliseconds', // Overridden elsewhere. //'getMinutes', // Overridden elsewhere. //'getMonth', // Overridden elsewhere. //'getSeconds', // Overridden elsewhere. //'getTime', // Overridden elsewhere. //'getTimezoneOffset', // Overridden elsewhere. 'getUTCDate', 'getUTCDay', 'getUTCFullYear', 'getUTCHours', 'getUTCMilliseconds', 'getUTCMinutes', 'getUTCMonth', 'getUTCSeconds', //X 'getYear', // Deprecated. ] .forEach( function proxy_getter( getter, g ) { TimeInterval.prototype[ getter ] = function() { let value = this.encapsulated[ getter ](); return( value ); }; }, this ); // Setter functions. [ 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', //X 'setYear', // Deprecated. ] .forEach( function proxy_not_implemented( not_implemented, n ) { TimeInterval.prototype[ not_implemented ] = function() {throw( new ReferenceError( ''.concat( "Function not implemented: '", not_implemented, "'." )));}; }, this ); // Converter functions. [ //'toDateString', // Overridden elsewhere. //'toISOString', // Overridden elsewhere. 'toJSON', 'toGMTString', 'toLocaleDateString', 'toLocaleFormat', 'toLocaleString', 'toLocaleTimeString', //'toString', // Overridden elsewhere. //'toTimeString', // Overridden elsewhere. 'toUTCString', 'valueOf', ] .forEach( function proxy_converter( converter, c ) { TimeInterval.prototype[ converter ] = function() { let value = this.encapsulated[ converter ](); return( value ); }; }, this ); // Not implemented functions. [ 'getDay', // What would this even mean? 'getYear', // Deprecated. 'setYear', // Deprecated. ] .forEach( function proxy_not_implemented( not_implemented, n ) { TimeInterval.prototype[ not_implemented ] = function() {throw( new ReferenceError( ''.concat( "Function not implemented: '", not_implemented, "'." )));}; }, this ); return( TimeInterval ); })( Date, TimeDuration, TimeIntervalIterator ); (module || {'exports':{}}).exports = TimeInterval; //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=//