4a3d75d2cc
Fixed: Issue with end date earlier than start date being allowed Added: Jest tests Updated: tests
173 lines
4.5 KiB
JavaScript
173 lines
4.5 KiB
JavaScript
/*
|
|
|
|
The following charges are used:
|
|
|
|
Short Stay
|
|
£1.10 per hour between 8am and 6pm on weekdays, free outside of these times.
|
|
Visits need not be whole hours and can last more than one day.
|
|
|
|
Long Stay
|
|
£7.50 per day or part day including weekends, so the minimum charge will be for one day.
|
|
|
|
|
|
A stay entirely outside of a chargeable period will return £0.00
|
|
A short stay from 07/09/2017 16:50:00 to 09/09/2017 19:15:00 would cost £12.28
|
|
Thursday => Saturday ( 1504803000 => 1504984500)
|
|
|
|
Thursday: 1h 10m = 1.28
|
|
Friday: 10h = 11.00
|
|
Saturday: free
|
|
|
|
A long stay from 07/09/2017 07:50:00 to 09/09/2017 05:20:00 would cost £22.50
|
|
Thursday => Saturday ( 1504770600 => 1504934400)
|
|
|
|
Thursday: 1 = 7.5
|
|
Friday: 1
|
|
Saturday: 1
|
|
Total: 22.50
|
|
|
|
---
|
|
|
|
hour = 3600 seconds
|
|
|
|
|
|
|
|
1604414190894
|
|
|
|
*/
|
|
|
|
const fecha = require('fecha');
|
|
|
|
class Carpark {
|
|
#_startDT;
|
|
#_endDT;
|
|
#_dayLength = 60 * 60 * 24;
|
|
#_hourLength = 60 * 60;
|
|
#_validDays = [1, 2, 3, 4, 5]; // Monday, Tuesday, Wednesday, Thursday, Friday
|
|
#_validStart = this.#_hourLength * 8; // 8AM // 08:00
|
|
#_validEnd = this.#_hourLength * 18; // 6PM // 18:00
|
|
#_longPrice = 7.5;
|
|
#_shortPrice = 1.1;
|
|
#_secondPrice = this.#_shortPrice / this.#_hourLength;
|
|
|
|
/**
|
|
* Check if a specific timestamp is a valid date
|
|
* @param workTime
|
|
* @returns {boolean}
|
|
* @private
|
|
*/
|
|
_isValidTime(workTime) {
|
|
const day = new Date(workTime * 1000).getDay();
|
|
|
|
if (this.#_validDays.indexOf(day) > -1) {
|
|
const dayBase = ~~(workTime / this.#_dayLength) * this.#_dayLength;
|
|
const dayPosition = workTime - dayBase;
|
|
|
|
return dayPosition >= this.#_validStart && dayPosition < this.#_validEnd - 1;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate short term parking costs
|
|
* @returns {number}
|
|
* @private
|
|
*/
|
|
_calcShortTerm() {
|
|
let startSeconds = this.#_startDT.getTime() / 1000;
|
|
let endSeconds = this.#_endDT.getTime() / 1000;
|
|
let workTime, total;
|
|
let validHours = 0;
|
|
let startHourPart = 0;
|
|
|
|
// Calculate Start Hour
|
|
let startBaseHour = ~~(startSeconds / this.#_hourLength) * this.#_hourLength;
|
|
|
|
// Calculate End Hour
|
|
let endBaseHour = ~~(endSeconds / this.#_hourLength) * this.#_hourLength;
|
|
|
|
// Check if the very first portion of the hour is valid
|
|
if (this._isValidTime(startSeconds)) {
|
|
startHourPart = this.#_hourLength - (startSeconds - startBaseHour);
|
|
}
|
|
|
|
// Go to the next hour after the initial start
|
|
workTime = startBaseHour + this.#_hourLength;
|
|
|
|
// If there's more of an hours worth of time then loop through the next hours
|
|
if (endBaseHour - workTime > this.#_hourLength) {
|
|
do {
|
|
validHours = validHours + (this._isValidTime(workTime) ? 1 : 0);
|
|
workTime = workTime + this.#_hourLength;
|
|
} while (workTime <= endBaseHour);
|
|
}
|
|
|
|
// Sum it all
|
|
total = startHourPart * this.#_secondPrice + validHours * this.#_shortPrice;
|
|
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* Calculate long term parking costs
|
|
* @returns {number}
|
|
* @private
|
|
*/
|
|
_calcLongTerm() {
|
|
// convert to time stamps and make them second based
|
|
const startSeconds = this.#_startDT.getTime() / 1000;
|
|
const endSeconds = this.#_endDT.getTime() / 1000;
|
|
|
|
// Very start of the first day
|
|
const startBaseSeconds = ~~(startSeconds / this.#_dayLength) * this.#_dayLength;
|
|
|
|
// Very end of the second day
|
|
const endBaseSeconds = ~~(endSeconds / this.#_dayLength) * this.#_dayLength + this.#_dayLength;
|
|
|
|
// Subtract the start seconds from the end seconds
|
|
const distance = endBaseSeconds - startBaseSeconds;
|
|
|
|
// divide them by number of seconds in a day and multiply by cost
|
|
|
|
return (distance / this.#_dayLength) * this.#_longPrice;
|
|
}
|
|
|
|
/**
|
|
* The public calculate method
|
|
* @param start
|
|
* @param end
|
|
* @param mode
|
|
* @returns {number}
|
|
*/
|
|
calculate(start, end, mode = 'longterm') {
|
|
let workVal = 0.0;
|
|
// Ensure there are 3 arguments
|
|
if (arguments.length < 3) return workVal;
|
|
|
|
this.#_startDT = fecha.parse(start, 'DD/MM/YYYY HH:mm:ssZ');
|
|
this.#_endDT = fecha.parse(end, 'DD/MM/YYYY HH:mm:ssZ');
|
|
|
|
// Ensure that endDT is after startDT;
|
|
if (this.#_endDT.getTime() <= this.#_startDT.getTime()) return workVal;
|
|
|
|
switch (mode.toLowerCase()) {
|
|
case 'short':
|
|
case 'shortterm':
|
|
workVal = this._calcShortTerm();
|
|
break;
|
|
|
|
case 'long':
|
|
case 'longterm':
|
|
workVal = this._calcLongTerm();
|
|
break;
|
|
|
|
default:
|
|
}
|
|
|
|
return parseFloat(workVal.toFixed(2));
|
|
}
|
|
}
|
|
|
|
module.exports = Carpark;
|