πŸ“… Bash Dates & Time

Comprehensive Reference Guide β€” Formatting, parsing, arithmetic, timezones, and practical date operations

πŸ“‹ Table of Contents

Overview

The date command is the primary tool for displaying and manipulating dates and times in Bash. It supports extensive formatting options, date arithmetic, parsing, and timezone conversionsβ€”essential for log rotation, backup scheduling, report generation, and system administration tasks.

Key Capabilities

Display: Format current date/time with date +FORMAT

Parse: Convert strings to dates with date -d "STRING"

Arithmetic: Add/subtract time with date -d "+1 day"

Timezone: Convert between zones with TZ=zone date

Format Specifiers Reference

Date Components

SpecifierDescriptionExample
%Y4-digit year2025
%y2-digit year25
%mMonth (01-12)11
%dDay of month (01-31)18
%eDay of month (1-31, space pad)18
%BFull month nameNovember
%bAbbreviated monthNov
%AFull weekday nameTuesday
%aAbbreviated weekdayTue
%uDay of week (1-7, Monday=1)2
%wDay of week (0-6, Sunday=0)2
%jDay of year (001-366)322
%UWeek number (Sunday start)46
%WWeek number (Monday start)46
%VISO week number47

Time Components

SpecifierDescriptionExample
%HHour (00-23)14
%IHour (01-12)02
%MMinute (00-59)30
%SSecond (00-59)45
%NNanoseconds (000000000-999999999)123456789
%pAM/PM (uppercase)PM
%Pam/pm (lowercase)pm
%sUnix epoch (seconds since 1970)1731958245
%ZTimezone abbreviationEST
%zTimezone offset-0500
%:zTimezone offset with colon-05:00

Common Combinations

SpecifierEquivalentExample
%F%Y-%m-%d2025-11-18
%T%H:%M:%S14:30:45
%D%m/%d/%y11/18/25
%R%H:%M14:30
%r%I:%M:%S %p02:30:45 PM
%cLocale date/timeTue Nov 18 14:30:45 2025
%xLocale date11/18/2025
%XLocale time14:30:45

Quick Reference

date +%Y-%m-%d β†’ 2025-11-18
date +%H:%M:%S β†’ 14:30:45
date +%s β†’ 1731958245 (epoch)
date -d "tomorrow" β†’ Tomorrow's date
date -d @1731958245 β†’ From epoch

Detailed Examples

1
Date Formatting for Logs and Filenames

Essential date formats for system administrationβ€”logs, backups, and reports.

#!/bin/bash
# Date formatting for various purposes

echo "=== Standard Formats ==="
echo "Default:      $(date)"
echo "ISO 8601:     $(date +%Y-%m-%dT%H:%M:%S%z)"
echo "ISO Date:     $(date +%Y-%m-%d)"
echo "ISO Time:     $(date +%H:%M:%S)"
echo "RFC 2822:     $(date -R)"
echo "UTC:          $(date -u '+%Y-%m-%d %H:%M:%S UTC')"

echo ""
echo "=== Regional Formats ==="
echo "US Date:      $(date +%m/%d/%Y)"
echo "US Long:      $(date '+%B %d, %Y')"
echo "EU Date:      $(date +%d/%m/%Y)"
echo "EU Long:      $(date '+%d %B %Y')"

echo ""
echo "=== Filename-Safe Formats ==="
echo "Daily backup:   backup-$(date +%Y-%m-%d).tar.gz"
echo "Timestamped:    log-$(date +%Y%m%d_%H%M%S).txt"
echo "Hourly:         data-$(date +%Y-%m-%d_%H).csv"
echo "Weekly:         report-$(date +%Y-W%W).pdf"
echo "Monthly:        archive-$(date +%Y-%m).zip"

echo ""
echo "=== Log Entry Formats ==="
# Syslog style
echo "$(date '+%b %d %H:%M:%S') $(hostname) myapp[$$]: Message"

# ISO style (recommended)
echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: Application started"

# With milliseconds
echo "[$(date '+%Y-%m-%d %H:%M:%S.%3N')] DEBUG: Detailed message"

echo ""
echo "=== Report Headers ==="
echo "Generated: $(date '+%A, %B %e, %Y at %I:%M %p %Z')"
echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S %Z')"

echo ""
echo "=== Special Values ==="
echo "Epoch:        $(date +%s)"
echo "Nanoseconds:  $(date +%N)"
echo "Day of year:  $(date +%j) / 365"
echo "Week number:  $(date +%W) / 52"
echo "Quarter:      Q$(( ($(date +%-m) - 1) / 3 + 1 ))"
Output: === Standard Formats === Default: Tue Nov 18 14:30:45 EST 2025 ISO 8601: 2025-11-18T14:30:45-0500 ISO Date: 2025-11-18 ISO Time: 14:30:45 === Filename-Safe Formats === Daily backup: backup-2025-11-18.tar.gz Timestamped: log-20251118_143045.txt Hourly: data-2025-11-18_14.csv === Log Entry Formats === Nov 18 14:30:45 server myapp[12345]: Message [2025-11-18 14:30:45] INFO: Application started [2025-11-18 14:30:45.123] DEBUG: Detailed message === Report Headers === Generated: Tuesday, November 18, 2025 at 02:30 PM EST
2
Date Arithmetic: Adding and Subtracting

Calculating future and past dates for scheduling, retention, and planning.

#!/bin/bash
# Date arithmetic with -d flag

echo "=== Reference Point ==="
echo "Today: $(date '+%Y-%m-%d %A')"

echo ""
echo "=== Adding Time ==="
echo "Tomorrow:      $(date -d 'tomorrow' '+%Y-%m-%d %A')"
echo "+1 day:        $(date -d '+1 day' '+%Y-%m-%d')"
echo "+3 days:       $(date -d '+3 days' '+%Y-%m-%d')"
echo "+1 week:       $(date -d '+1 week' '+%Y-%m-%d')"
echo "+2 weeks:      $(date -d '+2 weeks' '+%Y-%m-%d')"
echo "+1 month:      $(date -d '+1 month' '+%Y-%m-%d')"
echo "+3 months:     $(date -d '+3 months' '+%Y-%m-%d')"
echo "+1 year:       $(date -d '+1 year' '+%Y-%m-%d')"

echo ""
echo "=== Subtracting Time ==="
echo "Yesterday:     $(date -d 'yesterday' '+%Y-%m-%d %A')"
echo "-1 day:        $(date -d '-1 day' '+%Y-%m-%d')"
echo "-7 days:       $(date -d '-7 days' '+%Y-%m-%d')"
echo "-30 days:      $(date -d '-30 days' '+%Y-%m-%d')"
echo "-3 months:     $(date -d '-3 months' '+%Y-%m-%d')"
echo "-1 year:       $(date -d '-1 year' '+%Y-%m-%d')"

echo ""
echo "=== Time Arithmetic ==="
echo "Now:           $(date '+%H:%M:%S')"
echo "+2 hours:      $(date -d '+2 hours' '+%H:%M:%S')"
echo "-30 minutes:   $(date -d '-30 minutes' '+%H:%M:%S')"
echo "+90 seconds:   $(date -d '+90 seconds' '+%H:%M:%S')"

echo ""
echo "=== Combined Arithmetic ==="
echo "+1 day 3 hours:    $(date -d '+1 day +3 hours' '+%Y-%m-%d %H:%M')"
echo "tomorrow noon:     $(date -d 'tomorrow 12:00' '+%Y-%m-%d %H:%M')"
echo "next monday 9am:   $(date -d 'next monday 09:00' '+%Y-%m-%d %H:%M')"

echo ""
echo "=== From Specific Date ==="
base_date="2025-01-15"
echo "Base: $base_date"
echo "+30 days:      $(date -d "$base_date +30 days" '+%Y-%m-%d')"
echo "+6 months:     $(date -d "$base_date +6 months" '+%Y-%m-%d')"
echo "End of month:  $(date -d "$base_date +1 month -1 day" '+%Y-%m-%d')"

echo ""
echo "=== Practical: Retention Dates ==="
echo "Delete files older than:"
echo "  7 days:   $(date -d '-7 days' '+%Y-%m-%d')"
echo "  30 days:  $(date -d '-30 days' '+%Y-%m-%d')"
echo "  90 days:  $(date -d '-90 days' '+%Y-%m-%d')"
echo "  1 year:   $(date -d '-1 year' '+%Y-%m-%d')"
Output: === Reference Point === Today: 2025-11-18 Tuesday === Adding Time === Tomorrow: 2025-11-19 Wednesday +1 day: 2025-11-19 +3 days: 2025-11-21 +1 week: 2025-11-25 +1 month: 2025-12-18 +1 year: 2026-11-18 === Subtracting Time === Yesterday: 2025-11-17 Monday -7 days: 2025-11-11 -30 days: 2025-10-19 -1 year: 2024-11-18 === Practical: Retention Dates === Delete files older than: 7 days: 2025-11-11 30 days: 2025-10-19 90 days: 2025-08-20 1 year: 2024-11-18
3
Parsing and Converting Date Formats

Converting between date formats and parsing various input strings.

#!/bin/bash
# Date parsing and format conversion

echo "=== Parse Various Input Formats ==="
inputs=(
    "November 18, 2025"
    "Nov 18, 2025"
    "18 Nov 2025"
    "11/18/2025"
    "2025-11-18"
    "2025/11/18"
    "18-Nov-2025"
)

for input in "${inputs[@]}"; do
    parsed=$(date -d "$input" '+%Y-%m-%d' 2>/dev/null)
    if [[ $? -eq 0 ]]; then
        printf "%-20s β†’ %s\n" "$input" "$parsed"
    else
        printf "%-20s β†’ FAILED\n" "$input"
    fi
done

echo ""
echo "=== Parse with Time ==="
echo "'2025-11-18 14:30':     $(date -d '2025-11-18 14:30' '+%Y-%m-%d %H:%M:%S')"
echo "'Nov 18 2:30 PM':       $(date -d 'Nov 18 2:30 PM' '+%Y-%m-%d %H:%M:%S')"
echo "'2025-11-18T14:30:00':  $(date -d '2025-11-18T14:30:00' '+%Y-%m-%d %H:%M:%S')"

echo ""
echo "=== Convert Between Formats ==="

# US to ISO
us_date="11/18/2025"
iso=$(date -d "$us_date" '+%Y-%m-%d')
echo "US to ISO: $us_date β†’ $iso"

# ISO to verbose
iso_date="2025-11-18"
verbose=$(date -d "$iso_date" '+%A, %B %d, %Y')
echo "ISO to verbose: $iso_date β†’ $verbose"

# Short to long
short="Nov 18"
long=$(date -d "$short" '+%B %d, %Y')
echo "Short to long: $short β†’ $long"

echo ""
echo "=== Epoch Conversions ==="
# Current to epoch
now_epoch=$(date +%s)
echo "Now to epoch: $(date '+%Y-%m-%d %H:%M:%S') β†’ $now_epoch"

# Epoch to human
echo "Epoch to human: 1731958245 β†’ $(date -d @1731958245 '+%Y-%m-%d %H:%M:%S')"

# String to epoch
date_str="2025-12-25 00:00:00"
epoch=$(date -d "$date_str" +%s)
echo "String to epoch: $date_str β†’ $epoch"

echo ""
echo "=== Parse Log Timestamps ==="

# Syslog format
syslog="Nov 18 14:30:45"
parsed=$(date -d "$syslog" '+%Y-%m-%d %H:%M:%S')
echo "Syslog: $syslog β†’ $parsed"

# Apache format (need to adjust)
apache="18/Nov/2025:14:30:45"
adjusted=$(echo "$apache" | sed 's/\// /g; s/:/ /')
parsed=$(date -d "$adjusted" '+%Y-%m-%d %H:%M:%S')
echo "Apache: $apache β†’ $parsed"

echo ""
echo "=== Validate Date Input ==="
validate_date() {
    date -d "$1" '+%Y-%m-%d' &>/dev/null
}

test_dates=("2025-11-18" "2025-02-30" "invalid" "13/45/2025")
for d in "${test_dates[@]}"; do
    if validate_date "$d"; then
        echo "'$d' is VALID"
    else
        echo "'$d' is INVALID"
    fi
done
Output: === Parse Various Input Formats === November 18, 2025 β†’ 2025-11-18 Nov 18, 2025 β†’ 2025-11-18 18 Nov 2025 β†’ 2025-11-18 11/18/2025 β†’ 2025-11-18 2025-11-18 β†’ 2025-11-18 === Convert Between Formats === US to ISO: 11/18/2025 β†’ 2025-11-18 ISO to verbose: 2025-11-18 β†’ Tuesday, November 18, 2025 === Epoch Conversions === Now to epoch: 2025-11-18 14:30:45 β†’ 1731958245 Epoch to human: 1731958245 β†’ 2025-11-18 14:30:45 String to epoch: 2025-12-25 00:00:00 β†’ 1735189200 === Validate Date Input === '2025-11-18' is VALID '2025-02-30' is INVALID 'invalid' is INVALID '13/45/2025' is INVALID
πŸ’‘ Epoch Parsing
Use @ prefix to parse Unix epoch: date -d @1731958245
4
Calculating Time Differences

Computing elapsed time, age, and duration between two dates.

#!/bin/bash
# Calculating time differences

echo "=== Days Between Two Dates ==="
date1="2025-01-01"
date2="2025-11-18"

epoch1=$(date -d "$date1" +%s)
epoch2=$(date -d "$date2" +%s)
diff_secs=$((epoch2 - epoch1))
diff_days=$((diff_secs / 86400))

echo "From: $date1"
echo "To:   $date2"
echo "Days: $diff_days"
echo "Weeks: $((diff_days / 7))"
echo "Months (approx): $((diff_days / 30))"

echo ""
echo "=== Time Until Future Date ==="
future="2025-12-25"
now=$(date +%s)
future_epoch=$(date -d "$future" +%s)
until_secs=$((future_epoch - now))
until_days=$((until_secs / 86400))

echo "Days until $future: $until_days"

echo ""
echo "=== Age Calculation ==="
birth_date="1982-06-25"
today=$(date +%s)
birth_epoch=$(date -d "$birth_date" +%s)
age_secs=$((today - birth_epoch))
age_days=$((age_secs / 86400))
age_years=$((age_days / 365))

echo "Birth: $birth_date"
echo "Age: ~$age_years years ($age_days days)"

echo ""
echo "=== Format Duration ==="
format_duration() {
    local secs=$1
    local days=$((secs / 86400))
    local hours=$(( (secs % 86400) / 3600 ))
    local mins=$(( (secs % 3600) / 60 ))
    local s=$((secs % 60))
    
    if ((days > 0)); then
        printf "%dd %dh %dm %ds" $days $hours $mins $s
    elif ((hours > 0)); then
        printf "%dh %dm %ds" $hours $mins $s
    elif ((mins > 0)); then
        printf "%dm %ds" $mins $s
    else
        printf "%ds" $s
    fi
}

echo "Duration examples:"
for secs in 45 3661 86400 90061 604800; do
    printf "%8d seconds = %s\n" $secs "$(format_duration $secs)"
done

echo ""
echo "=== Working Days Between Dates ==="
count_workdays() {
    local start=$1
    local end=$2
    local count=0
    local current=$start
    
    while [[ "$current" < "$end" ]]; do
        local dow=$(date -d "$current" +%u)
        if ((dow < 6)); then  # Mon-Fri
            ((count++))
        fi
        current=$(date -d "$current +1 day" +%Y-%m-%d)
    done
    
    echo $count
}

start="2025-11-01"
end="2025-11-18"
workdays=$(count_workdays "$start" "$end")
echo "Working days from $start to $end: $workdays"

echo ""
echo "=== Practical: SLA Calculation ==="
ticket_opened="2025-11-18 09:00:00"
ticket_closed="2025-11-18 14:30:45"

open_epoch=$(date -d "$ticket_opened" +%s)
close_epoch=$(date -d "$ticket_closed" +%s)
resolution_secs=$((close_epoch - open_epoch))
resolution_mins=$((resolution_secs / 60))
resolution_hours=$(echo "scale=2; $resolution_secs / 3600" | bc)

echo "Opened: $ticket_opened"
echo "Closed: $ticket_closed"
echo "Resolution time: ${resolution_mins} minutes (${resolution_hours} hours)"
Output: === Days Between Two Dates === From: 2025-01-01 To: 2025-11-18 Days: 321 Weeks: 45 Months (approx): 10 === Time Until Future Date === Days until 2025-12-25: 37 === Age Calculation === Birth: 1982-06-25 Age: ~43 years (15852 days) === Format Duration === Duration examples: 45 seconds = 45s 3661 seconds = 1h 1m 1s 86400 seconds = 1d 0h 0m 0s 90061 seconds = 1d 1h 1m 1s 604800 seconds = 7d 0h 0m 0s === Working Days Between Dates === Working days from 2025-11-01 to 2025-11-18: 11 === Practical: SLA Calculation === Opened: 2025-11-18 09:00:00 Closed: 2025-11-18 14:30:45 Resolution time: 330 minutes (5.51 hours)
5
Timezone Operations

Converting between timezones and displaying times for global teams.

#!/bin/bash
# Timezone operations

echo "=== Current Time in Various Zones ==="
echo "Local:      $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "UTC:        $(TZ=UTC date '+%Y-%m-%d %H:%M:%S %Z')"

echo ""
echo "US Timezones:"
echo "  Eastern:  $(TZ=America/New_York date '+%H:%M %Z')"
echo "  Central:  $(TZ=America/Chicago date '+%H:%M %Z')"
echo "  Mountain: $(TZ=America/Denver date '+%H:%M %Z')"
echo "  Pacific:  $(TZ=America/Los_Angeles date '+%H:%M %Z')"

echo ""
echo "International:"
echo "  London:   $(TZ=Europe/London date '+%H:%M %Z')"
echo "  Paris:    $(TZ=Europe/Paris date '+%H:%M %Z')"
echo "  Tokyo:    $(TZ=Asia/Tokyo date '+%H:%M %Z')"
echo "  Sydney:   $(TZ=Australia/Sydney date '+%H:%M %Z')"

echo ""
echo "=== Convert Specific Time Between Zones ==="
# Meeting at 2 PM Eastern - what time elsewhere?
meeting_time="2025-11-18 14:00:00"
meeting_epoch=$(TZ=America/New_York date -d "$meeting_time" +%s)

echo "Meeting: $meeting_time Eastern"
echo ""
echo "Equivalent times:"
for tz in "America/Los_Angeles:Pacific" "America/Chicago:Central" \
          "UTC:UTC" "Europe/London:London" "Asia/Tokyo:Tokyo"; do
    IFS=':' read -r zone label <<< "$tz"
    converted=$(TZ=$zone date -d @$meeting_epoch '+%H:%M %Z (%Y-%m-%d)')
    printf "  %-10s %s\n" "$label:" "$converted"
done

echo ""
echo "=== Parse Time with Timezone ==="
# Input time is in Pacific, convert to local
pacific_time="2025-11-18 09:00:00"
pacific_epoch=$(TZ=America/Los_Angeles date -d "$pacific_time" +%s)

echo "Source: $pacific_time Pacific"
echo "Local:  $(date -d @$pacific_epoch '+%Y-%m-%d %H:%M:%S %Z')"
echo "UTC:    $(TZ=UTC date -d @$pacific_epoch '+%Y-%m-%d %H:%M:%S %Z')"

echo ""
echo "=== Timezone Offset Information ==="
echo "Current timezone: $(date +%Z)"
echo "UTC offset: $(date +%z)"
echo "Offset with colon: $(date +%:z)"

echo ""
echo "=== Store in UTC, Display Local ==="
# Best practice: store timestamps in UTC
stored_utc="2025-11-18T19:30:45Z"

# Display in various local times
echo "Stored (UTC): $stored_utc"
for tz in "America/New_York" "America/Los_Angeles" "Europe/London"; do
    local_time=$(TZ=$tz date -d "$stored_utc" '+%Y-%m-%d %H:%M:%S %Z')
    printf "  %-20s %s\n" "$tz:" "$local_time"
done

echo ""
echo "=== Practical: Global Team Meeting Planner ==="
echo ""
echo "Finding a good meeting time (9 AM - 5 PM for all):"

# Check 14:00 UTC
utc_hour=14
echo "Testing ${utc_hour}:00 UTC:"

zones=("America/New_York" "America/Los_Angeles" "Europe/London" "Asia/Tokyo")
all_good=true

for tz in "${zones[@]}"; do
    local_hour=$(TZ=$tz date -d "2025-11-18 ${utc_hour}:00 UTC" '+%H')
    local_time=$(TZ=$tz date -d "2025-11-18 ${utc_hour}:00 UTC" '+%H:%M %Z')
    
    if ((10#$local_hour >= 9 && 10#$local_hour < 17)); then
        status="βœ“"
    else
        status="βœ—"
        all_good=false
    fi
    
    printf "  %s %-20s %s\n" "$status" "$tz" "$local_time"
done
Output: === Current Time in Various Zones === Local: 2025-11-18 14:30:45 EST UTC: 2025-11-18 19:30:45 UTC US Timezones: Eastern: 14:30 EST Central: 13:30 CST Mountain: 12:30 MST Pacific: 11:30 PST === Convert Specific Time Between Zones === Meeting: 2025-11-18 14:00:00 Eastern Equivalent times: Pacific: 11:00 PST (2025-11-18) Central: 13:00 CST (2025-11-18) UTC: 19:00 UTC (2025-11-18) London: 19:00 GMT (2025-11-18) Tokyo: 04:00 JST (2025-11-19) === Practical: Global Team Meeting Planner === Finding a good meeting time (9 AM - 5 PM for all): Testing 14:00 UTC: βœ“ America/New_York 09:00 EST βœ— America/Los_Angeles 06:00 PST βœ“ Europe/London 14:00 GMT βœ— Asia/Tokyo 23:00 JST
⚠️ Use Full Timezone Names
Always use America/New_York not EST. Abbreviations are ambiguous (EST = Eastern Standard Time or Eastern Summer Time in Australia).
6
Weekday and Calendar Operations

Finding specific weekdays, first/last of month, and calendar calculations.

#!/bin/bash
# Weekday and calendar operations

echo "=== Current Day Information ==="
echo "Day name: $(date '+%A')"
echo "Day number (Mon=1): $(date '+%u')"
echo "Day number (Sun=0): $(date '+%w')"
echo "Day of month: $(date '+%d')"
echo "Day of year: $(date '+%j')"

echo ""
echo "=== Find Specific Weekdays ==="
echo "Next Monday:    $(date -d 'next monday' '+%Y-%m-%d')"
echo "Next Friday:    $(date -d 'next friday' '+%Y-%m-%d')"
echo "Last Sunday:    $(date -d 'last sunday' '+%Y-%m-%d')"
echo "This Wednesday: $(date -d 'this wednesday' '+%Y-%m-%d')"

echo ""
echo "=== This Week's Days ==="
for day in monday tuesday wednesday thursday friday saturday sunday; do
    echo "  $(date -d "this $day" '+%Y-%m-%d %A')"
done

echo ""
echo "=== First and Last of Month ==="
year=$(date '+%Y')
month=$(date '+%m')

# First day
first=$(date -d "$year-$month-01" '+%Y-%m-%d (%A)')
echo "First of month: $first"

# Last day
last=$(date -d "$year-$month-01 +1 month -1 day" '+%Y-%m-%d (%A)')
echo "Last of month: $last"

# Days in month
days_in_month=$(date -d "$year-$month-01 +1 month -1 day" '+%d')
echo "Days in month: $days_in_month"

echo ""
echo "=== First Monday of Month ==="
first_of_month="$year-$month-01"
dow=$(date -d "$first_of_month" '+%u')  # 1=Monday

if ((dow == 1)); then
    first_monday="$first_of_month"
else
    days_until=$((8 - dow))
    first_monday=$(date -d "$first_of_month +$days_until days" '+%Y-%m-%d')
fi
echo "First Monday: $first_monday"

echo ""
echo "=== Check Weekend vs Weekday ==="
check_day() {
    local d=$1
    local dow=$(date -d "$d" '+%u')
    local name=$(date -d "$d" '+%A')
    
    if ((dow >= 6)); then
        echo "$d ($name) is a WEEKEND"
    else
        echo "$d ($name) is a WEEKDAY"
    fi
}

check_day "2025-11-18"
check_day "2025-11-22"
check_day "2025-11-23"

echo ""
echo "=== Practical: Next Occurrence ==="
# Find next occurrence of a specific day (e.g., 15th)
target_day=15
current_day=$(date '+%d')
current_month=$(date '+%Y-%m')

if ((10#$current_day < target_day)); then
    next_occurrence="$current_month-$(printf '%02d' $target_day)"
else
    next_occurrence=$(date -d "$current_month-01 +1 month" '+%Y-%m')-$(printf '%02d' $target_day)
fi

echo "Next ${target_day}th: $next_occurrence ($(date -d "$next_occurrence" '+%A'))"

echo ""
echo "=== Calendar for Current Month ==="
cal
Output: === Current Day Information === Day name: Tuesday Day number (Mon=1): 2 Day number (Sun=0): 2 Day of month: 18 Day of year: 322 === Find Specific Weekdays === Next Monday: 2025-11-24 Next Friday: 2025-11-21 Last Sunday: 2025-11-16 This Wednesday: 2025-11-19 === First and Last of Month === First of month: 2025-11-01 (Saturday) Last of month: 2025-11-30 (Sunday) Days in month: 30 === First Monday of Month === First Monday: 2025-11-03 === Check Weekend vs Weekday === 2025-11-18 (Tuesday) is a WEEKDAY 2025-11-22 (Saturday) is a WEEKEND 2025-11-23 (Sunday) is a WEEKEND === Next Occurrence === Next 15th: 2025-12-15 (Monday)
7
Relative Date Expressions

Using natural language for intuitive date manipulation.

#!/bin/bash
# Relative and natural language dates

echo "=== Basic Relative Terms ==="
echo "now:        $(date -d 'now' '+%Y-%m-%d %H:%M:%S')"
echo "today:      $(date -d 'today' '+%Y-%m-%d')"
echo "yesterday:  $(date -d 'yesterday' '+%Y-%m-%d')"
echo "tomorrow:   $(date -d 'tomorrow' '+%Y-%m-%d')"

echo ""
echo "=== Time-of-Day Terms ==="
echo "midnight:   $(date -d 'midnight' '+%H:%M:%S')"
echo "noon:       $(date -d 'noon' '+%H:%M:%S')"
echo "teatime:    $(date -d 'teatime' '+%H:%M:%S')"  # 4:00 PM

echo ""
echo "=== Ago and Hence ==="
echo "3 days ago:      $(date -d '3 days ago' '+%Y-%m-%d')"
echo "2 weeks ago:     $(date -d '2 weeks ago' '+%Y-%m-%d')"
echo "1 month ago:     $(date -d '1 month ago' '+%Y-%m-%d')"
echo "1 week hence:    $(date -d '1 week hence' '+%Y-%m-%d')"
echo "fortnight:       $(date -d 'fortnight' '+%Y-%m-%d')"  # 2 weeks

echo ""
echo "=== Compound Expressions ==="
echo "yesterday noon:      $(date -d 'yesterday noon' '+%Y-%m-%d %H:%M')"
echo "tomorrow midnight:   $(date -d 'tomorrow midnight' '+%Y-%m-%d %H:%M')"
echo "next week monday:    $(date -d 'next week monday' '+%Y-%m-%d')"
echo "2 days ago 3pm:      $(date -d '2 days ago 15:00' '+%Y-%m-%d %H:%M')"

echo ""
echo "=== First/Last of Period ==="
# First of current month
echo "First of month:   $(date -d "$(date '+%Y-%m-01')" '+%Y-%m-%d')"

# Last of current month
echo "Last of month:    $(date -d "$(date '+%Y-%m-01') +1 month -1 day" '+%Y-%m-%d')"

# First of year
echo "First of year:    $(date '+%Y-01-01')"

# Last of year
echo "Last of year:     $(date '+%Y-12-31')"

echo ""
echo "=== Practical: Report Periods ==="
today=$(date '+%Y-%m-%d')

echo "Report periods as of $today:"
echo ""
echo "Today:        $today"
echo "Yesterday:    $(date -d 'yesterday' '+%Y-%m-%d')"
echo ""
echo "This week:    $(date -d 'last monday' '+%Y-%m-%d') to $(date -d 'next sunday' '+%Y-%m-%d')"
echo "Last week:    $(date -d 'last monday -7 days' '+%Y-%m-%d') to $(date -d 'last sunday' '+%Y-%m-%d')"
echo ""
echo "This month:   $(date '+%Y-%m-01') to $(date -d "$(date '+%Y-%m-01') +1 month -1 day" '+%Y-%m-%d')"
echo "Last month:   $(date -d 'last month' '+%Y-%m-01') to $(date -d "$(date '+%Y-%m-01') -1 day" '+%Y-%m-%d')"
echo ""
echo "Last 7 days:  $(date -d '-6 days' '+%Y-%m-%d') to $today"
echo "Last 30 days: $(date -d '-29 days' '+%Y-%m-%d') to $today"
echo "Last 90 days: $(date -d '-89 days' '+%Y-%m-%d') to $today"

echo ""
echo "=== Quarter Calculation ==="
month=$(date '+%-m')
quarter=$(( (month - 1) / 3 + 1 ))
year=$(date '+%Y')

q_start_month=$(( (quarter - 1) * 3 + 1 ))
q_end_month=$((quarter * 3))

q_start=$(date -d "$year-$q_start_month-01" '+%Y-%m-%d')
q_end=$(date -d "$year-$q_end_month-01 +1 month -1 day" '+%Y-%m-%d')

echo "Current quarter: Q$quarter $year"
echo "Q$quarter: $q_start to $q_end"
Output: === Basic Relative Terms === now: 2025-11-18 14:30:45 today: 2025-11-18 yesterday: 2025-11-17 tomorrow: 2025-11-19 === Time-of-Day Terms === midnight: 00:00:00 noon: 12:00:00 teatime: 16:00:00 === Ago and Hence === 3 days ago: 2025-11-15 2 weeks ago: 2025-11-04 1 month ago: 2025-10-18 1 week hence: 2025-11-25 === Practical: Report Periods === Report periods as of 2025-11-18: Today: 2025-11-18 Yesterday: 2025-11-17 This week: 2025-11-17 to 2025-11-23 Last week: 2025-11-10 to 2025-11-16 This month: 2025-11-01 to 2025-11-30 Last 7 days: 2025-11-12 to 2025-11-18 Last 30 days: 2025-10-20 to 2025-11-18 === Quarter Calculation === Current quarter: Q4 2025 Q4: 2025-10-01 to 2025-12-31
8
Log Rotation and File Dating

Creating dated filenames and implementing rotation schemes.

#!/bin/bash
# Log rotation and file dating

echo "=== Filename Timestamp Patterns ==="
echo "Second:    file-$(date '+%Y%m%d_%H%M%S').log"
echo "Minute:    file-$(date '+%Y%m%d_%H%M').log"
echo "Hourly:    file-$(date '+%Y-%m-%d_%H').log"
echo "Daily:     file-$(date '+%Y-%m-%d').log"
echo "Weekly:    file-$(date '+%Y-W%W').log"
echo "Monthly:   file-$(date '+%Y-%m').log"
echo "Yearly:    file-$(date '+%Y').log"

echo ""
echo "=== Rotation File Names ==="
base="application"

echo "Current:   ${base}.log"
for i in {1..7}; do
    rotated="${base}.log.$(date -d "-$i days" '+%Y-%m-%d')"
    echo "Day -$i:    $rotated"
done

echo ""
echo "=== Backup Naming Schemes ==="
prefix="backup"

echo "Daily:     ${prefix}-daily-$(date '+%Y-%m-%d').tar.gz"
echo "Weekly:    ${prefix}-weekly-$(date -d 'last sunday' '+%Y-%m-%d').tar.gz"
echo "Monthly:   ${prefix}-monthly-$(date '+%Y-%m').tar.gz"
echo "Quarterly: ${prefix}-Q$(( ($(date '+%-m') - 1) / 3 + 1 ))-$(date '+%Y').tar.gz"
echo "Yearly:    ${prefix}-$(date '+%Y').tar.gz"

echo ""
echo "=== Rotation Script Pattern ==="
cat << 'SCRIPT'
#!/bin/bash
LOG_DIR="/var/log/myapp"
LOG_FILE="$LOG_DIR/app.log"
KEEP_DAYS=7

# Rotate current log
if [[ -f "$LOG_FILE" ]]; then
    mv "$LOG_FILE" "${LOG_FILE}.$(date '+%Y-%m-%d')"
    touch "$LOG_FILE"
    chmod 644 "$LOG_FILE"
fi

# Remove old rotated logs
find "$LOG_DIR" -name "app.log.*" -mtime +$KEEP_DAYS -delete

echo "Rotation complete: $(date)"
SCRIPT

echo ""
echo "=== Find Files by Date ==="
echo "Modified in last 24 hours:"
echo "  find /var/log -mtime -1 -type f"
echo ""
echo "Modified more than 7 days ago:"
echo "  find /var/log -mtime +7 -type f"
echo ""
echo "Modified today:"
echo "  find /var/log -daystart -mtime 0 -type f"
echo ""
echo "Modified between dates:"
echo "  find /var/log -newermt '2025-11-01' ! -newermt '2025-11-15'"

echo ""
echo "=== Cleanup Schedule ==="
echo "Retention policy cleanup dates:"
printf "%-20s %s\n" "Hourly logs:" "$(date -d '-24 hours' '+%Y-%m-%d %H:%M')"
printf "%-20s %s\n" "Daily backups:" "$(date -d '-7 days' '+%Y-%m-%d')"
printf "%-20s %s\n" "Weekly backups:" "$(date -d '-4 weeks' '+%Y-%m-%d')"
printf "%-20s %s\n" "Monthly backups:" "$(date -d '-12 months' '+%Y-%m-%d')"
printf "%-20s %s\n" "Yearly backups:" "$(date -d '-7 years' '+%Y-%m-%d')"

echo ""
echo "=== Date-Based Directory Structure ==="
# Create hierarchy: /logs/2025/11/18/
log_dir="/tmp/logs/$(date '+%Y/%m/%d')"
echo "Creating: $log_dir"
mkdir -p "$log_dir"
ls -la /tmp/logs/
Output: === Filename Timestamp Patterns === Second: file-20251118_143045.log Minute: file-20251118_1430.log Hourly: file-2025-11-18_14.log Daily: file-2025-11-18.log Weekly: file-2025-W46.log Monthly: file-2025-11.log === Rotation File Names === Current: application.log Day -1: application.log.2025-11-17 Day -2: application.log.2025-11-16 Day -3: application.log.2025-11-15 ... === Backup Naming Schemes === Daily: backup-daily-2025-11-18.tar.gz Weekly: backup-weekly-2025-11-16.tar.gz Monthly: backup-monthly-2025-11.tar.gz Quarterly: backup-Q4-2025.tar.gz === Cleanup Schedule === Retention policy cleanup dates: Hourly logs: 2025-11-17 14:30 Daily backups: 2025-11-11 Weekly backups: 2025-10-21 Monthly backups: 2024-11-18 Yearly backups: 2018-11-18
9
Cron Schedule Helpers

Working with cron schedules and finding next run times.

#!/bin/bash
# Cron schedule helpers

echo "=== Current Time Context ==="
echo "Now:       $(date '+%Y-%m-%d %H:%M:%S %A')"
echo "Hour:      $(date '+%H')"
echo "Minute:    $(date '+%M')"
echo "Day:       $(date '+%d')"
echo "Month:     $(date '+%m')"
echo "Weekday:   $(date '+%u') ($(date '+%A'))"

echo ""
echo "=== Next Run Calculations ==="

# Daily at 2 AM
current_hour=$(date '+%H')
if ((10#$current_hour < 2)); then
    next_daily=$(date -d 'today 02:00' '+%Y-%m-%d %H:%M')
else
    next_daily=$(date -d 'tomorrow 02:00' '+%Y-%m-%d %H:%M')
fi
echo "Daily @2AM:     $next_daily"

# Weekly on Sunday at 3 AM
current_dow=$(date '+%u')
if ((current_dow == 7 && 10#$current_hour < 3)); then
    next_weekly=$(date -d 'today 03:00' '+%Y-%m-%d %H:%M')
else
    next_weekly=$(date -d 'next sunday 03:00' '+%Y-%m-%d %H:%M')
fi
echo "Weekly Sun@3AM: $next_weekly"

# Monthly on 1st at midnight
current_day=$(date '+%d')
if ((10#$current_day == 1 && 10#$current_hour < 1)); then
    next_monthly=$(date -d 'today 00:00' '+%Y-%m-%d %H:%M')
else
    next_monthly=$(date -d "$(date -d '+1 month' '+%Y-%m-01') 00:00" '+%Y-%m-%d %H:%M')
fi
echo "Monthly 1st:    $next_monthly"

# Every 15 minutes
current_min=$(date '+%M')
next_quarter=$((((10#$current_min / 15) + 1) * 15))
if ((next_quarter >= 60)); then
    next_15min=$(date -d '+1 hour' '+%Y-%m-%d %H:00')
else
    next_15min=$(date '+%Y-%m-%d %H:')$(printf '%02d' $next_quarter)
fi
echo "Every 15min:    $next_15min"

echo ""
echo "=== Cron Expression Examples ==="
cat << 'EOF'
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ minute (0 - 59)
# β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ hour (0 - 23)
# β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ day of month (1 - 31)
# β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ month (1 - 12)
# β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ day of week (0 - 6, Sunday=0)
# β”‚ β”‚ β”‚ β”‚ β”‚
# * * * * *

0 2 * * *     # Daily at 2:00 AM
0 3 * * 0     # Weekly Sunday at 3:00 AM
0 0 1 * *     # Monthly on 1st at midnight
*/15 * * * *  # Every 15 minutes
0 9-17 * * 1-5 # Hourly 9-5, Mon-Fri
0 0 * * 1     # Weekly Monday at midnight
EOF

echo ""
echo "=== Time Until Next Run ==="
next_run=$(date -d 'tomorrow 02:00' '+%s')
now=$(date '+%s')
until_secs=$((next_run - now))
until_hours=$((until_secs / 3600))
until_mins=$(( (until_secs % 3600) / 60 ))

echo "Next daily backup (2 AM):"
echo "  Runs at: $(date -d @$next_run '+%Y-%m-%d %H:%M')"
echo "  In: ${until_hours}h ${until_mins}m"

echo ""
echo "=== Check if Should Run Now ==="
# Simulate cron check for "0 * * * *" (hourly on the hour)
should_run_hourly() {
    local minute=$(date '+%M')
    ((10#$minute == 0))
}

# Simulate check for "0 2 * * *" (daily at 2 AM)
should_run_daily_2am() {
    local hour=$(date '+%H')
    local minute=$(date '+%M')
    ((10#$hour == 2 && 10#$minute == 0))
}

# Simulate check for weekday only
is_weekday() {
    local dow=$(date '+%u')
    ((dow <= 5))
}

echo "Current minute: $(date '+%M')"
should_run_hourly && echo "Hourly job: SHOULD RUN" || echo "Hourly job: not yet"

echo "Current hour: $(date '+%H')"
should_run_daily_2am && echo "Daily 2AM job: SHOULD RUN" || echo "Daily 2AM job: not yet"

is_weekday && echo "Weekday check: YES" || echo "Weekday check: NO (weekend)"
Output: === Current Time Context === Now: 2025-11-18 14:30:45 Tuesday Hour: 14 Minute: 30 Day: 18 Month: 11 Weekday: 2 (Tuesday) === Next Run Calculations === Daily @2AM: 2025-11-19 02:00 Weekly Sun@3AM: 2025-11-23 03:00 Monthly 1st: 2025-12-01 00:00 Every 15min: 2025-11-18 14:45 === Time Until Next Run === Next daily backup (2 AM): Runs at: 2025-11-19 02:00 In: 11h 29m === Check if Should Run Now === Current minute: 30 Hourly job: not yet Current hour: 14 Daily 2AM job: not yet Weekday check: YES
10
Complete Date/Time Library

A reusable library of date/time functions for scripts.

#!/bin/bash
# Complete date/time function library

#######################################
# Date Parsing and Formatting
#######################################

# Parse any date to epoch
to_epoch() {
    date -d "$1" '+%s' 2>/dev/null
}

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

# Format date
format_date() {
    local input=$1
    local format=$2
    date -d "$input" "+$format"
}

#######################################
# Date Arithmetic
#######################################

# Add days to date
add_days() {
    local d=$1 n=$2
    date -d "$d +$n days" '+%Y-%m-%d'
}

# Subtract days
sub_days() {
    local d=$1 n=$2
    date -d "$d -$n days" '+%Y-%m-%d'
}

# Days between dates
days_between() {
    local d1=$1 d2=$2
    local e1=$(to_epoch "$d1")
    local e2=$(to_epoch "$d2")
    echo $(( (e2 - e1) / 86400 ))
}

# Add months
add_months() {
    local d=$1 n=$2
    date -d "$d +$n months" '+%Y-%m-%d'
}

#######################################
# Date Checks
#######################################

# Is valid date
is_valid_date() {
    date -d "$1" &>/dev/null
}

# Is weekend
is_weekend() {
    local dow=$(date -d "$1" '+%u')
    ((dow >= 6))
}

# Is in range
date_in_range() {
    local d=$1 start=$2 end=$3
    local de=$(to_epoch "$d")
    local se=$(to_epoch "$start")
    local ee=$(to_epoch "$end")
    ((de >= se && de <= ee))
}

#######################################
# Calendar Functions
#######################################

# First of month
first_of_month() {
    local d=${1:-$(date '+%Y-%m-%d')}
    date -d "$(date -d "$d" '+%Y-%m-01')" '+%Y-%m-%d'
}

# Last of month
last_of_month() {
    local d=${1:-$(date '+%Y-%m-%d')}
    date -d "$(date -d "$d" '+%Y-%m-01') +1 month -1 day" '+%Y-%m-%d'
}

# Days in month
days_in_month() {
    local d=${1:-$(date '+%Y-%m-%d')}
    date -d "$(date -d "$d" '+%Y-%m-01') +1 month -1 day" '+%d'
}

# Quarter of date
quarter_of() {
    local d=${1:-$(date '+%Y-%m-%d')}
    local month=$(date -d "$d" '+%-m')
    echo $(( (month - 1) / 3 + 1 ))
}

#######################################
# Duration Formatting
#######################################

# Format seconds as readable
format_duration() {
    local secs=$1
    local d=$((secs / 86400))
    local h=$(( (secs % 86400) / 3600 ))
    local m=$(( (secs % 3600) / 60 ))
    local s=$((secs % 60))
    
    if ((d > 0)); then
        printf "%dd %dh %dm" $d $h $m
    elif ((h > 0)); then
        printf "%dh %dm %ds" $h $m $s
    elif ((m > 0)); then
        printf "%dm %ds" $m $s
    else
        printf "%ds" $s
    fi
}

#######################################
# Demo
#######################################

echo "========================================"
echo "    Date/Time Library Demo"
echo "========================================"

echo ""
echo "=== Parsing and Formatting ==="
echo "to_epoch 'Nov 18, 2025': $(to_epoch 'Nov 18, 2025')"
echo "from_epoch 1731958245: $(from_epoch 1731958245)"
echo "format_date 'tomorrow' '%A %B %d': $(format_date 'tomorrow' '%A %B %d')"

echo ""
echo "=== Date Arithmetic ==="
echo "add_days '2025-11-18' 10: $(add_days '2025-11-18' 10)"
echo "sub_days '2025-11-18' 30: $(sub_days '2025-11-18' 30)"
echo "days_between '2025-01-01' '2025-11-18': $(days_between '2025-01-01' '2025-11-18')"
echo "add_months '2025-11-18' 3: $(add_months '2025-11-18' 3)"

echo ""
echo "=== Date Checks ==="
is_valid_date "2025-11-18" && echo "2025-11-18: valid" || echo "2025-11-18: invalid"
is_valid_date "2025-02-30" && echo "2025-02-30: valid" || echo "2025-02-30: invalid"
is_weekend "2025-11-22" && echo "2025-11-22: weekend" || echo "2025-11-22: weekday"

echo ""
echo "=== Calendar Functions ==="
echo "first_of_month: $(first_of_month)"
echo "last_of_month: $(last_of_month)"
echo "days_in_month: $(days_in_month)"
echo "quarter_of: Q$(quarter_of)"

echo ""
echo "=== Duration Formatting ==="
echo "3661 seconds: $(format_duration 3661)"
echo "90061 seconds: $(format_duration 90061)"

echo ""
echo "========================================"
Output: ======================================== Date/Time Library Demo ======================================== === Parsing and Formatting === to_epoch 'Nov 18, 2025': 1731909600 from_epoch 1731958245: 2025-11-18 14:30:45 format_date 'tomorrow' '%A %B %d': Wednesday November 19 === Date Arithmetic === add_days '2025-11-18' 10: 2025-11-28 sub_days '2025-11-18' 30: 2025-10-19 days_between '2025-01-01' '2025-11-18': 321 add_months '2025-11-18' 3: 2026-02-18 === Date Checks === 2025-11-18: valid 2025-02-30: invalid 2025-11-22: weekend === Calendar Functions === first_of_month: 2025-11-01 last_of_month: 2025-11-30 days_in_month: 30 quarter_of: Q4 === Duration Formatting === 3661 seconds: 1h 1m 1s 90061 seconds: 1d 1h 1m ========================================

Quick Reference

Essential Commands

# Current date/time
date                          # Default format
date '+%Y-%m-%d'              # ISO date
date '+%H:%M:%S'              # Time
date '+%s'                    # Epoch

# Parse dates
date -d "Nov 18, 2025"        # Parse string
date -d @1731958245           # From epoch
date -d "2025-11-18 14:30"    # With time

# Arithmetic
date -d "tomorrow"
date -d "+3 days"
date -d "-1 week"
date -d "next monday"
date -d "last month"

# Timezone
TZ=UTC date
TZ=America/Los_Angeles date

Common Patterns

TaskCommand
ISO timestampdate +%Y-%m-%dT%H:%M:%S%z
Filename safedate +%Y%m%d_%H%M%S
Days between$(( ($(date -d "$d2" +%s) - $(date -d "$d1" +%s)) / 86400 ))
Is weekend(($(date -d "$d" +%u) >= 6))
Last of monthdate -d "$(date +%Y-%m-01) +1 month -1 day"
Validatedate -d "$input" &>/dev/null
βœ“ Best Practices
βœ— Common Mistakes
← Back to BashSpecial Index ↑ Back to EXPANDED