シェルのタイムゾーンを一時変更するときは、TZの値だけでなく「変数がsetかunsetか」を保存します。元がsetなら同じ値をexportし直し、元がunsetならunsetへ戻します。system timezoneは変更せず、shell表示のzoneとUTC offsetまで復旧条件にします。
set状態とexact valueを保存する
${TZ-<unset>}の表示文字列をbaseline値として使うと、本当の文字列とunsetを区別できません。Bashの[[ -v TZ ]]で状態を保存し、値、%Z:%z、timedatectlのsystem zoneを別fieldとして記録します。4ブロックは同じshellで実行します。
set -Eeuo pipefail
if [[ -v TZ ]]; then
tz_was_set=1
tz_saved=$TZ
else
tz_was_set=0
tz_saved=''
fi
tz_saved_zone=$(date '+%Z:%z')
system_zone_before=$(timedatectl show --property=Timezone --value)
printf 'TZ_was_set=%s TZ_value=%q shell_zone=%s system_zone=%s\n' "$tz_was_set" "$tz_saved" "$tz_saved_zone" "$system_zone_before"
現在のshellだけを一時変更する
export TZ=Asia/Tokyoは現在shellとそこから起動する子processだけに作用します。JST:+0900を確認し、timedatectlの値がbaselineから変わっていないことを必須にします。
export TZ=Asia/Tokyo
test "$TZ" = 'Asia/Tokyo'
test "$(date '+%Z:%z')" = 'JST:+0900'
test "$(timedatectl show --property=Timezone --value)" = "$system_zone_before"
printf 'temporary_TZ=%s display=%s system_unchanged=%s\n' "$TZ" "$(date --iso-8601=seconds)" "$system_zone_before"
unsetとUTCの2 fixtureを検証する
subshell内で初期unsetと初期TZ=UTCの両方を作り、一時変更後にset状態、exact value、dateのzone/offsetがbaselineへ戻ることを自動検証します。subshellなので外側のTZは変更しません。
verify_restore_case() (
case $1 in
unset) unset TZ ;;
UTC) export TZ=UTC ;;
*) return 90 ;;
esac
if [[ -v TZ ]]; then saved_set=1; saved_value=$TZ; else saved_set=0; saved_value=''; fi
saved_zone=$(date '+%Z:%z')
export TZ=Asia/Tokyo
if test "$saved_set" -eq 1; then export TZ="$saved_value"; else unset TZ; fi
if test "$saved_set" -eq 1; then [[ -v TZ ]] && test "$TZ" = "$saved_value"; else [[ ! -v TZ ]]; fi
test "$(date '+%Z:%z')" = "$saved_zone"
)
verify_restore_case unset
verify_restore_case UTC
printf 'fixture_restore=pass cases=unset,UTC\n'
実shellをexact restoreする
保存したstateに従い、setなら空文字を含む同値をexportし、unsetならunsetします。値だけでなく[[ -v TZ ]]、%Z:%z、system timezoneが一致した場合だけ復旧完了です。
if test "$tz_was_set" -eq 1; then
export TZ="$tz_saved"
else
unset TZ
fi
if test "$tz_was_set" -eq 1; then
[[ -v TZ ]] && test "$TZ" = "$tz_saved"
else
[[ ! -v TZ ]]
fi
test "$(date '+%Z:%z')" = "$tz_saved_zone"
test "$(timedatectl show --property=Timezone --value)" = "$system_zone_before"
printf 'restored TZ_set=%s TZ_value=%q shell_zone=%s system_zone=%s\n' "$tz_was_set" "$tz_saved" "$(date '+%Z:%z')" "$system_zone_before"
受入条件
初期unsetでは復旧後も[[ ! -v TZ ]]、初期UTCではTZ=UTCかつUTC:+0000です。実環境では保存した任意値とzone/offsetへ戻り、timedatectlは全工程で不変です。
system設定と混同しない
対話shellのexportはcron、既存service、別terminalを変更しません。system timezone変更が要件なら、影響範囲、DST、service restartを別の承認作業として設計し、この一時表示手順へ混ぜないでください。
one-command scopeで子processへの継承を切り分ける
TZ=値 commandの形式なら、親shellのTZを変更せず、そのcommandと子processだけにtimezoneを渡せます。比較時刻は各commandの実行時刻ではなく同じepoch秒へ固定し、UTCとAsia/Tokyoの表示差だけを測ります。これにより処理時間の差をtimezone差と誤認しません。実行前後には親のset/unset状態、exact value、timedatectlのsystem timezoneを再照合し、shell scopeの検証をsystem変更の証跡として扱わないことが重要です。
epoch=$(date +%s)
if [[ -v TZ ]]; then parent_tz_set=1; parent_tz_value=$TZ; else parent_tz_set=0; parent_tz_value=''; fi
parent_system_zone=$(timedatectl show --property=Timezone --value)
for zone in UTC Asia/Tokyo; do
test -e "/usr/share/zoneinfo/$zone"
TZ="$zone" date --date="@$epoch" '+scope=child epoch=%s zone=%Z offset=%z iso=%Y-%m-%dT%H:%M:%S%:z'
done
if test "$parent_tz_set" -eq 1; then [[ -v TZ ]] && test "$TZ" = "$parent_tz_value"; else [[ ! -v TZ ]]; fi
test "$(timedatectl show --property=Timezone --value)" = "$parent_system_zone"
printf 'parent_TZ_set=%s parent_TZ_value=%q system_zone=%s
' "$parent_tz_set" "$parent_tz_value" "$parent_system_zone"
DST境界は略称ではなくoffsetとepochで判定する
地域timezoneには夏時間や過去の規則が含まれるため、JSTのような固定offsetの例だけで地域timezone全般を判定できません。運用対象の地域は、切替前後を含む既知のepoch秒を入力にして、%zまたはISO 8601の数値offsetと日時を記録します。%Zの略称は表示用とし、同じ略称が別地域で使われる可能性を考慮します。将来時刻の検証では、そのhostに入っているtimezone databaseの版も作業記録へ残します。
zone=America/New_York
test -e "/usr/share/zoneinfo/$zone"
for instant in 2026-01-15T12:00:00Z 2026-07-15T12:00:00Z; do
epoch=$(date --date="$instant" +%s)
TZ="$zone" date --date="@$epoch" '+input_epoch=%s local=%Y-%m-%dT%H:%M:%S%:z label=%Z'
done
serviceやsystem全体の変更とは作業票を分ける
export TZは現在のshellと、その後に起動した子processへ継承されます。すでに起動済みのservice、別login session、container、system timezoneが同時に変わるわけではありません。常駐serviceへ適用する場合は、そのservice managerの環境設定、再起動可否、影響範囲、rollbackを別の承認作業として設計します。本手順の合格条件は親shellのexact restoreとsystem timezone不変であり、serviceの変更完了ではありません。unsetだった状態と空文字を含むset状態は別物なので、値だけでなくset/unsetも保存します。
公式情報・参考資料
シェルのタイムゾーン確認・設定の構文と制約は、本文末の一次資料と対象環境のlocal helpで照合します。シェルのタイムゾーン確認・設定の記事確認日は2026年7月17日で、版が異なる場合はoption、default、終了statusの差を先に確認してください。

コメント