⏱️ Bash Epoch Time & Duration Calculations

Comprehensive Reference Guide — Unix epoch, time differences, session tracking, and precise duration calculations

📋 Table of Contents

Overview

Unix epoch time is the number of seconds elapsed since January 1, 1970 00:00:00 UTC. It's the universal way to represent time in computing—perfect for calculating durations, comparing timestamps, and storing time data.

Why Use Epoch Time?

Simple Math: Duration = End Time - Start Time. No date parsing, no timezone headaches.
Universal: Works across systems, languages, and timezones.
Precise: Can include milliseconds or nanoseconds for high-precision timing.

Common Use Cases

Session duration tracking, performance benchmarking, log analysis, billing calculations, SLA monitoring, cache expiration, and any scenario where you need to measure elapsed time.

Epoch Time Basics

Quick Reference

date +%s — Current epoch (seconds)
date +%s%N — Epoch with nanoseconds
date +%s%3N — Epoch with milliseconds
date -d @EPOCH — Convert epoch to human readable
date -d "STRING" +%s — Convert string to epoch

Time Unit Conversions

UnitSecondsCalculation
1 Minute6060
1 Hour3,60060 × 60
1 Day86,40060 × 60 × 24
1 Week604,80086,400 × 7
1 Year (avg)31,536,00086,400 × 365

Epoch Timeline

0 — January 1, 1970 00:00:00 UTC (Unix Epoch)
1,000,000,000 — September 9, 2001 01:46:40 UTC
1,500,000,000 — July 14, 2017 02:40:00 UTC
1,700,000,000 — November 14, 2023 22:13:20 UTC
2,000,000,000 — May 18, 2033 03:33:20 UTC
⚠️ Year 2038 Problem
32-bit systems store epoch as a signed integer, maxing out at 2,147,483,647 (January 19, 2038). Modern 64-bit systems don't have this limitation.

Epoch Time Examples

1
Basic Epoch Conversions

Getting current epoch time and converting between epoch and human-readable formats.

#!/bin/bash
# Basic epoch time operations

echo "=== Current Time as Epoch ==="
epoch=$(date +%s)
echo "Epoch seconds: $epoch"
echo "With milliseconds: $(date +%s%3N)"
echo "With microseconds: $(date +%s%6N)"
echo "With nanoseconds: $(date +%s%N)"

echo ""
echo "=== Epoch to Human Readable ==="
echo "Current epoch $epoch ="
echo "  Local: $(date -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "  UTC:   $(date -u -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "  Full:  $(date -d @$epoch)"

echo ""
echo "=== Human Readable to Epoch ==="
# Various input formats
echo "'2025-11-18 14:30:00' = $(date -d '2025-11-18 14:30:00' +%s)"
echo "'Nov 18, 2025 2:30pm' = $(date -d 'Nov 18, 2025 2:30pm' +%s)"
echo "'now' = $(date -d 'now' +%s)"
echo "'yesterday' = $(date -d 'yesterday' +%s)"
echo "'tomorrow noon' = $(date -d 'tomorrow noon' +%s)"

echo ""
echo "=== Specific Epochs ==="
echo "Epoch 0 = $(date -d @0 '+%Y-%m-%d %H:%M:%S %Z')"
echo "Epoch 1000000000 = $(date -d @1000000000 '+%Y-%m-%d %H:%M:%S %Z')"
echo "Epoch 2000000000 = $(date -d @2000000000 '+%Y-%m-%d %H:%M:%S %Z')"

echo ""
echo "=== Current Time Details ==="
echo "Epoch: $epoch"
echo "ISO 8601: $(date -d @$epoch '+%Y-%m-%dT%H:%M:%S%z')"
echo "RFC 2822: $(date -d @$epoch -R)"
Output: === Current Time as Epoch === Epoch seconds: 1731945045 With milliseconds: 1731945045123 With microseconds: 1731945045123456 With nanoseconds: 1731945045123456789 === Epoch to Human Readable === Current epoch 1731945045 = Local: 2025-11-18 14:30:45 EST UTC: 2025-11-18 19:30:45 UTC Full: Tue Nov 18 14:30:45 EST 2025 === Human Readable to Epoch === '2025-11-18 14:30:00' = 1731945000 'Nov 18, 2025 2:30pm' = 1731945000 'now' = 1731945045 'yesterday' = 1731858645 'tomorrow noon' = 1732024800 === Specific Epochs === Epoch 0 = 1970-01-01 00:00:00 UTC Epoch 1000000000 = 2001-09-09 01:46:40 UTC Epoch 2000000000 = 2033-05-18 03:33:20 UTC
2
Simple Duration Calculation

The fundamental pattern: capture start time, do work, capture end time, calculate difference.

#!/bin/bash
# Simple duration calculation

echo "=== Basic Duration Pattern ==="

# Capture start time
start_epoch=$(date +%s)
echo "Started at: $(date -d @$start_epoch '+%H:%M:%S')"

# Simulate work
echo "Working..."
sleep 3

# Capture end time
end_epoch=$(date +%s)
echo "Ended at: $(date -d @$end_epoch '+%H:%M:%S')"

# Calculate duration
duration=$((end_epoch - start_epoch))
echo "Duration: $duration seconds"

echo ""
echo "=== Duration with Milliseconds ==="

start_ms=$(date +%s%3N)
sleep 1.5
end_ms=$(date +%s%3N)

duration_ms=$((end_ms - start_ms))
echo "Duration: $duration_ms milliseconds"
echo "Duration: $(echo "scale=3; $duration_ms / 1000" | bc) seconds"

echo ""
echo "=== Duration Between Two Dates ==="

# Calculate time between two specific dates
date1="2025-11-18 09:00:00"
date2="2025-11-18 17:30:00"

epoch1=$(date -d "$date1" +%s)
epoch2=$(date -d "$date2" +%s)

diff=$((epoch2 - epoch1))

echo "From: $date1"
echo "To:   $date2"
echo ""
echo "Duration: $diff seconds"
echo "         = $((diff / 60)) minutes"
echo "         = $(echo "scale=2; $diff / 3600" | bc) hours"
Output: === Basic Duration Pattern === Started at: 14:30:45 Working... Ended at: 14:30:48 Duration: 3 seconds === Duration with Milliseconds === Duration: 1502 milliseconds Duration: 1.502 seconds === Duration Between Two Dates === From: 2025-11-18 09:00:00 To: 2025-11-18 17:30:00 Duration: 30600 seconds = 510 minutes = 8.50 hours
3
Converting Duration to Human Readable

Converting raw seconds into days, hours, minutes, and seconds format.

#!/bin/bash
# Convert seconds to human readable duration

# Function: seconds to readable format
seconds_to_human() {
    local total_secs=$1
    local days=$((total_secs / 86400))
    local hours=$(( (total_secs % 86400) / 3600 ))
    local mins=$(( (total_secs % 3600) / 60 ))
    local secs=$((total_secs % 60))
    
    if ((days > 0)); then
        printf "%dd %02dh %02dm %02ds" $days $hours $mins $secs
    elif ((hours > 0)); then
        printf "%dh %02dm %02ds" $hours $mins $secs
    elif ((mins > 0)); then
        printf "%dm %02ds" $mins $secs
    else
        printf "%ds" $secs
    fi
}

# Function: seconds to HH:MM:SS
seconds_to_hms() {
    local total_secs=$1
    local hours=$((total_secs / 3600))
    local mins=$(( (total_secs % 3600) / 60 ))
    local secs=$((total_secs % 60))
    printf "%02d:%02d:%02d" $hours $mins $secs
}

# Function: seconds to verbose
seconds_to_verbose() {
    local total_secs=$1
    local days=$((total_secs / 86400))
    local hours=$(( (total_secs % 86400) / 3600 ))
    local mins=$(( (total_secs % 3600) / 60 ))
    local secs=$((total_secs % 60))
    
    local result=""
    ((days > 0)) && result+="$days day(s) "
    ((hours > 0)) && result+="$hours hour(s) "
    ((mins > 0)) && result+="$mins minute(s) "
    ((secs > 0)) && result+="$secs second(s)"
    
    echo "${result:-0 seconds}"
}

echo "=== Test Various Durations ==="
test_values=(45 125 3661 7384 90061 186400)

printf "%-12s %-20s %-15s %s\n" "Seconds" "Human" "HH:MM:SS" "Verbose"
printf "%-12s %-20s %-15s %s\n" "-------" "-----" "--------" "-------"

for secs in "${test_values[@]}"; do
    human=$(seconds_to_human $secs)
    hms=$(seconds_to_hms $secs)
    verbose=$(seconds_to_verbose $secs)
    printf "%-12d %-20s %-15s %s\n" "$secs" "$human" "$hms" "$verbose"
done

echo ""
echo "=== Practical Example ==="
# Time someone was on a webpage
arrival="2025-11-18 10:23:17"
departure="2025-11-18 10:31:42"

start=$(date -d "$arrival" +%s)
end=$(date -d "$departure" +%s)
duration=$((end - start))

echo "Page session:"
echo "  Arrived:  $arrival"
echo "  Left:     $departure"
echo "  Duration: $(seconds_to_human $duration) ($(seconds_to_hms $duration))"
Output: === Test Various Durations === Seconds Human HH:MM:SS Verbose ------- ----- -------- ------- 45 45s 00:00:45 45 second(s) 125 2m 05s 00:02:05 2 minute(s) 5 second(s) 3661 1h 01m 01s 01:01:01 1 hour(s) 1 minute(s) 1 second(s) 7384 2h 03m 04s 02:03:04 2 hour(s) 3 minute(s) 4 second(s) 90061 1d 01h 01m 01s 25:01:01 1 day(s) 1 hour(s) 1 minute(s) 1 second(s) 186400 2d 03h 46m 40s 51:46:40 2 day(s) 3 hour(s) 46 minute(s) 40 second(s) === Practical Example === Page session: Arrived: 2025-11-18 10:23:17 Left: 2025-11-18 10:31:42 Duration: 8m 25s (00:08:25)
4
Web Session Tracking (Your Use Case)

Complete implementation for tracking how long someone spends on a page—exactly like your original scenario.

#!/bin/bash
# Web session duration tracking

# Simulate session data (in real life, from logs or database)
# Format: session_id|page|arrival_time|departure_time

sessions="
S001|/home|2025-11-18 09:15:23|2025-11-18 09:18:47
S002|/products|2025-11-18 09:20:01|2025-11-18 09:35:22
S003|/checkout|2025-11-18 09:36:05|2025-11-18 09:42:18
S004|/home|2025-11-18 10:01:00|2025-11-18 10:02:30
S005|/products|2025-11-18 10:15:45|2025-11-18 11:02:33
"

# Duration formatter
format_duration() {
    local secs=$1
    local mins=$((secs / 60))
    local s=$((secs % 60))
    printf "%d:%02d" $mins $s
}

echo "=== Session Duration Report ==="
echo ""
printf "%-8s %-12s %-10s %-10s %s\n" "Session" "Page" "Duration" "Seconds" "Status"
printf "%-8s %-12s %-10s %-10s %s\n" "-------" "----" "--------" "-------" "------"

total_time=0
session_count=0

while IFS='|' read -r sid page arrival departure; do
    [[ -z "$sid" ]] && continue
    
    # Convert to epoch
    start_epoch=$(date -d "$arrival" +%s)
    end_epoch=$(date -d "$departure" +%s)
    
    # Calculate duration
    duration=$((end_epoch - start_epoch))
    ((total_time += duration))
    ((session_count++))
    
    # Determine status based on duration
    if ((duration < 60)); then
        status="Bounce"
    elif ((duration < 300)); then
        status="Short"
    elif ((duration < 1800)); then
        status="Normal"
    else
        status="Extended"
    fi
    
    # Format duration as M:SS
    formatted=$(format_duration $duration)
    
    printf "%-8s %-12s %-10s %-10d %s\n" "$sid" "$page" "$formatted" "$duration" "$status"
    
done <<< "$sessions"

echo ""
echo "=== Summary ==="
avg=$((total_time / session_count))
echo "Total sessions: $session_count"
echo "Total time: $(format_duration $total_time) ($total_time seconds)"
echo "Average session: $(format_duration $avg) ($avg seconds)"

echo ""
echo "=== Single Session Detail ==="
# Detailed breakdown for one session
arrival="2025-11-18 14:23:17"
departure="2025-11-18 14:31:42"

arr_epoch=$(date -d "$arrival" +%s)
dep_epoch=$(date -d "$departure" +%s)
dur_secs=$((dep_epoch - arr_epoch))

echo "Arrival:   $arrival"
echo "           Epoch: $arr_epoch"
echo ""
echo "Departure: $departure"
echo "           Epoch: $dep_epoch"
echo ""
echo "Calculation: $dep_epoch - $arr_epoch = $dur_secs seconds"
echo ""
echo "Duration breakdown:"
echo "  Total seconds: $dur_secs"
echo "  Minutes: $((dur_secs / 60))"
echo "  Remaining seconds: $((dur_secs % 60))"
echo "  Formatted: $(format_duration $dur_secs)"
Output: === Session Duration Report === Session Page Duration Seconds Status ------- ---- -------- ------- ------ S001 /home 3:24 204 Normal S002 /products 15:21 921 Normal S003 /checkout 6:13 373 Normal S004 /home 1:30 90 Short S005 /products 46:48 2808 Extended === Summary === Total sessions: 5 Total time: 73:16 (4396 seconds) Average session: 14:39 (879 seconds) === Single Session Detail === Arrival: 2025-11-18 14:23:17 Epoch: 1731944597 Departure: 2025-11-18 14:31:42 Epoch: 1731945102 Calculation: 1731945102 - 1731944597 = 505 seconds Duration breakdown: Total seconds: 505 Minutes: 8 Remaining seconds: 25 Formatted: 8:25
💡 The Core Pattern
1. Get arrival time → convert to epoch
2. Get departure time → convert to epoch
3. Subtract: duration = departure - arrival
4. Convert seconds to minutes:seconds for display
5
High-Precision Timing with Nanoseconds

Millisecond and nanosecond precision for benchmarking and performance measurement.

#!/bin/bash
# High-precision timing

echo "=== Nanosecond Precision ==="

# Capture with nanoseconds
start_ns=$(date +%s%N)

# Simulate quick operation
for i in {1..10000}; do
    echo "$i" > /dev/null
done

end_ns=$(date +%s%N)

# Calculate difference
diff_ns=$((end_ns - start_ns))
diff_ms=$((diff_ns / 1000000))
diff_us=$((diff_ns / 1000))
diff_s=$(echo "scale=6; $diff_ns / 1000000000" | bc)

echo "Start:    $start_ns ns"
echo "End:      $end_ns ns"
echo ""
echo "Duration: $diff_ns nanoseconds"
echo "          $diff_us microseconds"
echo "          $diff_ms milliseconds"
echo "          $diff_s seconds"

echo ""
echo "=== Benchmark Function ==="

benchmark() {
    local label="$1"
    shift
    local start=$(date +%s%N)
    
    "$@"
    
    local end=$(date +%s%N)
    local elapsed_ms=$(( (end - start) / 1000000 ))
    printf "%-30s %6d ms\n" "$label:" "$elapsed_ms"
}

# Test various operations
benchmark "Sleep 100ms" sleep 0.1
benchmark "Loop 10000" bash -c 'for i in {1..10000}; do :; done'
benchmark "Echo to /dev/null" bash -c 'for i in {1..1000}; do echo test > /dev/null; done'

echo ""
echo "=== Multiple Run Statistics ==="

runs=5
times=()

for ((i=1; i<=runs; i++)); do
    start=$(date +%s%N)
    
    # Operation to benchmark
    for j in {1..5000}; do
        echo "$j" > /dev/null
    done
    
    end=$(date +%s%N)
    elapsed=$(( (end - start) / 1000 ))  # microseconds
    times+=($elapsed)
    echo "Run $i: $elapsed µs"
done

# Calculate stats
total=0
min=${times[0]}
max=${times[0]}

for t in "${times[@]}"; do
    ((total += t))
    ((t < min)) && min=$t
    ((t > max)) && max=$t
done

avg=$((total / runs))

echo ""
echo "Statistics:"
echo "  Min: $min µs"
echo "  Max: $max µs"
echo "  Avg: $avg µs"
Output: === Nanosecond Precision === Start: 1731945045123456789 ns End: 1731945045145678912 ns Duration: 22222123 nanoseconds 22222 microseconds 22 milliseconds 0.022222 seconds === Benchmark Function === Sleep 100ms: 100 ms Loop 10000: 15 ms Echo to /dev/null: 42 ms === Multiple Run Statistics === Run 1: 8234 µs Run 2: 7891 µs Run 3: 8102 µs Run 4: 7956 µs Run 5: 8045 µs Statistics: Min: 7891 µs Max: 8234 µs Avg: 8045 µs
6
Log File Time Analysis

Parsing log timestamps and calculating durations between events.

#!/bin/bash
# Log file time analysis

# Simulated log entries
log_data="2025-11-18 09:15:23 START Job_A
2025-11-18 09:15:45 START Job_B
2025-11-18 09:18:12 END Job_A
2025-11-18 09:22:31 END Job_B
2025-11-18 09:30:00 START Job_C
2025-11-18 09:45:17 END Job_C
2025-11-18 10:00:00 START Job_D
2025-11-18 10:02:30 END Job_D"

echo "=== Job Duration Analysis ==="
echo ""

declare -A start_times

# Process log entries
while read -r date time action job; do
    timestamp="$date $time"
    epoch=$(date -d "$timestamp" +%s)
    
    if [[ "$action" == "START" ]]; then
        start_times[$job]=$epoch
    elif [[ "$action" == "END" ]]; then
        if [[ -n "${start_times[$job]}" ]]; then
            duration=$((epoch - start_times[$job]))
            mins=$((duration / 60))
            secs=$((duration % 60))
            printf "%-8s Duration: %2d:%02d (%d seconds)\n" "$job" $mins $secs $duration
        fi
    fi
done <<< "$log_data"

echo ""
echo "=== Time Between Log Events ==="

# Calculate time gaps between consecutive entries
prev_epoch=""
prev_entry=""

while read -r date time rest; do
    timestamp="$date $time"
    epoch=$(date -d "$timestamp" +%s)
    
    if [[ -n "$prev_epoch" ]]; then
        gap=$((epoch - prev_epoch))
        if ((gap > 60)); then
            echo "Gap of ${gap}s between:"
            echo "  $prev_entry"
            echo "  $date $time $rest"
            echo ""
        fi
    fi
    
    prev_epoch=$epoch
    prev_entry="$date $time $rest"
done <<< "$log_data"

echo "=== Apache Log Format Parsing ==="

# Apache common log format timestamp
apache_entry='192.168.1.100 - - [18/Nov/2025:14:30:45 -0500] "GET /page HTTP/1.1" 200 1234'

# Extract and parse timestamp
timestamp=$(echo "$apache_entry" | grep -oP '\[\K[^\]]+')
# Convert Apache format to parseable format
parsed_time=$(echo "$timestamp" | sed 's/:/ /' | sed 's/\// /g')
epoch=$(date -d "$parsed_time" +%s 2>/dev/null)

echo "Apache log: $apache_entry"
echo "Timestamp:  $timestamp"
echo "Epoch:      $epoch"
Output: === Job Duration Analysis === Job_A Duration: 2:49 (169 seconds) Job_B Duration: 6:46 (406 seconds) Job_C Duration: 15:17 (917 seconds) Job_D Duration: 2:30 (150 seconds) === Time Between Log Events === Gap of 448s between: 2025-11-18 09:22:31 END Job_B 2025-11-18 09:30:00 START Job_C Gap of 883s between: 2025-11-18 09:45:17 END Job_C 2025-11-18 10:00:00 START Job_D === Apache Log Format Parsing === Apache log: 192.168.1.100 - - [18/Nov/2025:14:30:45 -0500] "GET /page HTTP/1.1" 200 1234 Timestamp: 18/Nov/2025:14:30:45 -0500 Epoch: 1731945045
7
SLA and Timeout Monitoring

Calculating if operations meet SLA requirements and implementing timeout logic.

#!/bin/bash
# SLA and timeout monitoring

echo "=== SLA Compliance Check ==="

# SLA thresholds (in seconds)
SLA_WARNING=30
SLA_CRITICAL=60
SLA_BREACH=120

# Simulated response times
responses=(15 28 45 62 95 180 22 31)

passed=0
warning=0
critical=0
breach=0

for response in "${responses[@]}"; do
    if ((response <= SLA_WARNING)); then
        status="✓ PASS"
        ((passed++))
    elif ((response <= SLA_CRITICAL)); then
        status="⚠ WARNING"
        ((warning++))
    elif ((response <= SLA_BREACH)); then
        status="✗ CRITICAL"
        ((critical++))
    else
        status="✗✗ BREACH"
        ((breach++))
    fi
    printf "Response: %3ds - %s\n" $response "$status"
done

echo ""
echo "Summary:"
echo "  Passed:   $passed"
echo "  Warning:  $warning"
echo "  Critical: $critical"
echo "  Breach:   $breach"

compliance=$(echo "scale=1; ($passed * 100) / ${#responses[@]}" | bc)
echo "  SLA Compliance: ${compliance}%"

echo ""
echo "=== Timeout Implementation ==="

run_with_timeout() {
    local timeout=$1
    local cmd="$2"
    local start=$(date +%s)
    
    # Run command in background
    eval "$cmd" &
    local pid=$!
    
    # Check periodically
    while kill -0 $pid 2>/dev/null; do
        local elapsed=$(($(date +%s) - start))
        if ((elapsed >= timeout)); then
            kill $pid 2>/dev/null
            echo "TIMEOUT after ${timeout}s"
            return 1
        fi
        sleep 0.1
    done
    
    wait $pid
    local exit_code=$?
    local elapsed=$(($(date +%s) - start))
    echo "Completed in ${elapsed}s (exit: $exit_code)"
    return $exit_code
}

echo "Running with 2s timeout:"
run_with_timeout 2 "sleep 1"

echo ""
echo "Running with 2s timeout (will timeout):"
run_with_timeout 2 "sleep 5"

echo ""
echo "=== Deadline Calculation ==="

# Calculate if we'll meet a deadline
deadline="2025-11-18 17:00:00"
deadline_epoch=$(date -d "$deadline" +%s)
now_epoch=$(date +%s)

remaining=$((deadline_epoch - now_epoch))

if ((remaining > 0)); then
    hours=$((remaining / 3600))
    mins=$(( (remaining % 3600) / 60 ))
    echo "Deadline: $deadline"
    echo "Time remaining: ${hours}h ${mins}m"
    
    if ((remaining < 3600)); then
        echo "⚠ WARNING: Less than 1 hour remaining!"
    fi
else
    overdue=$((-remaining))
    echo "✗ DEADLINE MISSED by $((overdue / 60)) minutes"
fi
Output: === SLA Compliance Check === Response: 15s - ✓ PASS Response: 28s - ✓ PASS Response: 45s - ⚠ WARNING Response: 62s - ✗ CRITICAL Response: 95s - ✗ CRITICAL Response: 180s - ✗✗ BREACH Response: 22s - ✓ PASS Response: 31s - ⚠ WARNING Summary: Passed: 3 Warning: 2 Critical: 2 Breach: 1 SLA Compliance: 37.5% === Timeout Implementation === Running with 2s timeout: Completed in 1s (exit: 0) Running with 2s timeout (will timeout): TIMEOUT after 2s === Deadline Calculation === Deadline: 2025-11-18 17:00:00 Time remaining: 2h 29m
8
Billing and Time-Based Calculations

Calculating charges based on time usage—perfect for cloud resources, consulting, parking, etc.

#!/bin/bash
# Time-based billing calculations

echo "=== Hourly Billing ==="

# Usage records: start|end|resource
usage="
2025-11-18 08:15:00|2025-11-18 12:30:00|VM-Large
2025-11-18 09:00:00|2025-11-18 11:45:00|VM-Small
2025-11-18 10:30:00|2025-11-18 18:00:00|VM-Large
2025-11-18 14:00:00|2025-11-18 14:45:00|VM-Small
"

# Rates per hour
declare -A rates
rates["VM-Large"]=0.50
rates["VM-Small"]=0.15

total_cost=0

printf "%-12s %-8s %-10s %s\n" "Resource" "Hours" "Rate" "Cost"
printf "%-12s %-8s %-10s %s\n" "--------" "-----" "----" "----"

while IFS='|' read -r start end resource; do
    [[ -z "$start" ]] && continue
    
    start_epoch=$(date -d "$start" +%s)
    end_epoch=$(date -d "$end" +%s)
    
    duration_secs=$((end_epoch - start_epoch))
    duration_hours=$(echo "scale=2; $duration_secs / 3600" | bc)
    
    rate=${rates[$resource]}
    cost=$(echo "scale=2; $duration_hours * $rate" | bc)
    total_cost=$(echo "scale=2; $total_cost + $cost" | bc)
    
    printf "%-12s %-8s \$%-9s \$%s\n" "$resource" "$duration_hours" "$rate/hr" "$cost"
    
done <<< "$usage"

echo ""
printf "%-12s %-8s %-10s \$%s\n" "TOTAL" "" "" "$total_cost"

echo ""
echo "=== Parking Meter Calculation ==="

# $2 per hour, minimum 30 minutes, maximum 4 hours
arrival="2025-11-18 10:23:00"
departure="2025-11-18 13:47:00"

start=$(date -d "$arrival" +%s)
end=$(date -d "$departure" +%s)
duration=$((end - start))

# Round up to nearest 30 minutes
half_hours=$(( (duration + 1799) / 1800 ))
hours=$(echo "scale=1; $half_hours / 2" | bc)

# Calculate charge ($2/hour)
charge=$(echo "scale=2; $hours * 2" | bc)

echo "Arrived:  $arrival"
echo "Left:     $departure"
echo "Duration: $((duration / 60)) minutes"
echo "Billable: $hours hours"
echo "Charge:   \$$charge"

echo ""
echo "=== Consulting Time Tracker ==="

# Track billable time with different rates
declare -A client_time
declare -A client_rate

client_rate["ClientA"]=150
client_rate["ClientB"]=125
client_rate["ClientC"]=175

# Simulated time entries (minutes)
client_time["ClientA"]=245
client_time["ClientB"]=180
client_time["ClientC"]=90

echo "Billable Hours Report"
echo ""
printf "%-10s %-8s %-10s %s\n" "Client" "Hours" "Rate" "Amount"
printf "%-10s %-8s %-10s %s\n" "------" "-----" "----" "------"

grand_total=0

for client in "${!client_time[@]}"; do
    mins=${client_time[$client]}
    hours=$(echo "scale=2; $mins / 60" | bc)
    rate=${client_rate[$client]}
    amount=$(echo "scale=2; $hours * $rate" | bc)
    grand_total=$(echo "scale=2; $grand_total + $amount" | bc)
    
    printf "%-10s %-8s \$%-9s \$%s\n" "$client" "$hours" "$rate/hr" "$amount"
done

echo ""
printf "%-10s %-8s %-10s \$%s\n" "TOTAL" "" "" "$grand_total"
Output: === Hourly Billing === Resource Hours Rate Cost -------- ----- ---- ---- VM-Large 4.25 $0.50/hr $2.12 VM-Small 2.75 $0.15/hr $0.41 VM-Large 7.50 $0.50/hr $3.75 VM-Small 0.75 $0.15/hr $0.11 TOTAL $6.39 === Parking Meter Calculation === Arrived: 2025-11-18 10:23:00 Left: 2025-11-18 13:47:00 Duration: 204 minutes Billable: 3.5 hours Charge: $7.00 === Consulting Time Tracker === Billable Hours Report Client Hours Rate Amount ------ ----- ---- ------ ClientA 4.08 $150/hr $612.00 ClientB 3.00 $125/hr $375.00 ClientC 1.50 $175/hr $262.50 TOTAL $1249.50
9
Epoch Time with Different Timezones

Handling epoch conversions across timezones—critical for distributed systems and global operations.

#!/bin/bash
# Epoch time and timezone handling

echo "=== Epoch is Timezone-Independent ==="

# Same epoch = same moment in time
epoch=1731945045

echo "Epoch: $epoch"
echo ""
echo "This moment in different timezones:"
echo "  UTC:      $(TZ=UTC date -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "  Eastern:  $(TZ=America/New_York date -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "  Pacific:  $(TZ=America/Los_Angeles date -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "  London:   $(TZ=Europe/London date -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "  Tokyo:    $(TZ=Asia/Tokyo date -d @$epoch '+%Y-%m-%d %H:%M:%S %Z')"

echo ""
echo "=== Convert Local Time to Epoch ==="

# When converting TO epoch, timezone matters
local_time="2025-11-18 14:00:00"

echo "Time string: '$local_time'"
echo ""
echo "Interpreted in different zones:"

for tz in "America/New_York" "America/Los_Angeles" "UTC" "Asia/Tokyo"; do
    epoch=$(TZ=$tz date -d "$local_time" +%s)
    echo "  $tz: $epoch"
done

echo ""
echo "=== Calculate Duration Across Timezones ==="

# Meeting: 2pm Eastern to 4pm Eastern
# What's the duration for someone in Tokyo?

meeting_start="2025-11-18 14:00:00"
meeting_end="2025-11-18 16:00:00"

# Convert to epoch (in Eastern time)
start_epoch=$(TZ=America/New_York date -d "$meeting_start" +%s)
end_epoch=$(TZ=America/New_York date -d "$meeting_end" +%s)

duration=$((end_epoch - start_epoch))

echo "Meeting (Eastern): $meeting_start to $meeting_end"
echo "Duration: $((duration / 60)) minutes"
echo ""
echo "In Tokyo time:"
echo "  Start: $(TZ=Asia/Tokyo date -d @$start_epoch '+%Y-%m-%d %H:%M %Z')"
echo "  End:   $(TZ=Asia/Tokyo date -d @$end_epoch '+%Y-%m-%d %H:%M %Z')"
echo "  (Same duration: $((duration / 60)) minutes)"

echo ""
echo "=== Global Event Countdown ==="

# Event at specific UTC time
event_utc="2025-12-01 00:00:00 UTC"
event_epoch=$(date -d "$event_utc" +%s)
now_epoch=$(date +%s)
remaining=$((event_epoch - now_epoch))

days=$((remaining / 86400))
hours=$(( (remaining % 86400) / 3600 ))

echo "Event: $event_utc"
echo "Countdown: ${days}d ${hours}h"
echo ""
echo "Event time in various zones:"
for tz in "America/New_York" "America/Los_Angeles" "Europe/London" "Asia/Tokyo"; do
    local_time=$(TZ=$tz date -d @$event_epoch '+%Y-%m-%d %H:%M %Z')
    city=$(echo $tz | cut -d'/' -f2)
    printf "  %-15s %s\n" "$city:" "$local_time"
done
Output: === Epoch is Timezone-Independent === Epoch: 1731945045 This moment in different timezones: UTC: 2025-11-18 19:30:45 UTC Eastern: 2025-11-18 14:30:45 EST Pacific: 2025-11-18 11:30:45 PST London: 2025-11-18 19:30:45 GMT Tokyo: 2025-11-19 04:30:45 JST === Convert Local Time to Epoch === Time string: '2025-11-18 14:00:00' Interpreted in different zones: America/New_York: 1731952800 America/Los_Angeles: 1731963600 UTC: 1731938400 Asia/Tokyo: 1731906000 === Calculate Duration Across Timezones === Meeting (Eastern): 2025-11-18 14:00:00 to 2025-11-18 16:00:00 Duration: 120 minutes In Tokyo time: Start: 2025-11-19 04:00 JST End: 2025-11-19 06:00 JST (Same duration: 120 minutes) === Global Event Countdown === Event: 2025-12-01 00:00:00 UTC Countdown: 12d 4h Event time in various zones: New_York: 2025-11-30 19:00 EST Los_Angeles: 2025-11-30 16:00 PST London: 2025-12-01 00:00 GMT Tokyo: 2025-12-01 09:00 JST
💡 Key Insight
Epoch time is always UTC. When you convert a local time string to epoch, specify the timezone. When displaying epoch, convert to the desired timezone.
10
Complete Duration Tracking Library

A reusable library of functions for epoch time and duration calculations.

#!/bin/bash
# Complete epoch time and duration library

#######################################
# EPOCH CONVERSION FUNCTIONS
#######################################

# Get current epoch (seconds)
now_epoch() {
    date +%s
}

# Get current epoch (milliseconds)
now_ms() {
    date +%s%3N
}

# Get current epoch (nanoseconds)
now_ns() {
    date +%s%N
}

# Convert date string to epoch
to_epoch() {
    date -d "$1" +%s
}

# Convert epoch to date string
from_epoch() {
    local format="${2:-%Y-%m-%d %H:%M:%S}"
    date -d @$1 "+$format"
}

#######################################
# DURATION CALCULATION FUNCTIONS
#######################################

# Calculate duration between two epochs
duration() {
    echo $(($2 - $1))
}

# Calculate duration between two date strings
duration_between() {
    local start=$(to_epoch "$1")
    local end=$(to_epoch "$2")
    echo $((end - start))
}

#######################################
# DURATION FORMATTING FUNCTIONS
#######################################

# Seconds to HH:MM:SS
to_hms() {
    local secs=$1
    printf "%02d:%02d:%02d" $((secs/3600)) $(((secs%3600)/60)) $((secs%60))
}

# Seconds to human readable
to_human() {
    local secs=$1
    local d=$((secs/86400)) h=$(((secs%86400)/3600))
    local m=$(((secs%3600)/60)) s=$((secs%60))
    
    ((d>0)) && printf "%dd " $d
    ((h>0)) && printf "%dh " $h
    ((m>0)) && printf "%dm " $m
    printf "%ds" $s
}

# Seconds to short format (Xm Ys)
to_short() {
    local secs=$1
    if ((secs >= 3600)); then
        printf "%dh %dm" $((secs/3600)) $(((secs%3600)/60))
    elif ((secs >= 60)); then
        printf "%dm %ds" $((secs/60)) $((secs%60))
    else
        printf "%ds" $secs
    fi
}

#######################################
# TIMER FUNCTIONS
#######################################

# Start a named timer
timer_start() {
    eval "TIMER_$1=$(now_ms)"
}

# Get elapsed time for named timer (ms)
timer_elapsed() {
    local start_var="TIMER_$1"
    local start=${!start_var}
    echo $(($(now_ms) - start))
}

# Stop timer and return formatted duration
timer_stop() {
    local ms=$(timer_elapsed $1)
    if ((ms >= 1000)); then
        echo "$(echo "scale=2; $ms/1000" | bc)s"
    else
        echo "${ms}ms"
    fi
}

#######################################
# DEMO
#######################################

echo "===== Epoch Time Library Demo ====="
echo ""

echo "Current time:"
echo "  Epoch (s):  $(now_epoch)"
echo "  Epoch (ms): $(now_ms)"
echo "  Formatted:  $(from_epoch $(now_epoch))"

echo ""
echo "Conversions:"
echo "  String to epoch: $(to_epoch '2025-11-18 14:30:00')"
echo "  Epoch to string: $(from_epoch 1731945000)"
echo "  Custom format:   $(from_epoch 1731945000 '%A, %B %d')"

echo ""
echo "Duration calculations:"
dur=$(duration_between "2025-11-18 09:00:00" "2025-11-18 17:30:00")
echo "  9 AM to 5:30 PM = $dur seconds"
echo "  As HH:MM:SS:    $(to_hms $dur)"
echo "  Human readable: $(to_human $dur)"
echo "  Short format:   $(to_short $dur)"

echo ""
echo "Timer demo:"
timer_start "demo"
sleep 0.5
echo "  After 500ms: $(timer_stop demo)"

timer_start "work"
for i in {1..10000}; do :; done
echo "  Loop 10000:  $(timer_stop work)"

echo ""
echo "Format examples:"
for secs in 45 125 3661 90061; do
    printf "  %6d sec = %-12s = %-15s = %s\n" \
        $secs "$(to_short $secs)" "$(to_human $secs)" "$(to_hms $secs)"
done

echo ""
echo "===== End Demo ====="
Output: ===== Epoch Time Library Demo ===== Current time: Epoch (s): 1731945045 Epoch (ms): 1731945045123 Formatted: 2025-11-18 14:30:45 Conversions: String to epoch: 1731945000 Epoch to string: 2025-11-18 14:30:00 Custom format: Tuesday, November 18 Duration calculations: 9 AM to 5:30 PM = 30600 seconds As HH:MM:SS: 08:30:00 Human readable: 8h 30m 0s Short format: 8h 30m Timer demo: After 500ms: 0.50s Loop 10000: 12ms Format examples: 45 sec = 45s = 45s = 00:00:45 125 sec = 2m 5s = 2m 5s = 00:02:05 3661 sec = 1h 1m = 1h 1m 1s = 01:01:01 90061 sec = 25h 1m = 1d 1h 1m 1s = 25:01:01 ===== End Demo =====

Quick Reference & Patterns

Essential Commands

# Get current epoch
epoch=$(date +%s)

# Convert to epoch
epoch=$(date -d "2025-11-18 14:30:00" +%s)

# Convert from epoch
date -d @1731945000 '+%Y-%m-%d %H:%M:%S'

# Calculate duration
duration=$((end_epoch - start_epoch))

# Duration to minutes:seconds
mins=$((duration / 60))
secs=$((duration % 60))
printf "%d:%02d" $mins $secs

# Duration to hours:minutes:seconds
printf "%02d:%02d:%02d" $((dur/3600)) $(((dur%3600)/60)) $((dur%60))

Common Patterns

TaskCode
Time an operation start=$(date +%s); work; echo $(($(date +%s) - start))s
Check if expired (($(date +%s) > expiry_epoch)) && echo "Expired"
Add time to epoch future=$((epoch + 3600)) # +1 hour
Subtract time past=$((epoch - 86400)) # -1 day
High precision ms=$(date +%s%3N)
UTC epoch date -u +%s (same as local)

Best Practices

✓ Do
✗ Avoid
← Back to BashSpecial Index ↑ Back to EXPANDED