Timesheet hours total
Add up worked hours from clock-in and clock-out times minus unpaid breaks: two shifts of 09:00 to 17:30 and 09:15 to 17:00, with 30 and 45 minute breaks, give 15 net hours. Overnight shifts and time zones are handled. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
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. 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. The offset is essential because a local wall-clock time alone does not identify one instant during timezone changes. 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.
Hour and minute values are rounded only at the output boundary to six decimal places; the internal accumulation remains in integer milliseconds.
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. 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.
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
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?
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.
Can a shift run past midnight?
Yes. The clock-out time may fall on the next calendar day, so night shifts need no special setting. Each time is placed on one UTC timeline using its own offset before the hours are counted.
Related tools
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. |
Technical notes
Enter complete, unambiguous shift records
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. Add break_minutes when a shift contains an unpaid break; omit it when no break should be deducted.
Understand the UTC calculation
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. The same inputs therefore produce the same JSON regardless of browser locale or server location.
Use the result in payroll and reporting workflows
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. 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.
Technical questions
What does a request cost?
API automation costs $0.002 per request, and the calculation can also run free in the browser.