calcost v1.0.6
CalCost
calcost is a Node.js module which allows you to define time-based rules for
calculating the cost of resource usage. The module provides CostRule objects
which are used to define the rules and their constraints. Several rules can be
defined for a resource and then used to calculate the cost of using the
resource.
Example Use Case
A compute resource has variable costs. The resource costs more to use during
peak times and costs significantly less during the off-peak times. Multiple
CostRule objects are defined to cover the peak periods: Monday-Friday, 8am-8pm.
Other CostRule objects are defined to cover all non-peak times.
Calculating the cost of a user's usage then requires creating DateSpan objects
for each interval of time during which the user has used the resource. An array containing
these DateSpan objects is then passed to each CostRule for the peak times.
Each CostRule object determines if any of the DateSpan objects time intervals
do overlap with any interval for which it is configured to allocate costs. The
cost is allocated to each overlapping DateSpan and then the method totalCost()
returns the total cost and arrays representing the overlapping (used) time intervals
and any remainder time intervals for which costs have not been allocated.
If there are any remaining DateSpan objects, which have not been used to
allocate costs, these are passed to the non-peak CostRule objects. This non-peak
cost is added to the peak cost to generate the total cost of using the compute
resource.
API Usage
The calcost module can be installed using NPM.
$ cd <myproject>
$ npm install --save calcostCurrently, the calcost module provides one functional constructor for the
CostRule object and it also provides the constants object.
var calcost = require('calcost');
// functional constructor to create CostRule objects
var costrule = calcost.costRule;
// Constants object provides all of the constants defined by the module
var calcostConstants = calcost.constants;CostRule
A CostRule is an immutable object which allows a time-based rule to be defined
for the calculation of the cost of resource usage. It provides a functional
constructor costRule and a single method totalCost which is used to calculate
the cost of an array of DateSpan objects.
costRule()
The costRule function is a functional constructor therefore it should not be
called with the new operator. The function accepts several arguments:
inTimeRuleATimeRuleobject which defines the interval of time during which the costs can be calculated using this rule.inRateRate per unit of time. The rate is currency-agnostic.inRateTypeArgument describes how the costs should be calculated.inWorkDayDurationDuration of a working day in minutes.
Valid values for inRateType are:
RATETYPE_PER_MILLISECONDRate is the cost per millisecond.RATETYPE_PER_SECOND_PRORATARate is the cost per second and/or fraction of a second.RATETYPE_PER_SECOND_ROUNDUPRate is the cost per second. Cost is rounded up to the nearest second.RATETYPE_PER_MINUTE_PRORATARate is the cost per minute and/or fraction of a minute.RATETYPE_PER_MINUTE_ROUNDUPRate is the cost per minute. Cost is rounded up to the nearest minute.RATETYPE_PER_MINUTE_NATURALRate is the cost per natural minute during which the resource is used for any duration.RATETYPE_PER_HOUR_PRORATARate is the cost per hour and/or fraction of an hour.RATETYPE_PER_HOUR_ROUNDUPRate is the cost per hour. Cost is rounded up to the nearest hour.RATETYPE_PER_HOUR_NATURALRate is the cost per natural hour during which the resource is used for any duration.RATETYPE_PER_DAY_PRORATARate is the cost per working day and/or fraction of a working day. The duration of a working day must also be passed to the function when using this type of rate.RATETYPE_PER_DAY_ROUNDUPRate is the cost per working day. Cost is rounded up to the nearest full working day. The duration of a working day must also be passed to the function when using this type of rate.RATETYPE_PER_DAY_NATURALRate is the cost per working day during which the resource is used for any duration.
Pro-Rata Rate Types
Pro-Rata rate types have a unit of cost defined per unit of time. If the resource usage includes a fraction of the rate's unit of time, then the calculated cost will include a proportional fraction of the cost of one time unit.
Round-up Rate Types
Round-up rate types calculate the cost similarly to the Pro-Rata rate, however number of units of time used is rounded-up to the next whole integer.
Natural Rate Types
Natural rate types do not calculate the total resource usage by summing all of the durations of intervals of time during which the resource was used. This rate type requires counting all of the natural time units which overlap with intervals of resource usage.
A natural time unit is one which has a begin and end time which coincide with those defined by a clock or calendar. The natural day is the interval of time between 00:00 midnight and the subsequent occurrence of midnight. A natural minute is the interval of time on a clock from when the seconds count is zero until the following instant when the seconds count is zero.
Working Day
RATETYPE_PER_DAY_PRORATA and RATETYPE_PER_DAY_ROUNDUP require that the duration of the working day interval is defined. An 8 hour work day is defined by default but this can be any value between 0 and 24 hours.
totalCost()
This method takes an array of DateSpan objects, an optional timezone
identifier, and then calculates the sum of the costs due to all of the
DateSpan objects for a specific CostRule. The method returns and object
with three data members:
cost. A number containing the total sum of all of the costs accumulated byDateSpanobjects which overlapped with the time-spans defined by theCostRule.usedSpans: An array ofDateSpanobjects, each of which indicates an interval of time for which there was an overlap and costs were accumulated by theCostRule.remainderSpans: An array of remainderDateSpanobjects, each of which represents an interval of time during which no costs were accrued because there was no overlap with the time interval defined by theCostRule.
The following example code is based on a test case which is available in the
file unit-costrule.js. Several cost rules are defined, both for peak and
non-peak times during the week. The times are defined as:
- Tuesday from 9am-6pm at a cost of 3 units per pro-rata hour of resource usage.
- Wednesday from 9am-6pm at a cost of 4 units per pro-rata hour of resource usage.
- Friday from 9am-6pm at a cost of 6 units per pro-rata hour of resource usage.
- Friday off-peak from midnight-midnight at a cost of 1 unit per pro-rata hour of resource usage.
- Saturday off-peak from midnight-midnight at a cost of 1 unit per pro-rata hour of resource usage.
- Sunday off-peak from midnight-midnight at a cost of 1 unit per pro-rata hour of resource usage.
The actual resource is used during a combination of peak and non-peak times:
- Wednesday 5.7.2017, 16:00 - 17:00. 1 hour during peak period.
- Friday 14.7.2017, 12:00 - 17:00. 5 hours during peak period.
- Friday 14.7.2017, 19:00 - 23:00. 4 hours during non-peak period.
- Saturday 15.7.2017, 10:00 - 22:00. 12 hours during non-peak period.
- Sunday 16.7.2017, 13:00 - 17:00. 4 hours during non-peak period.
The CostRule objects are used in order of their priority i.e. peak cost rules are
applied before non-peak cost rules are applied. If a CostRule does not overlap
with a date-span of resource usage, then the totalCost() method will return
the non-overlapping DateSpans in the array result.remainderSpans.
var caltime = require('caltime');
var calcost = require('calcost');
/* timezones */
const TZ_UTC = 'Etc/UTC'; // UTC timezone
// Peak during 09:00-18:00
const timespanPeak = caltime.timeSpan(9, 0, 0, 0, 9*60); // 09:00-18:00
// Tuesday Peak
const timeruleTuesday = caltime.timeRule(timespanPeak,
caltime.constants.CONSTRAINT_DAY_OF_WEEK,
caltime.constants.TUESDAY,
TZ_UTC);
const costruleTuesday = costruleCtor(timeruleTuesday,
3.0,
calcost.constants.RATETYPE_PER_HOUR_PRORATA);
// Wednesday Peak
const timeruleWednesday = caltime.timeRule(timespanPeak,
caltime.constants.CONSTRAINT_DAY_OF_WEEK,
caltime.constants.WEDNESDAY,
TZ_UTC);
const costruleWednesday = costruleCtor(timeruleWednesday,
4.0,
calcost.constants.RATETYPE_PER_HOUR_PRORATA);
// Friday Peak
const timeruleFriday = caltime.timeRule(timespanPeak,
caltime.constants.CONSTRAINT_DAY_OF_WEEK,
caltime.constants.FRIDAY,
TZ_UTC);
const costruleFriday = costruleCtor(timeruleFriday,
6.0,
calcost.constants.RATETYPE_PER_HOUR_PRORATA);
// Friday Off-peak
const timespanOffPeak = caltime.timeSpan(0, 0, 0, 0, 24*60); // 00:00 - 00:00+1
const timeruleOffPeakFriday = caltime.timeRule(timespanOffPeak,
caltime.constants.CONSTRAINT_DAY_OF_WEEK,
caltime.constants.FRIDAY,
TZ_UTC);
const costruleOffPeakFriday = costruleCtor(timeruleOffPeakFriday,
1.0,
calcost.constants.RATETYPE_PER_HOUR_PRORATA);
// Saturday Off-peak
const timeruleOffPeakSaturday = caltime.timeRule(timespanOffPeak,
caltime.constants.CONSTRAINT_DAY_OF_WEEK,
caltime.constants.SATURDAY,
TZ_UTC);
const costruleOffPeakSaturday = costruleCtor(timeruleOffPeakSaturday,
1.0,
calcost.constants.RATETYPE_PER_HOUR_PRORATA);
// Sunday Off-peak
const timeruleOffPeakSunday = caltime.timeRule(timespanOffPeak,
caltime.constants.CONSTRAINT_DAY_OF_WEEK,
caltime.constants.SUNDAY,
TZ_UTC);
const costruleOffPeakSunday = costruleCtor(timeruleOffPeakSunday,
1.0,
calcost.constants.RATETYPE_PER_HOUR_PRORATA);
// resource used for 6 hours during peak and 20 hours off-peak.
const spanA = caltime.dateSpan(dateB, null, 1*60, 0, 0); // Wednesday, 16:00 - 17:00, 1 hr. peak
const spanB = caltime.dateSpan(dateF, null, 5*60, 0, 0); // Friday, 12:00 - 17:00, 5 hrs. peak
const spanC = caltime.dateSpan(dateFa, null, 4*60, 0, 0); // Friday 19:00 - 23:00, 4 hrs. off-peak
const spanD = caltime.dateSpan(dateG, null, 12*60, 0, 0); // Saturday, 10:00 - 22:00, 12 hrs. off-peak
const spanE = caltime.dateSpan(dateP, null, 4*60, 0, 0); // Sunday, 13:00 - 17:00, 4 hrs. off-peak
const datespans = [spanA, spanB, spanC, spanD, spanE];
// step through the cost rules to calculate the total cost
let sumCost = 0.0;
// Tuesday
let ruleResult = costruleTuesday.totalCost(datespans, TZ_UTC);
sumCost += ruleResult.cost;
// Wednesday
ruleResult = costruleWednesday.totalCost(ruleResult.remainderSpans, TZ_UTC);
sumCost += ruleResult.cost;
// Friday
ruleResult = costruleFriday.totalCost(ruleResult.remainderSpans, TZ_UTC);
sumCost += ruleResult.cost;
// Friday off-peak
ruleResult = costruleOffPeakFriday.totalCost(ruleResult.remainderSpans, TZ_UTC);
sumCost += ruleResult.cost;
// Saturday off-peak
ruleResult = costruleOffPeakSaturday.totalCost(ruleResult.remainderSpans, TZ_UTC);
sumCost += ruleResult.cost;
// Sunday off-peak
ruleResult = costruleOffPeakSunday.totalCost(ruleResult.remainderSpans, TZ_UTC);
sumCost += ruleResult.cost;
// total cost due to all cost-rules
console.log(`Total cost of resource usage: ${sumCost}`); // 54.0Constants
The module makes several constants available in the constants object. Each
constant is a data member of this object. These constants define the way the
costs are calculated for a specific CostRule when they are passed to the
inRateType parameter of the costRule functional constructor.
API Documentation
The latest version of the API documentation can be generated using jsdoc. The
documentation is created in the docs/ directory.
$ cd <calcost-git-clone>
$ npm run -s docSupport
Bug Reports and New Feature Requests should be reported at the CalCost GitHub Issues Page.
Dependencies
calcost currently depends on three modules when in production. Other modules are
required to test or develop calcost. The production dependencies are:
License
calcost is copyright (c) 2017-2018 Michael McCarthy michael.mccarthy@ieee.org.
calcost is free software, licensed under the MIT licence. See the file LICENSE
in this distribution for more information.