Over a million developers have joined DZone.

Time4: A Useful Time Interval (Value) Capsule?


In the age of enlightment of object oriented Programming, somebody invented the strange concept of a (mutable) Date object.

Since then we are forced/doomed to work with Unix dates and calculate with exported time values in milliseconds since 1.1.1970,0t.

Calculations with time numbers often prove dangerous, since time values are more than numbers.

This experience leads to a simple question: What are time values exactly and how should we deal with them?

Time is, as we all know, a fundamental physical quantity measured in one or another unit.
Units are years, days, hours, seconds, etc. and quantities can be positive as well as negative.

So time quantities are values are values are values and they are immutable as some of us may know.

But it is crucial that working with time values affords accessing time quantities by mixed units and bags of mixed units.  How can we build such values with rich, mixed unit accessors in JavaScript:

First of all, years and months are problematic time quantities since their durations vary over time, hence we forbid them as valid time units.

Useful quantities for calculation and i/o are day, hour, minute, second, millisecond.
If long time interval quantities are absolutely necessary, one could implement year-extraction according to the Gregorian calender year duration of 365.2425 days.

Next, for the creation of time values one may wish several construction mechanisms (aka constructors), like

Time(startDate, endDate)

Function constructor overloading is not well supported in Javascript, so we choose a varying-argument-number as well as a parameter-bag approach.

Time4(hours, mins, secs, millis, days)

As parameter bag serves any object that may contain some of the properties:
days, hours, mins, secs, millis, start, end

The bag values and default 0 values are used to calculate the plausible time value.

The time value is either positive or negative or 0 of course. Positive time values produce always just positive quantities, negative time values produce just negatives.

Eg.: <1hours 1mins 1secs 1msecs> subtract <2hours 2secs> ==> <-59mins -999msecs>

Accessing certain quantities by accessor methods extract a value by applying some division, modulo and floor operations.

Interestingly enough, Javascripts modulo(%) and floor operation produce wrong results on negative time numbers, due to their semantic rules. So we have to correct these operations in specific floor and modulo functions.

function Time4(hours, mins, secs, millis, days){
   this.val = 0; //Time() delivers 0
   var bag, startTime=0, endTime=0, h=0, m=0, s=0, ms=0, d=0;
   if (arguments.length===1 && hours instanceof Object) {
     //assume param bag values
     bag = hours;
     if ('days' in bag) {d = bag.days;}
     if ('hours' in bag) {h = bag.hours;}
     if ('mins' in bag) {m = bag.mins;}
     if ('secs' in bag) {s = bag.secs;}
     if ('millis' in bag) {ms = bag.millis;}
     if ('start' in bag && bag.start instanceof Date) {startTime = bag.start.getTime();} else {startTime = 0;}
     if ('end' in bag && bag.end instanceof Date) {endTime = bag.end.getTime();} else {endTime = 0;}
   } else {
     if (hours) {h = hours;}
     if (mins) {m = mins;}
     if (secs) {s = secs;}
     if (millis) {ms = millis;}
     if (days) {d = days;}
   if (bag instanceof Object && ('start' in bag || 'end' in bag)) {
     this.val = endTime-startTime;
   } else {
     this.val = (((d*24 + h)*60 + m)*60 + s)*1000 + ms;

   Time4.prototype.add = function(t){
     return new Time4(0,0,0,this.val + t.val);
   Time4.prototype.subtract = function(t){
     return new Time4(0,0,0,this.val - t.val);
   Time4.prototype.multiply = function(f){
     return new Time4(0,0,0,this.val * f);
   Time4.prototype.divide = function(f){
     return new Time4(0,0,0,this.val / f);
   Time4.prototype.compare = function(t){
     return this.val - t.val;
   Time4.prototype.equals = function(t){
     return this.val===t.val;
   Time4.prototype.getDays = function(){
     return floor(this.val/(24*60*60*1000));
   Time4.prototype.getHours = function(){
     return modulo(floor(this.val/(60*60*1000)), 24);
   Time4.prototype.getMinutes = function(){   
     return modulo(floor(this.val/(60*1000)), 60);
   Time4.prototype.getSeconds = function(){
     return modulo(floor(this.val/1000), 60);
   Time4.prototype.getMillis = function(){
     return modulo(this.val, 1000);

   function modulo(n,m) {
     return (n < 0)? -Math.floor(Math.abs(n) % m): Math.floor(Math.abs(n) % m);
   function floor(n) {
     return (n<0)? Math.floor(n)+1: Math.floor(n);
   Time4.prototype.toString = function(){
     var days = this.getDays();
     var hours = this.getHours();
     var minutes = this.getMinutes();
     var seconds = this.getSeconds();
     var millis = this.getMillis();
     var temp = "";
     if (days!==0) {temp += days+"d";}
     if (hours!==0) {temp += hours+"h";}
     if (minutes!==0) {temp += minutes+"m";}
     if (seconds!==0) {temp += seconds+"s";}
     if (millis!==0) {temp += millis+"msec";}
     if (temp==="") {temp = "0t";}
     //temp += "::val:"+this.val;
     return temp;

Since this Time-related discussion and proposal applies very well to many other physical values/quantities, Programmers have to be extremly cautiously and must accept these challenges very seriously.

Some test results:

t1(1,1,1,1) : 1h1m1s1msec
t2(2,0,2,0) : 2h2s
t3(3,3,3,3,3) : 3d3h3m3s3msec
t4(2 Date diff) : 0t
t5(7h7s) : 7h7s
t2.subtract(t1) : 59m999msec
t2-t1.add(t1) : 2h2s
t4.subtract(t3) : -3d-3h-3m-3s-3msec
t4-t3.add(t3) : 0t
t6(1h1s) : 1h1s
t6div5 : 12m200msec
t6div5mult60 : 12h12s


The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}