Portfolio: Time Calculator

By

View the app: Time Calculator

Sample Source Files

Here are a few selected source files. To view the rest, visit the calculator (link above) and view source.

NOTE: Unlike the other items in my portfolio, I'm not releasing this one under an open source license. All rights are reserved. See the actual calculator for license terms.

calc.js

if(!document.getElementById) alert('Your browser doesn\'t support all the necessary functions to use the calculator.\\nPlease use a different browser.');

//utility functions
//--------------------------------

function byId(id) {
  return document.getElementById(id);
}
function crEl(el) {
  return document.createElement(el);
}

buttonQueue = [];
oldClassQueue = [];
function press(button) {
  if(errorDisplayed && button != 'c' && button != 'ac') return;
  var button = byId(button);
  if(button.className.indexOf('disabled') == -1) {// don't highlight the dot when it's disabled.
    if(button.className.indexOf(' pressed') == -1) {
      buttonQueue.push(button);
      oldClassQueue.push(button.className);
      button.className += ' pressed';
      setTimeout('restoreButton()', 200);
    }
    if(console) console.debug('Button "' + button.id + '" pressed.')
    button.click();
  }
}
function restoreButton() {
  buttonQueue.shift().className = oldClassQueue.shift();
}

//keyboard stuff
//--------------------------------

// onkeypress fires when characters are typed. Other keys such as arrows and
// backspace don't fire this event.
function keypressKeys(evt) {
  if(this.activeElement.id == 'q') return true; //ignore input to the search box
  if(!evt) evt = window.event;
  // find the code of the pressed key.
  if(evt.altKey || evt.ctrlKey) return true; // We don't want Ctrl- or Alt-modified keys
  var code;
  if(evt.key) {
    var key = evt.key;
    if (evt.keyCode) code = evt.keyCode;
  } else {
    if(evt.keyCode) code = evt.keyCode;
    else if(evt.charCode) code = evt.charCode;
    else code = 0;
    var key = String.fromCharCode(code);
  }
  if(console) console.debug('keypressKeys(): Key code: ' + code + '; Key: ' + key);
  
  // Decide which button to press
  if(key.match(/^[0-9]$/)) press('b' + key);
  else if(key.match(/^[s:S ]$/)) press('colon');
  else if(key == '.') {
    if(dotAsColon) press('colon');
    else press('dot');
  }
  else if(key.match(/^[vV,]$/)) press('dot');
  else if(key.match(/^[pP+]$/)) press('plus');
  else if(key.match(/^[-mM]$/)) press('sub');
  else if(key.match(/^[*tT]$/)) press('mul');
  else if(key.match(/^[dD/]$/)) press('div');
  else if(key.match(/^[eE=]$/)) press('eq');
  else if(key.match(/^[cC]$/)) press('c');
  else if(key.match(/^[aA]$/)) press('ac');
  else if(key.match(/^[bB<]$/)) press('bs');
  else if(key.match(/^[qQ]$/)) press('m+');
  else if(key.match(/^[wW]$/)) press('m-');
  else if(key.match(/^[rR]$/)) press('mr');
  else if(key.match(/^[xX]$/)) press('mc');
  else if(key.match(/^[hH?]$/)) {
    try {
      switchHelp('help-intro');
      help();
    }
    catch(e) {
      if(console) console.error('Help error: ' + e);
      else alert('Help error: ' + e);
    }
  }
  else if(key.match(/^[kK]$/)) keys();
  else {
    if(console) console.info('keypressKeys(): No keybinding for "' + key + '" (code: ' + code + ')');
    return true; // The key wasn't meant for us. Perhaps someone else is interested.
  }
  return false; // We've processed the key. No one else need mess with it.
}

function keyupKeys (evt) {
  if(this.activeElement.id == 'q') return true; //ignore input to the search box
  if(!evt) evt = window.event;
  var code = evt.keyCode;
  if(console) console.debug('keyupKeys(): Key code: ' + code);
  
  //handle special keys:
  switch(code) {
    case  9: // tab
      if(disable_tab) {
        if (console) console.debug('keyupKeys(): Tab disabled');
        return true;
      } else {
        press('colon');
        break;
      }
    case 39: // right arrow
      press('colon'); break;
    case 37: // left arrow
      press('colon');
      press('colon'); break;
    case 13: // enter
      press('eq'); break;
    case  8: // backspace
      press('bs'); break;
    case 46: // delete
    case 144:// numpad clear
      press('c'); break;
    case 27: // esc
      press('ac'); break;
    default: // Not a key this function handles
      if(console) console.debug('keyupKeys(): Not handled');
      return true;
  }
  if(console) console.debug('keyupKeys(): Handled');
  return false;
}
function keydownKeys(evt) { //kill backspace triggering back button
  if(this.activeElement.id == 'q') return true; //ignore input to the search box
  if(!evt) evt = window.event;
  var code = evt.keyCode;
  if(console) console.debug('keydownKeys(): Key code: ' + code);
  if(code == 8) { // 8 == backspace
    if(console) console.debug('keydownKeys(): Swallowing a backspace');
    if(evt.preventDefault) {
      evt.preventDefault();
    }
    return false;
  }
  return true;
}

document.onkeypress = keypressKeys;
document.onkeydown = keydownKeys;
document.onkeyup = keyupKeys;
function loadCalculator() {
  setBlur(document.getElementsByTagName('BODY')[0].children);
  var selections = ['h', 'm', 's'];
  var clears = ['c', 'ac'];
  var specialNumbers = ['dot', 'colon'];
  var i = null;
  for(i = 0; i < 10; i++) {
    byId('b' + i).onclick = function () { insertNumber(this.value); };
  }
  for(i = 0; i < selections.length; i++) {
    byId(selections[i]).onclick = function () { setSelection(this.id); };
  }
  for(i = 0; i < clears.length; i++) {
    byId(clears[i]).onclick = function () { clearX(this.value); };
  }
  for(i = 0; i < specialNumbers.length; i++) {
    byId(specialNumbers[i]).onclick = function () { insertNumber(this.value); };
  }
  byId('plus').onclick = function () { calc('+', false); };
  byId('sub').onclick = function () { calc('-', false); };
  byId('mul').onclick = function () { calc('*', false); };
  byId('div').onclick = function () { calc('/', false); };
  byId('bs').onclick = function () { backspace(); };
  byId('eq').onclick = function () { equals(); };
  byId('c').ondblclick = function () { clearX('AC'); };
  byId('m+').onclick = function () { memSet('+'); };
  byId('m-').onclick = function () { memSet('-'); };
  byId('mr').onclick = function () { memRecall(); };
  byId('mc').onclick = function () { memClear(); };
  byId('dotAsColon').onchange = function () {
    if(this.checked) setDotAsColon(true);
    else setDotAsColon(false);
  };
  byId('disable_tab').onchange = function () {
    if (this.checked) set_disable_tab(true);
    else set_disable_tab(false);
  };
  byId('display_time_as').onchange = function () { setDisplayTimeAs(this.value);
  };
  byId('help-close').onclick = help;
  setDotAsColon(getDotAsColon());
  set_disable_tab(get_disable_tab());
  setDisplayTimeAs(getDisplayTimeAs());
  if(displayTimeAs == 'ms') setSelection('m');
  //BigDecimal constants
  MINUS_ONE = new BigDecimal('-1');
  ZERO = new BigDecimal('0');
  TEN = new BigDecimal('10');
  SIXTY = new BigDecimal('60');
}
function getDotAsColon() {
  return getSetting('dotAsColon');
}
function setDotAsColon(status) {
  return setSetting(status, 'dotAsColon');
}
function get_disable_tab() {
  return getSetting('disable_tab');
}
function set_disable_tab(status) {
  return setSetting(status, 'disable_tab');
}
function getSetting(name) {
  var status = readCookie(name);
  if(status) return (status == 'true') ? true : false;
  else return false;
}
function setSetting(status, name) {
  var box = byId(name);
  if(status != window[name]) {
    window[name] = status;
    var str = (status==true)?'true':'false';
    createCookie(name,str,3650,'/html/time_calculator.htm');
  }
  if(box.checked != status) {
    box.checked = status;
  }
}
function getDisplayTimeAs() {
  var current = readCookie('display_time_as');
  if(current) return current;
  else return 'hms';
}
function setDisplayTimeAs(value) {
  var menu = byId('display_time_as');
  if(value != displayTimeAs) {
    displayTimeAs = value;
    createCookie('display_time_as', value, 3650, '/html/time_calculator.htm');
  }
  menu.value = value;
}
function setBlur(items) {
  var item;
  for(var i = 0; i < items.length; i++) {
    item = items[i];
    if(item.id == 'search-box-wrapper') continue;
    if(item.tagName == 'SELECT' || item.tagName == 'INPUT') continue;
    if(item.children) setBlur(item.children);
    try {
      item.onfocus = function () { this.blur(); };
    }
    catch (e) {
      if(console) console.warn(e);
    }
  }
}

function extraOnload() {
  loadCalculator();
}

//calculator functions
//--------------------------------
dotAsColon = false;
disable_tab = false;
displayTimeAs = 'hms';
selection = 'h';
operation = ''; //+-*/=E or empty
//stored values
v1 = null,v2 = null;
memory = null;
error = null;
errorDisplayed = false;
clearOnNext = false;
operatorPressedLast = false;

function hms2s(hms) { // hms is an array of [neg, h, m, s]
  var s = hms[3];
  var sixty_sq = SIXTY.multiply(SIXTY);
  s = s.add(hms[2].multiply(SIXTY)).add(hms[1].multiply(sixty_sq));
  if(hms[0]) s = s.multiply(MINUS_ONE);
  return s;
}

function s2hms(s) {
  var neg = false;
  var h, m;
  if(s.compareTo(ZERO) < 0) {
    neg = true;
    s = s.multiply(MINUS_ONE);
  }
  m = new BigDecimal(parseInt(s.divideInteger(SIXTY)) + '');
  s = s.remainder(SIXTY);
  if(displayTimeAs == 'hms') {
    h = new BigDecimal(parseInt(m.divideInteger(SIXTY)) + '');
    m = m.remainder(SIXTY);
  }
  else {
    h = ZERO;
  }
  return [neg, h, m, s];
}

function getScreenValue() {
  var neg = false;
  var h = byId('h').innerHTML;
  if(h.indexOf('Error') != -1) return false;
  if(h.indexOf('-') == 0) {
    neg = true;
    h = new BigDecimal(h);
    if(h.compareTo(ZERO) < 0) h = h.multiply(MINUS_ONE);
  }
  else h = new BigDecimal(h);
  var m = new BigDecimal(byId('m').innerHTML);
  var s = new BigDecimal(byId('s').innerHTML);
  return [neg, h, m, s];
}

function setSelection(s) {
  if(errorDisplayed) return;
  if(s != selection) {
    var oldSelection = selection;
    selection = s;
    byId(oldSelection).className = 'not-selected';
    byId(selection).className = 'selected';
    var x = byId('dot');
    if(s == 's') {
      x.removeAttribute('disabled');
      x.className = 'number';
    }
    else {
      x.setAttribute('disabled','disabled');
      x.className = 'disabled';
    }
  }
}

function setOperator(op) {
  operation = op;
  var x = byId('operator');
  switch(op) {
    case '':
      x.innerHTML = '&nbsp;';
      break;
    case '/':
      x.innerHTML = '&divide;';
      break;
    case '*':
      x.innerHTML = '&times;';
      break;
    case '-':
      x.innerHTML = '&minus;';
      break;
    default:
      x.innerHTML = op;
  }
}

function formatDisplay(hms) {
  var neg = hms[0];
  var h = hms[1];
  var m = hms[2];
  var s = hms[3];
  if(h.indexOf && h.indexOf('Error') != -1) return [h, m, s];
  if(m.compareTo(TEN) < 0) m = '0' + m;
  else m = m + '';
  if(s.compareTo(TEN) < 0) s = '0' + s;
  else s = s + '';
  if(neg) h = '-' + h;
  else h = h + '';
  return [h, m, stripTrailingZeroes(s)];
}

function stripTrailingZeroes(n) {
  if(n.indexOf('.') == -1) return n;
  var extras = n.match(/[0]+$/);  //what about n=100.00?
  if(extras) {
    n = n.slice(0, n.length - extras[0].length);
    if(n.match(/\.$/)) {
      n = n.slice(0, n.length - 1);
    }
  }
  return n;
}

function setDisplay(hms) {
  if(errorDisplayed) return;
  hms = formatDisplay(hms);
  byId('h').innerHTML = hms[0];
  byId('m').innerHTML = hms[1];
  byId('s').innerHTML = hms[2];
}

function clearDisplay() {
  setDisplay([false, ZERO, ZERO, ZERO]);
}

function insertNumber(n) {
  if(console) console.debug('insertNumber(' + n + ')');
  if(errorDisplayed) return;
  if(clearOnNext && n != ':') {
    clearDisplay();
    clearOnNext = false;
  }
  if(n == ':') {
    switch(selection) {
      case 'h':
        setSelection('m');
        break;
      case 'm':
        setSelection('s');
        break;
      case 's':
        setSelection('h');
        break;
    }
    return;
  }
  var x = byId(selection);
  if((n == '.') && (x.innerHTML.indexOf('.') >= 0 || byId('dot').className.indexOf('disabled') != -1)) return;
  writeInsertedDigit(n, x);
  operatorPressedLast = false;
}

function writeInsertedDigit(digit, element) { // "element" can be an element node or the ID of an element.
  if(typeof(element) == 'string') var x = byId(element);
  else {
    var x = element;
    element = x.id;
  }
  var y = x.innerHTML;
  digit += '';
  if(y == 0 && y.substring(0, 4) != '00.0') {
    if(x.innerHTML.indexOf('.') == -1) y = digit;
    else y = '.' + digit;
  }
  else y += digit;
  while(y[0] == 0) y = y.substring(1);
  var minLength = (element == 'm' || element == 's') ? 2 : 1;
  while(y.split('.')[0].length < minLength) y = '0' + y;
  x.innerHTML = y;
}

function storeValues() {
  var s = hms2s(getScreenValue());
  if(v1 == null || operation == '=') v1 = s;
  else v2 = s;
}

function calc(op, equalsOp) { // equalsOp should be false unless the = key has been pressed.
  if(console) console.debug('calc(' + op + ', ' + equalsOp + ')');
  if(errorDisplayed) return;
  if(operatorPressedLast) {
    setOperator(op);
    tapeChangeLastOperator(op);
    if(operation == '/' || operation == '*') setSelection('s');
    else if(displayTimeAs == 'ms') setSelection('m');
    else setSelection('h');
    return;
  }
  storeValues();
  if(v2 == null) {
    setOperator(op);
    appendToTape(getScreenValue(), op, equalsOp);
  }
  else {
    appendToTape(getScreenValue(), op, equalsOp);
    switch(operation) {
      case '+':
        v1 = v1.add(v2);
        break;
      case '-':
        v1 = v1.subtract(v2);
        break;
      case '*':
        v1 = v1.multiply(v2);
        break;
      case '/':
        if(v2.compareTo(ZERO) == 0) {
          error = 'Div. by 0!';
          break;
        }
        var precision = Math.max(Math.abs(v1.exp), Math.abs(v2.exp)) + 10;
        var form = MathContext.prototype.PLAIN;
        v1 = v1.divide(v2, new MathContext(precision, form));
        break;
    }
    v2 = null;
    setOperator(op);
    displayResult(v1);
  }
  clearOnNext = true;
  if(operation == '/' || operation == '*') setSelection('s');
  else if(displayTimeAs == 'ms') setSelection('m');
  else setSelection('h');
  operatorPressedLast = true;
}

function equals() {
  if(console) console.debug('equals()');
  if(errorDisplayed) return;
  operatorPressedLast = false;
  if(!error) {
    calc(operation, true);
    setOperator('=');
  }
  else setOperator('');
  appendToTape(getScreenValue(), '=', false);
  operatorPressedLast = false;
}

function displayResult(s) {
  if(error) setDisplay([false,' Error ',' Error ',' Error ']);
  else setDisplay(s2hms(s));
  if(displayTimeAs == 'ms') setSelection('m');
  else setSelection('h');
}

function clearX(v) {
  if(v == 'C' && !(byId('h').innerHTML * 1) && !(byId('m').innerHTML * 1) && !(byId('s').innerHTML * 1)) v = 'AC';
  else if(operation == '=') v = 'AC';
  if(v == 'AC') {
    error = null;
    errorDisplayed = false;
  }
  clearDisplay();
  if(displayTimeAs == 'ms') setSelection('m');
  else setSelection('h');
  if(v == 'AC') {
    setOperator('');
    v1 = null;
    v2 = null;
    operatorPressedLast = false;
  }
  else operatorPressedLast = true;
}

function backspace() {
  if(errorDisplayed) return;
  var x = byId(selection);
  var y = x.innerHTML;
  if(y == 0 && y.indexOf('.') == -1) return;
  var z = y.substring(0,y.length-1);
  if(z.length == 0) z = 0;
  x.innerHTML = z;
  writeInsertedDigit('',selection);
}

function memSet(operation) {
  if(errorDisplayed) return;
  var s = hms2s(getScreenValue());
  if(memory == null) {
    memory = s;
    byId('memory-display').innerHTML = 'M';
  }
  else {
    if(operation == '+') memory = memory.add(s);
    else memory = memory.subtract(s);
    setDisplay(s2hms(memory));
  }
  setOperator('');
  setSelection('h');
  appendToTape(getScreenValue(), 'M');
}

function memRecall() {
  if(errorDisplayed) return;
  if(memory == null) alert('Nothing in memory!');
  else {
    setDisplay(s2hms(memory));
    setSelection('h');
    operatorPressedLast = false;
  }
}

function memClear() {
  memory = null;
  byId('memory-display').innerHTML = '&nbsp;';
}

calc.css

/* Calculator Styles
---------------------*/
#help-bar {
  text-align:center;
}
#calculator {
  border: solid 3px black;
  background: #ccf;
  margin-left:auto;
  margin-right:auto;
}
.buttons td {
  text-align: center;
}
/*.buttons td input, .buttons td input.disabled:hover {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAoCAYAAAA/tpB3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIAQQtANCeS34AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAQklEQVQI15XKKQ6AQABD0UfZhvsrjgmCxWHIJCMQVPz0p4U1uIPzC0dw1bYFe4/Se9MFU1CCubal1dJexoqh1X/DA51dDBiXcwb6AAAAAElFTkSuQmCC);
}*/
.buttons td input {
  width: 4em;
  height: 3em;
  background-color: #003;
  color: #ccf;
  font-weight: bold;
  border: #ccf solid 2px;
  //-moz-border-radius: 0.75em;
  border-radius: 0.25em;

  &:hover, &:active, &.pressed {
    //background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAoCAYAAAA/tpB3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIAQQ0KH4rSpwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAP0lEQVQI14XKsQqAMBAE0eedEfxR/78KqI1VJBHFLQaGWdgCZ+D4xx6oiTVREpO2Eg1LDPoZ8sY86iPEy6XTC/uXCuen3vfEAAAAAElFTkSuQmCC);
    background-color: darken(#003, 5%);
    color: #fff;
  }
  
  &.number {
    background-color: #225;
    
    &:hover, &.pressed, &:active {
      background-color: darken(#225, 5%);
    }
  }
  &.memory {
    background-color: #447;

    &:hover, &.pressed, &:active {
      background-color: darken(#447, 5%);
    }
  }
  &.disabled {
    background-color: #ccf;
    //color: #666;
    color: #ccf;
    
    &:active {
      border: #ccf solid 2px;
    }
  }
  &.colon {
    background-color: #060;

    &:hover, &:active, &.pressed {
      background-color: darken(#060, 5%);
    }
  }
  &.pressed, &:active {
    //border-style: inset;
    border-color: red;
    //background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAoCAYAAAA/tpB3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oIAQUEOlMWZxAAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAP0lEQVQI143JMQ6AMBADwdGFKyLyf4mPJikp4RqEi5W9hiuwA6u2FZh17nrMwG7oDdm8MwJnxeN6ID9x/HR5A6VvDhczW2AqAAAAAElFTkSuQmCC);
  }
}
#screen {
  background: #eee;
  text-align:right;
  font-size: 110%;
  font-weight: bold;
  padding:0.5em;
  border: 1px solid #003;
  cursor:default;
}
#operator, #memory-display {
  font-size:80%;
  color:#c00;
}
#operator {
  width: 3em;
}
#memory-display {
  display: block;
  float: left;
  position: relative;
  top: 0.25em;
  left: 0;
}
#h,#m,#s {
  cursor: pointer;
}
#h.selected,#m.selected,#s.selected {
  cursor:default;
}
.selected {
  background: #bbb;
  outline: 1px solid #c00;
}
.extra-copyright {
  font-size:65%;
}

time_calculator.htm

This is an excerpt showing the HTML underlying the calculator.

<!-- The following fixes a tape display bug in IE. -->
<!--[if IE]>
<style type="text/css">
#tape .operator {
  padding-right: 20px;
}
</style>
<![endif]-->

<p id="help-bar" style="text-indent:0"><a href="javascript:help()" id="help-link" title="Click to show/hide help">Help</a> | <b><a href="javascript:keys()">Keyboard Shortcuts</a></b> | <b><a href="javascript:settings()">Settings</a></b> | <b><a href="javascript:about()">About Time Calculator</a></b> | <a href="Time%20Calculator%20Download%20Edition.html">Download Edition</a></p>
    <div id="help-container" class="h-hide">
    <div id="help-close">&times;</div>
    <div id="help">

      <div id="help-contents">
        <p id="a-help-intro" class="help-selected"><a href="javascript:switchHelp('help-intro')">Introduction</a></p>
        <p id="a-help-usage"><a href="javascript:switchHelp('help-usage')">Basic Usage</a></p>
        <p id="a-help-keys"><a href="javascript:switchHelp('help-keys')">Keyboard</a></p>
        <p id="a-help-mult"><a href="javascript:switchHelp('help-mult')">Multiplication and Division</a></p>
        <p id="a-help-faq"><a href="javascript:switchHelp('help-faq')">FAQ</a></p>
        <p id="a-help-bugs"><a href="javascript:switchHelp('help-bugs')">Bugs</a></p>
        <p id="a-help-settings"><a href="javascript:switchHelp('help-settings')">Settings</a></p>
        <p id="a-help-hist"><a href="javascript:switchHelp('help-hist')">Version History</a></p>
        <p id="a-help-about"><a href="javascript:switchHelp('help-about')">About Time Calculator</a></p>
      </div>
      <div id="help-text">
        <div id="help-intro">
          <p>Doing math with times isn't as simple as doing math with regular numbers because times use a different base. Time Calculator simplifies this problem. It provides:</p>
          <ul>
            <li>An intuitive, calculator-like interface</li>
            <li>Support for multiple calculations</li>
            <li>Limited support for multiplication and division (WARNING: Read the multiplication and division section of help before using those functions&mdash;or risk incorrect results.)</li>
            <li>Memory functions</li>
          </ul>
          <p>If you have any comments or questions, feel free to <span class="mangle-email">e-mail me</span>.</p>
          <p><i>Scott Severance</i></p>

        </div>
        <div id="help-usage" class="h-hide">
          <h2>Time Format</h2>
          <p>The format for displaying times is <b>hours:minutes:seconds</b> (e.g., 2 hours, 30 minutes, and 15.4 seconds is displayed as 2:30:15.4). When entering times, the selection box (outlined in red) determines whether you are entering hours, minutes, or seconds.</p>
          <p>Click <span class="button">:</span> to advance the selection box. Alternatively, you can click directly on the calculator screen to set the selection box.</p>
          <p>IMPORTANT NOTE: Time Calculator supports hours, minutes, and seconds. It doesn't yet know anything about days. Thus, hours outside the range 0-23 are acceptable. If you need to handle days, you can do a manual conversion. For example, 14:00:00 - 15:00:00 = -1:00:00. If you add 24 hours (-1:00:00 + 24:00:00), you'll get the time on the previous day (23:00:00). Remember that this is just a workaround, as Time Calculator has no idea about days. Support for days is on my list of features to add, but I don't have a whole lot of spare time these days.</p>

          <h2>Buttons</h2>
          <p>Most buttons work as expected. Here are some special buttons:</p>
          <ul>
            <li><span class="button">:</span> Advances the selection box.</li>
            <li><span class="button">.</span> Inserts a decimal point. This button is disabled if the selection box is not on the seconds field.</li>
            <li><span class="button">&larr;</span> Backspace key. Deletes the last number from the selected field.</li>
            <li><span class="button">AC</span> Clears the the screen and resets the calculator. However, <span class="button">AC</span> doesn't clear the memory.</li>
            <li><span class="button">C</span> If the value on the screen is not 0:00:00, resets the screen without resetting the calculation. Otherwise, behaves like <span class="button">AC</span>.</li>
            <li><span class="button">M+</span> and <span class="button">M&minus;</span> Add/subtract the value displayed on the screen to the value currently in memory; or, if memory is empty, sets memory.</li>
            <li><span class="button">MR</span> Displays the value stored in memory on the screen, replacing whatever is currently on the screen.</li>
            <li><span class="button">MC</span> Clears the memory.</li>
          </ul>
        </div>
        <div id="help-mult" class="h-hide">
          <p>Unless you want to multiply or divide a time by another time (you probably don't), be sure to enter the non-time factor in the seconds field. If you don't, you will get unexpected results. As a reminder, the selection box is moved to the seconds field whenever you click <span class="button">&times;</span> or <span class="button">&divide;</span>.</p>
          <p>Here's why: Before calculating, all values are converted to seconds. That means that 0:30:00 &times; 0:00:2 gets converted (in part) to 0:30:00 &times; 2 and will output 1:00:00 as expected. However, 0:30:00 &times; 0:2:00 gets converted (in part) to 0:30:00 &times; <i>60</i> and will output 60:00:00. 0:30:00 &times; 2:00:00 gets converted (in part) to 0:30:00 &times; <i>7200</i> and will output 3600:00:00.</p>

        </div>
        <div id="help-keys" class="h-hide">
          <h2>Keyboard Shortcuts</h2>
          <p>To enter a number, type it. For other keys, refer to the table below (note that many buttons can be activated by several keys):</p>
          <table>
            <tr>
              <th>Button</th>
              <th>Keys</th>
              <td>&nbsp; &nbsp; &nbsp; </td>
              <th>Button</th>
              <th>Keys</th>
            </tr>
            <tr>
              <td><span class="button">+</span></td>
              <td class="keys"><span class="key">+</span> <span class="key">P</span></td>
              <td>&nbsp;</td>
              <td><span class="button">&minus;</span></td>
              <td class="keys"><span class="key">-</span> <span class="key">M</span></td>
            </tr>
            <tr>
              <td><span class="button">&times;</span></td>
              <td class="keys"><span class="key">*</span> <span class="key">T</span></td>
              <td>&nbsp;</td>
              <td><span class="button">&divide;</span></td>
              <td class="keys"><span class="key">/</span> <span class="key">D</span></td>
            </tr>
            <tr>
              <td><span class="button">=</span></td>
              <td class="keys"><span class="key">=</span> <span class="key">Enter</span> <span class="key">E</span></td>
              <td>&nbsp;</td>
              <td><span class="button">.</span></td>
              <td class="keys"><span class="key">.</span> <span class="key">,</span> <span class="key">V</span></td>
            </tr>
            <tr>
              <td><span class="button">&larr;</span></td>
              <td class="keys"><span class="key">B</span> <span class="key">&lt;</span> <span class="key">Backspace</span></td>
              <td>&nbsp;</td>
              <td><span class="button">:</span></td>
              <td class="keys"><span class="key">:</span> <span class="key">Tab</span> <span class="key">Right Arrow</span> <span class="key">Space</span> <span class="key">S</span></td>
            </tr>
            <tr>
              <td><span class="button">C</span></td>
              <td class="keys"><span class="key">C</span> <span class="key">Delete</span> <span class="key">Clear</span></td>
              <td>&nbsp;</td>
              <td><span class="button">AC</span></td>
              <td class="keys"><span class="key">A</span> <span class="key">Esc</span></td>
            </tr>
            <tr>
              <td><span class="button">M+</span></td>
              <td class="keys"><span class="key">Q</span></td>
              <td>&nbsp;</td>
              <td><span class="button">M&minus;</span></td>
              <td class="keys"><span class="key">W</span></td>
            </tr>
            <tr>
              <td><span class="button">MR</span></td>
              <td class="keys"><span class="key">R</span></td>
              <td>&nbsp;</td>
              <td><span class="button">MC</span></td>
              <td class="keys"><span class="key">X</span></td>
            </tr>
            <tr>
              <td>Help</td>
              <td class="keys"><span class="key">H</span> <span class="key">?</span></td>
              <td>&nbsp;</td>
              <td>Keybd.<br />Shortcuts</td>
              <td class="keys"><span class="key">K</span></td>
            </tr>
          </table>
        </div>
        <div id="help-faq" class="h-hide">
          <h2>How can I enter 2 PM?</h2>
          <p>The calculator works with absolute times, so it is unaware of the time of day. You can't enter PM, just as you can't enter AM. Just enter the raw hours, minutes, and seconds.</p>
          <p>If you're trying to find out how long you worked if you started at 8:22 AM and quit at 5:35 PM, you can convert to 24-hour time by adding 12 hours to every PM time. So, you can enter <pre>5:35:00 <span class="button">+</span> 12:00:00 <span class="button">&minus;</span> 8:22:00</pre> to get the total time you worked, 9 hours and 13 minutes.</p>
          <h2>Is there an iPhone or Android app?</h2>
          <p>An Android app is planned, but not ready yet. An iPhone version is unlikely since I refuse to buy Apple products and thus have no way of testing such an app.</p>
        </div>
        <div id="help-bugs" class="h-hide">
          <ul>
            <li>Some keyboard shortcuts don't work in all browsers. All such keys have alternatives. If the one you tried doesn't work, try one of the alternatives.</li>
            <li>The <span class="button">&larr;</span> Button doesn't work in older versions of Internet Explorer if the selected field contains more than one digit.</li>
          </ul>
        </div>
        <div id="help-settings" class="h-hide">
          <form onsubmit="return false">
            <fieldset>
              <legend>Key mapping</legend>
              <p>Users of the numeric keypad might prefer that the key <span class="key">.</span> activated <span class="button">:</span> instead of <span class="button">.</span>. Here's an option you can set:</p>
                <input type="checkbox" id="dotAsColon" value="dotAsColon" /><label for="dotAsColon">Map <span class="key">.</span> to <span class="button">:</span></label>
              <hr>
              <p>On some systems, using the Alt+Tab keyboard shortcut
                to switch between windows also causes the selection on
                the time calculator to advance. This option prevents
                this. Note, however, that it also disables the tab key
                to advance between fields.</p>
                <input type="checkbox" id="disable_tab"
                value="disable_tab" /><label for="disable_tab">Stop the
                  <span class="key">Tab</span> key from advancing the
                  selection.</label>
            </fieldset><br>
            <fieldset>
              <legend>Output format</legend>
              <p>Some users may prefer times to be always displayed in minutes and seconds, instead of hours, minutes, and seconds.</p>
                <label for="display_time_as">Display time as: </label>
                <select name="display_time_as" id="display_time_as">
                  <option value="hms" selected="selected">Hours, minutes, seconds</option>
                  <option value="ms">Minutes, seconds</option>
                </select>
            </fieldset>
          </form>
        </div>
        <div id="help-hist" class="h-hide">
          <h2>Time Calculator 3.4.2</h2>
          <p><i>November 22, 2020</i></p>
          <ul>
            <li>Because on some systems hitting the <span
              class="key">Alt</span>+<span class="key">Tab</span>
            keyboard shortcut to switch windows causes the selection to
            advance, added an option to disable that functionality of
            the tab key. As a consequence, when the option is enabled,
            the tab key won't perform its normal function, either.</li>
          </ul>
          <h2> Time Calculator 3.4.1</h2>
          <p><i>April 18, 2018</i></p>
          <ul>
            <li>Slightly modernized the calculator’s appearance</li>
            <li>Fixed display issues apparently caused by changes in browsers over the years</li>
          </ul>
          <h2>Time Calculator 3.4</h2>
          <p><i>August 15, 2015</i></p>
          <ul>
            <li>Added an option to display times in minutes and seconds instead of hours, minutes, and seconds. Thanks to Patrícia Bateira for the suggestion.</li>
            <li>Added close button to help.</li>
            <li>Certain versions of Chrome caused a problem by navigating back instead of deleting a number when the backspace key was pressed. Fixed.</li>
          </ul>
          <h2>Time Calculator 3.3.1</h2>
          <p><i>July 24, 2012</i></p>
          <ul>
            <li>Fixed a bug in the memory feature that was introduced by the change to BigDecimal in version 3.3.</li>
            <li>Fixed the stripTrailingZeroes functionality.</li>
          </ul>
          <h2>Time Calculator 3.3</h2>
          <p><i>December 30, 2011</i></p>
          <ul>
            <li>Fractional seconds are now calculated correctly. This has been a longstanding bug, and I'm happy to have it fixed thanks to the awesome BigDecimal.js.</li>
          </ul>
          <h2>Time Calculator 3.2.1</h2>
          <p><i>September 12, 2011</i></p>
          <ul>
            <li>Fixed two Internet Explorer bugs:
              <ul>
                <li>When there is another window open, the IE window with the time calculator no longer jumps behind all other windows.</li>
                <li>The operators are no longer cut off from the edge of the tape in IE.</li>
              </ul></li>
          </ul>
          <h2>Time Calculator 3.2</h2>
          <p><i>September 11, 2011</i></p>
          <ul>
            <li>Hopefully fixed a bug that caused Internet Explorer windows to jump behind all other windows in some cases.</li>
            <li>Fixed several bugs related to the history tape. The tape should now be easier to understand.</li>
            <li>Enhanced the documentation by adding a FAQ section and updating the bugs section to remove obsolete bugs and to reflect the problems with floating point math.</li>
          </ul>
          <h2>Time Calculator 3.1</h2>
          <p><i>November 12, 2010</i></p>
          <ul>
            <li>Users can now click entries on the history tape to copy the value to the calculator screen. Thanks to Kevin Matzdorf for suggesting this.</li>
            <li>Fixed a long-standing bug related to multiple calculations. Now, clicking operation buttons after a result is displayed, or clicking different operation buttons in succession works correctly.</li>
            <li>Better error handling</li>
            <li>Minor fixes/enhancements</li>
          </ul>
          <h2>Time Calculator 3.0.4</h2>
          <p><i>November 3, 2010</i></p>
          <ul>
            <li>The calculator now remembers the value of the "<span class="key">.</span> as <span class="button">:</span>" keyboard setting.</li>
          </ul>
          <h2>Time Calculator 3.0.3</h2>
          <p><i>October 28, 2010</i></p>
          <ul>
            <li>Added note to the "Basic Usage" section of the help explaining Time Calculator's lack of support for days. Also suggested a workaround.</li>
            <li>Fixed some typos.</li>
          </ul>
          <h2>Time Calculator 3.0.2</h2>
          <p><i>August 12, 2010</i></p>
          <ul>
            <li>Fixed a bug that kept Time Calculator 3 from working in older versions of Internet Explorer.</li>
            <li>Minor UI changes.</li>
          </ul>
          <h2>Time Calculator 3.0.1</h2>
          <p><i>August 8, 2010</i></p>
          <ul>
            <li>Changes made to 3.0 broke older versions of Internet Explorer. Since I don't have access to any version other than 8, I'm not able to find and fix the problem. This version presents users of older versions with several workarounds.</li>
          </ul>
          <h2>Time Calculator 3.0</h2>
          <p><i>August 1, 2010</i></p>
          <ul>
            <li>Time Calculator now has a memory.</li>
            <li>Added a calculation history panel.</li>
            <li>Vastly improved keyboard handling. Using the calculator via the keyboard should now be much simpler. See the <a href="javascript:switchHelp('help-keys')">keyboard</a> help section for details.</li>
            <li>Added an option, located under keyboard shortcuts, to map the <span class="key">.</span> key to the <span class="button">:</span> button in order to simplify entering times via the numeric keypad. This option is disabled by default. Thanks to Todd Andrews for the suggestion.</li>
            <li>Several user interface enhancements.</li>
            <li>Significant code cleanup.</li>
          </ul>
          <h2>Time Calculator 2.4</h2>
          <p><i>October 28, 2008</i></p>
          <ul>
            <li>Replaced the algorithm to convert from seconds to hours, minutes, and seconds. The old algorithm was really inefficient, especially for large values.</li>
          </ul>
          <h2>Time Calculator 2.3</h2>
          <p><i>May 4, 2008</i></p>
          <ul>
            <li>Added keyboard shortcuts</li>
          </ul>
          <h2>Time Calculator 2.2</h2>
          <p><i>February 28, 2008</i></p>
          <ul>
            <li>Clarified the legal language as regards distribution</li>
            <li>Dropped the 2 from Time Calculator, and dropped the link to Time Calculator 1</li>
          </ul>
          <h2>Time Calculator 2.1</h2>
          <p><i>January 9, 2006</i></p>
          <ul>
            <li>Minor user interface enhancements</li>
            <li>Added legal language to the about page</li>
            <li>Added version history page</li>
          </ul>
          <h2>Time Calculator 2.0</h2>
          <p><i>January 8, 2006</i></p>
          <ul>
            <li>Completely new interface</li>
            <li>Completely new back end</li>
            <li>Added limited support for multiplication and division</li>
            <li>Added support for multiple calculations</li>
          </ul>
          <h2>Time Calculator 1.0</h2>
          <p><i>2001</i></p>
          <ul>
            <li>Initial release</li>
            <li>Addition and subtraction</li>
            <li>Single calculations only</li>
          </ul>
        </div>
        <div id="help-about" class="h-hide">
          <p><b>Time Calculator 3.4.2</b><br />
          Copyright © 2001-2020 by <span class="mangle-email">Scott Severance</span><br />
          All rights reserved.</p>
          <p>NOTE: The following terms apply to the Internet edition of Time Calculator (which you are currently using). Separate terms apply to the <a href="Time%20Calculator%20Download%20Edition.html">download edition</a>.</p>
          <p>You may freely use Time Calculator, but if you want to copy it, mirror it, distribute it, modify it, or create a derivitive work, you need my <span class="mangle-email">permission</span>.</p>
          <p>The intent of these terms is to ensure that Time Calculator is only available on one website. You may, of course, link to Time Calculator. You don't need permission to do so. Restrictions on how you may link (as some sites use) are bogus. However, I request that you don't frame my site.</p>
          <p>Time Calculator has no warranty of any kind whatsoever (not even an implied warranty). Use it at your own risk. If your jurisdiction does not permit such warranty exclusions, you may not use Time Calculator.</p>
          <p>If you have any questions or suggestions, <span class="mangle-email">drop me a line</span>. Many of the features of Time Calculator are the direct result of user feedback.</p>
          <h2>Copyright Notices</h2>
          <p>The following notice applies <em>only</em> to the BigDecimal.js library used by this calculator:</p>
          <div class="extra-copyright"><ul>
            <li>Copyright © 2011 Daniel Trebbien</li>
            <li>Portions Copyright © 2003 STZ-IDA and PTV AG, Karlsruhe, Germany</li>
            <li>Portions Copyright © 1995-2001 International Business Machines Corporation and others</li>
          </ul>
          <p>All rights reserved.</p>
          <p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation.</p>
          <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
          <p>Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder.</p></div>
        </div>
      </div>
    </div>
    </div>
<!-- div class="content-rectangle"><script type="text/javascript" --><!--
google_ad_client = "ca-pub-4304188868853383";
/* Small Rectangle */
google_ad_slot = "4580236046";
google_ad_width = 250;
google_ad_height = 250;
//-->
<!-- /script>
<script type="text/javascript"
src="//pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div -->
    <noscript><p class="first-paragraph">Time Calculator requires Javascript in order to function. There is no way to remove this dependency. Please enable Javascript in order to use this calculator.</p></noscript>
    <p class="help-tag">To <b>enter numbers</b>, just start typing. To
    <b>change fields</b>, press <span class="key" title="Space"
    style="font-size:125%">Space</span> or <span class="colon"
    title="“:” button or key" style="font-size:125%">:</span>.</p>
    <form method="post" onsubmit="return false;" action="javascript:equals()">
    <table id="calculator">
      <tr>
        <td colspan="6" id="screen" role="status"><span id="memory-display">&nbsp;</span>
        <span id="h" class="selected" title="Hours">0</span>:<span id="m" title="Minutes">00</span>:<span id="s" title="Seconds">00</span>
        <span id="operator">&nbsp;</span></td>
    <td id="tape-container" rowspan="5"><div><table cellspacing="0" cellpadding="0">
      <tbody id="tape">
        <tr>
          <th colspan="8">Calculation History</th>
        </tr>
        <tr>
            <th colspan="8">(Click&nbsp;entry&nbsp;to<br>copy&nbsp;to&nbsp;calculator)</th>
        </tr>
      </tbody></table></div></td>
      </tr>
      <tr class="buttons">
        <td><input type="button" class="number" name="b7" id="b7" value="7" /></td>
        <td><input type="button" class="number" name="b8" id="b8" value="8" /></td>
        <td><input type="button" class="number" name="b9" id="b9" value="9" /></td>
        <td><input type="button" name="div" id="div" value="&divide;" /></td>
        <td><input type="button" name="c" id="c" value="C" aria-label="clear" /></td>
        <td><input type="button" class="memory" name="m+" id="m+" value="M+" aria-label="add to memory" /></td>
      </tr>
      <tr class="buttons">
        <td><input type="button" class="number" name="b4" id="b4" value="4" /></td>
        <td><input type="button" class="number" name="b5" id="b5" value="5" /></td>
        <td><input type="button" class="number" name="b6" id="b6" value="6" /></td>
        <td><input type="button" name="mul" id="mul" value="&times;" /></td>
        <td><input type="button" name="ac" id="ac" value="AC" /></td>
        <td><input type="button" class="memory" name="m-" id="m-" value="M&minus;" aria-label="remove from memory" /></td>
      </tr>
      <tr class="buttons">
        <td><input type="button" class="number" name="b1" id="b1" value="1" /></td>
        <td><input type="button" class="number" name="b2" id="b2" value="2" /></td>
        <td><input type="button" class="number" name="b3" id="b3" value="3" /></td>
        <td><input type="button" name="sub" id="sub" value="&minus;" /></td>
        <td><input type="button" name="bs" id="bs" value="&larr;" title="Backspace" aria-label="backspace" /></td>
        <td><input type="button" class="memory" name="mr" id="mr" value="MR" aria-label="memory recall" /></td>
      </tr>
      <tr class="buttons">
        <td><input type="button" class="number" name="b0" id="b0" value="0" /></td>
        <td><input type="button" class="disabled" name="dot" id="dot" value="." disabled="disabled" /></td>
        <td><input type="button" class="colon" name="colon" id="colon" value=":" aria-label="Switch between hours, minutes, and seconds" title="Switch between hours, minutes, and seconds" /></td>
        <td><input type="button" name="plus" id="plus" value="+" /></td>
        <td><input type="button" name="eq" id="eq" value="=" /></td>
        <td><input type="button" class="memory" name="mc" id="mc" value="MC" aria-label="memory clear" /></td>
      </tr>
    </table>
    </form>