Timesheet hours total
The timesheet hours total calculator adds worked time across a list of clock-in and clock-out records, subtracts the unpaid break entered for each shift, and returns a clear net total.
Run — free
Every ISO datetime must include either Z or an explicit offset, so records from different offices, daylight-saving periods, or overnight schedules are compared on one UTC timeline. The result also shows gross hours, total break minutes, and the number of shifts included, making the calculation easy to review or automate.
Enter complete, unambiguous shift records
Provide one row for every continuous work period. Each row needs a clock_in datetime and a clock_out datetime written in ISO 8601 form, including seconds and either Z or a numeric UTC offset such as -04:00. The offset is essential because a local wall-clock time alone does not identify one instant during timezone changes. Add break_minutes when a shift contains an unpaid break; omit it when no break should be deducted. Decimal break minutes are accepted, which is useful when a source system records fractions of a minute. A clock-out may fall on the next calendar day, so overnight work needs no special mode. The calculator rejects impossible dates, missing offsets, clock-outs that are not later than clock-ins, negative breaks, and breaks longer than their shift. It also rejects overlapping shifts after UTC normalization, because counting both would silently inflate the total. Keeping every row explicit makes the input auditable and avoids assumptions about lunch policies, rounding rules, or which gaps between shifts should count as breaks.
Understand the UTC calculation
Each timestamp is parsed as a Gregorian calendar value and converted to a UTC millisecond position using its written offset. The calculation does not consult the machine timezone, a timezone database, the current date, or any external service. For each valid row, gross duration equals the UTC clock-out instant minus the UTC clock-in instant. The entered break duration is converted from minutes to milliseconds and subtracted from that row's gross duration. Gross durations and break durations are then summed independently before the final net total is produced. This order gives stable results for teams whose entries use different offsets and for a shift that crosses midnight. Output includes total_hours for the net worked value, total_minutes for systems that prefer minutes, gross_hours before deductions, break_minutes as the combined deduction, and shift_count for reconciliation. Hour and minute values are rounded only at the output boundary to six decimal places; the internal accumulation remains in integer milliseconds. The same inputs therefore produce the same JSON regardless of browser locale or server location.
Use the result in payroll and reporting workflows
The returned fields separate source activity from the final payable duration, which helps a reviewer reproduce the arithmetic. Compare shift_count with the number of imported rows, gross_hours with the raw clock span, and break_minutes with the expected unpaid deductions before accepting total_hours. The capability deliberately does not apply overtime bands, paid-break rules, wage rates, schedule tolerances, or employer-specific rounding. Those policies vary by agreement and jurisdiction, so they should be applied in a later, clearly documented step. It also does not infer breaks from gaps between separate rows: only the break_minutes values you provide are deducted. This makes the function suitable as a deterministic building block for payroll preparation, contractor invoices, project time reports, and attendance reconciliation without embedding hidden policy. It can run free in the browser for an individual check, while API automation costs $0.002 per request. For repeatable imports, preserve the original ISO strings and offsets alongside the returned summary so any disputed total can be traced back to the exact instants and deductions used.
What you can do with it
Prepare a payroll total
Combine approved shift records and unpaid breaks into a net duration before applying rates or overtime rules.
Check a contractor invoice
Recalculate billed working time from timestamp evidence without depending on the reviewer's local timezone.
Reconcile attendance exports
Compare gross time, deductions, and shift count with records exported from a clock system.
FAQ
What does a request cost?
API automation costs $0.002 per request, and the calculation can also run free in the browser.
Which datetime format is required?
Use ISO 8601 with a date, time through seconds, and either Z or an explicit offset, for example 2026-07-20T09:00:00-04:00.
How are breaks supplied?
Set break_minutes on each shift to its unpaid break duration. If it is omitted, the deduction for that shift is zero.
Does it calculate overtime or wages?
No. It returns durations only; overtime thresholds, rounding policy, pay rates, and local labor rules belong in a separate step.
Why are overlapping shifts rejected?
Overlapping intervals would count the same time twice. Rejecting them exposes a timesheet conflict instead of returning an inflated total.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/date/timesheet-total \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"shifts":[{"clock_in":"2026-07-20T09:00:00-04:00","clock_out":"2026-07-20T17:30:00-04:00","break_minutes":30},{"clock_in":"2026-07-21T09:15:00-04:00","clock_out":"2026-07-21T17:00:00-04:00","break_minutes":45}]}'const res = await fetch("https://api.kit.forhosting.com/date/timesheet-total", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"shifts": [
{
"clock_in": "2026-07-20T09:00:00-04:00",
"clock_out": "2026-07-20T17:30:00-04:00",
"break_minutes": 30
},
{
"clock_in": "2026-07-21T09:15:00-04:00",
"clock_out": "2026-07-21T17:00:00-04:00",
"break_minutes": 45
}
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/date/timesheet-total",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"shifts": [
{
"clock_in": "2026-07-20T09:00:00-04:00",
"clock_out": "2026-07-20T17:30:00-04:00",
"break_minutes": 30
},
{
"clock_in": "2026-07-21T09:15:00-04:00",
"clock_out": "2026-07-21T17:00:00-04:00",
"break_minutes": 45
}
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/date/timesheet-total", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"shifts":[{"clock_in":"2026-07-20T09:00:00-04:00","clock_out":"2026-07-20T17:30:00-04:00","break_minutes":30},{"clock_in":"2026-07-21T09:15:00-04:00","clock_out":"2026-07-21T17:00:00-04:00","break_minutes":45}]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"shifts":[{"clock_in":"2026-07-20T09:00:00-04:00","clock_out":"2026-07-20T17:30:00-04:00","break_minutes":30},{"clock_in":"2026-07-21T09:15:00-04:00","clock_out":"2026-07-21T17:00:00-04:00","break_minutes":45}]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/date/timesheet-total", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"shifts": [
{
"clock_in": "2026-07-20T09:00:00-04:00",
"clock_out": "2026-07-20T17:30:00-04:00",
"break_minutes": 30
},
{
"clock_in": "2026-07-21T09:15:00-04:00",
"clock_out": "2026-07-21T17:00:00-04:00",
"break_minutes": 45
}
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "date.timesheet_total",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_items | 10000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |