Hours Calculator OnlineEvery minute, where it actually went.

How this calculator works

Last updated 1 November 2026

In short

Every tool on this site parses your clock times into whole minutes, does all the arithmetic in integer minutes, and converts to decimal hours only at the very last step. That order is deliberate: it is the reason a result can never drift by a hundredth of an hour. The calculation code is a single module of pure functions covered by 50 automated unit tests that run on every change.

This page is the technical reference for the whole site. It documents the exact formula behind each calculator, the rules used to read a time you typed, and the way the arithmetic is checked. If a number on the hours calculator or the time card calculator ever disagrees with what you work out by hand, this page should tell you which rule produced it.

Where the calculation happens

All of it happens in your browser. The site is static HTML, CSS and vanilla JavaScript served by nginx — there is no backend, no database and no server-side processing. The times, break lengths and wage rates you type are never transmitted anywhere, which is also why there is nothing to sign up for. The privacy policy sets out exactly what that means in practice.

The calculation functions live in one module, separate from all the code that touches the page. Every function takes plain values, returns plain values, and throws a readable error instead of guessing when an input makes no sense. That separation is what makes the arithmetic testable in isolation.

Step 1 — Parsing a time into minutes

Before anything can be added or subtracted, a time string becomes a single integer: minutes elapsed since midnight, from 0 to 1439. The parser applies these rules in order.

  1. The text is trimmed and lower-cased, and a trailing meridiem is detected and removed. am, a.m., pm, p and p.m. are all recognised, with or without a space.
  2. Dots and spaces are normalised to colons, so 9.30 and 9 30 are read the same as 9:30. Repeated separators collapse, and a trailing separator is dropped.
  3. What is left is read by shape. One or two digits is an hour on its own (9 → 9:00). Three digits is one hour digit plus two minute digits (930 → 9:30). Four digits is two and two (0930 → 9:30, 2130 → 21:30). Anything containing a colon is split on it, and both parts must be one or two digits.
  4. Minutes above 59 are rejected outright.
  5. With a meridiem, the hour must be 1–12. 12 AM becomes hour 0 and PM adds 12, so 12:00 AM is 0 and 12:30 PM is 750.
  6. Without a meridiem the hour must be 0–23, with one exception: 24:00 is accepted and read as midnight.

The result is hour × 60 + minutes. So all of 9, 930, 0930, 9:30, 9.30 and 9:30 AM become the integer 570, and both 9:30pm and 21:30 become 1290. Anything that does not match a rule above raises an error naming the field, rather than silently producing a wrong total.

Step 2 — Midnight crossover

The gross length of a shift starts as a plain subtraction in minutes:

Gross minutes, before any crossover check:

gross = end − start

If that result is negative, the end time must fall on the following day, so one whole day of minutes is added:

A shift from 10:00 PM to 6:30 AM:

390 − 1320 = −930 → −930 + 1440 = 510 minutes = 8:30

There is no checkbox for this and no date to enter. A negative difference is the signal, and the result is flagged as overnight so you can see that the assumption was made. The consequence worth knowing: a shift can never be recorded as longer than 24 hours, because the crossover adds exactly 1440 minutes once. Two identical times give zero, not a full day.

Step 3 — Deducting the break

Breaks are entered in whole minutes and rounded to an integer before use. The net figure is simply the gross minus the break:

9:00 AM to 5:30 PM with a 30-minute unpaid break:

1050 − 540 = 510 → 510 − 30 = 480 minutes = 8:00

A break longer than the shift itself is rejected with an error that states both lengths. It is never shown as a negative total, because a negative number of hours worked is not a meaningful answer to the question asked — it is a typo, and saying so is more useful than displaying −0:15. Whether a given break should be deducted at all is a separate question that depends on your rules rather than on arithmetic.

Step 4 — Converting to decimal hours

Decimal hours are produced once, at the end, from the final minute total:

Minutes to decimal hours, rounded to two places:

decimal = round(minutes ÷ 60 × 100) ÷ 100

So 7:45 is 465 minutes and becomes 7.75, and 7:20 is 440 minutes and becomes 7.33. Rounding is half away from zero, and a machine epsilon is added before rounding so that values sitting exactly on a binary boundary round up rather than down — the classic case where a naive implementation turns 1.005 into 1.00.

Because every intermediate step is an integer, this is the only place rounding occurs. Summing a week of shifts and converting at the end can therefore differ by a cent or two from converting each day and summing the decimals; the first order is the one used here, and the one payroll systems generally expect. The hours to decimal converter exposes the same function in both directions, and the reference table on that page is generated by it for all 60 minute values rather than typed by hand.

Step 5 — The daily and weekly overtime split

This is the part most often got wrong, and the rule that prevents it is non-duplication: no single hour may be counted as overtime twice.

Daily overtime is applied first, day by day. For each day, anything above the daily threshold is daily overtime and the remainder is regular. Those daily overtime minutes are then set aside. The weekly threshold is applied only to the minutes that were not already classified as daily overtime:

dailyOt   = sum over days of max(0, dayMinutes - dailyThreshold)
nonDaily  = totalMinutes - dailyOt
weeklyOt  = max(0, nonDaily - weeklyThreshold)
overtime  = dailyOt + weeklyOt
regular   = totalMinutes - overtime

A worked week makes the difference concrete. Four nine-hour days, one eight-hour day, thresholds of 8 daily and 40 weekly:

44 hours worked, daily 8 h, weekly 40 h
StepHours
Total worked44:00
Daily overtime (4 × 1 h)4:00
Hours not already daily overtime40:00
Weekly overtime (40 is not above 40)0:00
Total overtime4:00
Regular40:00

Adding the thresholds naively would give 4 hours of daily overtime plus another 4 hours weekly, inflating the week to 8 overtime hours that were never worked. Each threshold can be switched off independently on the overtime calculator if only one applies to you. Which thresholds are correct for your job is a legal question, not an arithmetic one, and our guide to overtime rules covers the common frameworks.

Step 6 — Gross pay

Pay is computed from the split, not from the raw total, and each component is rounded to two decimals before being added:

regularPay  = round2(regularMinutes / 60 * rate)
overtimePay = round2(overtimeMinutes / 60 * rate * multiplier)
totalPay    = round2(regularPay + overtimePay)

The multiplier defaults to 1.5 and is editable. On the single-shift calculators there is no split and no multiplier, so pay is just the net hours times the rate. These figures are gross: no tax, no social contributions, no deductions, no shift differentials or premiums are modelled anywhere on this site.

Step 7 — How the CSV export is built

The weekly tools export the table you can see, built from the same result object that rendered it — the file cannot disagree with the screen. The header row is Day, Start, End, Break (min), Hours (h:mm), Hours (decimal), followed by one row per day, with empty days written as zeros rather than skipped so the week keeps seven rows.

After a blank separator line come summary rows for Total, Regular and Overtime, each in both h:mm and two-decimal form, plus a Gross pay row when a rate was supplied. Times follow whichever 12-hour or 24-hour display you have chosen. Fields containing a comma, a double quote or a line break are wrapped in quotes with inner quotes doubled, lines end with CRLF, and a UTF-8 byte-order mark is prepended so that Excel opens the file in the right encoding without an import dialog. The file is generated and downloaded entirely in your browser; it is never uploaded to produce it.

How the arithmetic is verified

Because the calculation functions are pure, they can be tested directly. There are 50 automated unit tests written with Node's built-in node:test runner, and they run on every change to the code. They cover each parser format and the inputs that must be rejected, midnight crossover in both directions, breaks equal to and longer than the shift, the decimal rounding boundaries, the non-duplication rule with each threshold on and off, pay at several multipliers, the time card with blank and partial rows, and the CSV output including the escaping of awkward day labels.

Found a result you believe is wrong? Send the exact inputs, the figure you expected and the figure shown, and it can be reproduced as a test case. The contact page explains what to include, and the about page describes how corrections are handled.

Frequently asked questions

Common questions

Why does 7:20 become 7.33 and not 7.20?

Because decimal hours are a fraction of an hour, not the minute figure moved behind a dot. Twenty minutes is a third of an hour, so 7:20 is 7.333… and rounds to 7.33 at two decimals. Writing it as 7.20 would claim 12 minutes instead of 20 and underpay the shift by 8 minutes.

How does the calculator know my shift crossed midnight?

It subtracts the start from the end in minutes. If the answer is negative, the end must belong to the next day, so 1440 minutes — one full day — are added once. A 10:00 PM to 6:30 AM shift gives −930, which becomes 510 minutes, or 8:30. The result is flagged as overnight so you can see the assumption was applied.

What time formats can I type?

A bare hour (9), digits without a separator (930 or 0930), a colon or a dot (9:30, 9.30), 24-hour time (21:30) and 12-hour time with or without a space before the meridiem (9:30 AM, 9:30pm). Minutes above 59 are rejected, and 24:00 is read as midnight.

Can a total ever be wrong because of floating-point error?

No, because no arithmetic is done in decimals. Times are parsed to whole minutes and every addition, subtraction and comparison happens in integer minutes. A decimal is produced only once, at the final step, rounded to two places with a machine epsilon added so values on a binary boundary round up rather than down.

Why is a break longer than the shift an error instead of a negative total?

A negative number of hours worked is not a real answer to the question, so showing it would hide a typo rather than reveal one. The calculator rejects the entry and names both lengths — the break and the shift — so you can see immediately which of the two numbers is wrong.

How are daily and weekly overtime kept from double-counting?

Daily overtime is worked out first and those minutes are set aside. The weekly threshold is then applied only to the hours that were not already classified as daily overtime. Four nine-hour days plus one eight-hour day gives 4 hours of daily overtime and 40 remaining hours, which is not above a 40-hour weekly threshold, so the week has 4 overtime hours rather than 8.

Does the gross pay figure include tax?

No. Pay is regular hours at your rate plus overtime hours at your rate times the multiplier, each rounded to two decimals before being added. Nothing is deducted: no income tax, no social contributions, no pension, and no shift premiums or differentials are modelled anywhere on this site.

Is anything I type sent to a server?

No. The site is static files with no backend, and every calculation runs in your browser. Your times, breaks and wage rates are never transmitted. The weekly grids and your 12-hour or 24-hour preference are kept in local storage on your own device so a reload does not lose your week.

Why does the CSV open correctly in Excel?

Lines end with CRLF, any field containing a comma, quote or line break is quoted with inner quotes doubled, and a UTF-8 byte-order mark is written at the start of the file. That last detail is what lets Excel detect the encoding without showing an import dialog. The file is built in your browser from the same result that drew the table on screen.

How do I know the formulas are actually correct?

The calculation functions are pure — plain values in, plain values out — so they can be tested on their own. There are 50 automated unit tests using Node's built-in test runner, and they run on every change. They cover each accepted time format, the rejected ones, midnight crossover, break edge cases, rounding boundaries, the overtime non-duplication rule and the CSV escaping.