首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Shell 日期運算實戰:天數加減、月末計算與跨平台技巧

     編輯:關於C語言

核心工具:cal 與 date

Shell 日期運算常借助 cal 取得月天數,date 做格式化與時間戳轉換。經典片段:

aaa=$(cal $mm $yy)
bbb=$(echo $aaa | awk '{print $NF}')  # 取得該月天數

常用運算函數庫

1. 計算 N 天前的日期(YYYYMMDD)

prev_date() {
    local str=$1 days=$2
    local yy=${str:0:4} mm=${str:4:2} dd=${str:6:2}
    days=$((days - dd))
    while (( days >= 0 )); do
        ((mm--))
        if (( mm == 0 )); then mm=12; ((yy--)); fi
        local dim=$(cal $mm $yy | awk '{print $NF}')
        days=$((days - dim))
    done
    dd=$((0 - days))
    printf "%04d%02d%02d" $yy $mm $dd
}

2. 計算 N 天后的日期(修正版,支援跨年)

next_date() {
    local str=$1 days=$2
    local yy=${str:0:4} mm=${str:4:2} dd=${str:6:2}
    days=$((days + dd))
    while (( days > 0 )); do
        local dim=$(cal $mm $yy | awk '{print $NF}')
        if (( days <= dim )); then
            dd=$days; days=0
        else
            days=$((days - dim))
            ((mm++))
            if (( mm > 12 )); then mm=1; ((yy++)); fi
        fi
    done
    printf "%04d%02d%02d" $yy $mm $dd
}

3. 取得上個月最後一天

last_day_prev_month() {
    local yy=$(date +%Y) mm=$(($(date +%m) - 1))
    if (( mm == 0 )); then mm=12; ((yy--)); fi
    local dd=$(cal $mm $yy | awk '{print $NF}')
    printf "%04d%02d%02d" $yy $mm $dd
}

4. 計算兩日期相差天數

diff_days() {
    local d1=$1 d2=$2
    local yy1=${d1:0:4} mm1=${d1:4:2} dd1=${d1:6:2}
    local yy2=${d2:0:4} mm2=${d2:4:2} dd2=${d2:6:2}
    local days=$((dd2 - dd1))
    while (( yy2 != yy1 || mm2 != mm1 )); do
        ((mm2--))
        if (( mm2 == 0 )); then mm2=12; ((yy2--)); fi
        local dim=$(cal $mm2 $yy2 | awk '{print $NF}')
        days=$((days + dim))
    done
    echo $days
}

現代最佳實踐:直接用 date(GNU/BSD)

若系統支援 GNU date(Linux)或 BSD date(macOS),**強烈建議直接使用內建運算**,簡潔且無 Bug:

# Linux (GNU date)
date -d "20230101 5 days" +%Y%m%d      # 5 天后
date -d "20230101 5 days ago" +%Y%m%d  # 5 天前
date -d "last day" +%Y%m%d             # 昨天
date -d "$(date +%Y%m01) 1 day ago" +%Y%m%d  # 上月最後一天
# 兩日期差天數
echo $(( ($(date -d "20230202" +%s) - $(date -d "20230101" +%s)) / 86400 ))

# macOS (BSD date)
date -v+5d -j -f "%Y%m%d" "20230101" +%Y%m%d
date -v-5d -j -f "%Y%m%d" "20230101" +%Y%m%d
date -v-1d +%Y%m%d
date -v1d -v-1m -j -f "%Y%m%d" "$(date +%Y%m01)" +%Y%m%d

時間戳通用法(POSIX 相容)

date_by_interval() {
    local day=$1 interval=$2
    local daysec=$(date -d "$day" +%s 2>/dev/null || date -j -f "%Y%m%d" "$day" +%s)
    local resultsec=$((daysec + interval * 86400))
    date -d "@$resultsec" +%Y%m%d 2>/dev/null || date -r $resultsec +%Y%m%d
}
# 用法:date_by_interval 20230101 7  -> 20230108
#       date_by_interval 20230101 -7 -> 20221225

總結建議

  • 優先使用系統 date 命令,避免手寫邏輯 Bug(閏年、跨月、跨年極易出錯)。
  • 腳本需跨平台(Linux/macOS/Unix)時,封裝 date_by_interval 等函數,內部判斷 date 版本調用對應參數。
  • 若環境極受限(嵌入式無 date),再啟用基於 cal 的純 Shell 實現,並務必加上完整測試用例(含閏年、世紀年、跨世紀)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved