Tutorial
Python でのタイムゾーンの操作: 完全ガイド
はじめに
Python でのタイムゾーンの処理は難しい場合がありますが、異なるタイムゾーンにまたがって動作する堅牢なアプリケーションを構築するには不可欠です。このチュートリアルでは、基本概念から高度なテクニックまで、Python でのタイムゾーンの操作について知っておく必要があるすべてを説明します。
タイムゾーンの処理が重要な理由
# ❌ BAD: Naive datetime (no timezone info)
from datetime import datetime
now = datetime.now() # Which timezone is this?
# ✅ GOOD: Timezone-aware datetime
from datetime import datetime, timezone
now = datetime.now(timezone.utc) # Clear: UTC time
単純な日時に関する一般的な問題:
- DST 移行中のあいまいな時間
- タイムゾーンをまたいだ不正確な計算
- 分散システムでのデータ破損
- デバッグが難しいタイムゾーンのバグ
Python タイムゾーン ライブラリの概要
1. 日時 (組み込み)
Python 3.2 以降には、基本的なタイムゾーンのサポートが含まれています。
from datetime import datetime, timezone, timedelta
# UTC timezone
utc_now = datetime.now(timezone.utc)
print(utc_now) # 2025-01-15 10:30:00+00:00
# Fixed offset timezone
est = timezone(timedelta(hours=-5))
est_now = datetime.now(est)
print(est_now) # 2025-01-15 05:30:00-05:00
長所:
- 内蔵されており、インストールは必要ありません
- UTC と固定オフセットのシンプルな
短所:
- 名前付きタイムゾーンはサポートされません (例: "America/New_York")
- DST を自動的に処理できない
- 制限された機能
2. zoneinfo (組み込み、Python 3.9 以降)
Python 3.9 以降を推奨
from datetime import datetime
from zoneinfo import ZoneInfo
# Named timezone support
ny_time = datetime.now(ZoneInfo("America/New_York"))
tokyo_time = datetime.now(ZoneInfo("Asia/Tokyo"))
print(f"New York: {ny_time}")
print(f"Tokyo: {tokyo_time}")
長所:
- 組み込み (Python 3.9 以降)
- IANA タイムゾーン データベースのサポート
- 自動 DST 処理
- タイプセーフでモダン
短所:
- Python 3.9以降でのみ利用可能
- システムのタイムゾーン データ (または
tzdataパッケージ) が必要です
3. pytz (サードパーティ)
Python 3.9 未満または最大の互換性に最適
import pytz
from datetime import datetime
# Create timezone-aware datetime
utc = pytz.UTC
eastern = pytz.timezone('US/Eastern')
# Current time in timezone
ny_time = datetime.now(eastern)
print(ny_time)
長所:
- Python 2.7以降で動作します
- 包括的なタイムゾーンデータベース
- 十分にテストされており安定しています
短所:
- インストールが必要です (「pip install pytz」)
- もう少し複雑な API -zoneinfo に取って代わられる
4. dateutil (サードパーティ)
from dateutil import tz
from datetime import datetime
# Get timezone
eastern = tz.gettz('America/New_York')
utc = tz.UTC
# Create datetime
dt = datetime.now(eastern)
長所:
- 強力な日付解析
- 簡単なタイムゾーン変換
- ローカルタイムゾーンで動作します
短所:
- より大きな依存関係
- 単純なタイムゾーン作業にはやりすぎ
タイムゾーン対応の日付時刻の作成
ゾーン情報の使用 (Python 3.9 以降)
from datetime import datetime
from zoneinfo import ZoneInfo
# Method 1: Create with timezone
dt = datetime(2025, 1, 15, 14, 30, tzinfo=ZoneInfo("America/New_York"))
# Method 2: Get current time with timezone
now = datetime.now(ZoneInfo("America/New_York"))
# Method 3: Replace timezone on naive datetime
naive_dt = datetime(2025, 1, 15, 14, 30)
aware_dt = naive_dt.replace(tzinfo=ZoneInfo("America/New_York"))
pytz の使用
import pytz
from datetime import datetime
# Method 1: Use localize() for naive datetimes
eastern = pytz.timezone('US/Eastern')
naive_dt = datetime(2025, 1, 15, 14, 30)
aware_dt = eastern.localize(naive_dt)
# Method 2: Get current time (use UTC, then convert)
utc_now = datetime.now(pytz.UTC)
eastern_now = utc_now.astimezone(eastern)
# ❌ WRONG with pytz!
wrong_dt = datetime(2025, 1, 15, 14, 30, tzinfo=eastern)
# This bypasses DST handling!
pytz の重要な注意点: tzinfo を直接渡すのではなく、常に localize() を使用してください。
タイムゾーン間の変換
基本的な変換
from datetime import datetime
from zoneinfo import ZoneInfo
# Create datetime in one timezone
ny_time = datetime(2025, 1, 15, 14, 30, tzinfo=ZoneInfo("America/New_York"))
# Convert to another timezone
tokyo_time = ny_time.astimezone(ZoneInfo("Asia/Tokyo"))
london_time = ny_time.astimezone(ZoneInfo("Europe/London"))
utc_time = ny_time.astimezone(ZoneInfo("UTC"))
print(f"New York: {ny_time}") # 2025-01-15 14:30:00-05:00
print(f"Tokyo: {tokyo_time}") # 2025-01-16 04:30:00+09:00
print(f"London: {london_time}") # 2025-01-15 19:30:00+00:00
print(f"UTC: {utc_time}") # 2025-01-15 19:30:00+00:00
変換ヘルパー関数
from datetime import datetime
from zoneinfo import ZoneInfo
def convert_timezone(dt, from_tz, to_tz):
"""
Convert datetime between timezones
Args:
dt: datetime object (naive or aware)
from_tz: Source timezone string (e.g., 'America/New_York')
to_tz: Target timezone string (e.g., 'Asia/Tokyo')
Returns:
Timezone-aware datetime in target timezone
"""
# If datetime is naive, localize it first
if dt.tzinfo is None:
dt = dt.replace(tzinfo=ZoneInfo(from_tz))
# Convert to target timezone
return dt.astimezone(ZoneInfo(to_tz))
# Usage
naive_dt = datetime(2025, 6, 15, 14, 30)
tokyo_time = convert_timezone(naive_dt, "America/New_York", "Asia/Tokyo")
print(tokyo_time) # 2025-06-16 03:30:00+09:00
DST 移行の処理
夏時間 (DST) は、曖昧で存在しない時間を作成します。
あいまいな時間 (フォールバック)
クロックが「フォールバック」する (DST が終了する) と、1 つのクロック時間が 2 回発生します。
from datetime import datetime
from zoneinfo import ZoneInfo
import pytz
# November 5, 2023, 01:30 AM happens TWICE in US/Eastern
# Once in EDT (UTC-4), once in EST (UTC-5)
# With zoneinfo (Python 3.9+)
tz = ZoneInfo("America/New_York")
# Create the ambiguous time
dt = datetime(2023, 11, 5, 1, 30, tzinfo=tz)
print(dt) # Uses the first occurrence (DST)
# With pytz - explicit control
eastern = pytz.timezone('US/Eastern')
# First occurrence (DST, UTC-4)
dt_dst = eastern.localize(datetime(2023, 11, 5, 1, 30), is_dst=True)
print(f"DST: {dt_dst}") # 2023-11-05 01:30:00-04:00
# Second occurrence (Standard, UTC-5)
dt_std = eastern.localize(datetime(2023, 11, 5, 1, 30), is_dst=False)
print(f"Standard: {dt_std}") # 2023-11-05 01:30:00-05:00
存在しない時代 (スプリングフォワード)
時計が「早送り」される (DST が始まる) と、いくつかの時間が存在しません。
from datetime import datetime
from zoneinfo import ZoneInfo
import pytz
# March 10, 2024, 02:30 AM doesn't exist in US/Eastern
# Clocks jump from 02:00 AM to 03:00 AM
# With zoneinfo - automatically adjusts forward
tz = ZoneInfo("America/New_York")
dt = datetime(2024, 3, 10, 2, 30, tzinfo=tz)
print(dt) # Automatically becomes 03:30
# With pytz - raises error by default
eastern = pytz.timezone('US/Eastern')
try:
dt = eastern.localize(datetime(2024, 3, 10, 2, 30))
except pytz.exceptions.NonExistentTimeError:
print("This time doesn't exist!")
# Handle non-existent time explicitly
dt = eastern.localize(datetime(2024, 3, 10, 2, 30), is_dst=None)
# Returns the next valid time
ベストプラクティス
1. タイムスタンプを常に UTC で保存する
from datetime import datetime, timezone
# ✅ GOOD: Store in UTC
def save_event(event_time):
utc_time = event_time.astimezone(timezone.utc)
database.save(utc_time)
return utc_time
# ✅ GOOD: Display in user's timezone
def display_event(utc_time, user_timezone):
local_time = utc_time.astimezone(ZoneInfo(user_timezone))
return local_time.strftime("%Y-%m-%d %H:%M:%S %Z")
2. シリアル化には ISO 8601 形式を使用する
from datetime import datetime
from zoneinfo import ZoneInfo
dt = datetime.now(ZoneInfo("America/New_York"))
# ✅ GOOD: ISO 8601 with timezone
iso_string = dt.isoformat()
print(iso_string) # 2025-01-15T14:30:00-05:00
# Parse back
parsed_dt = datetime.fromisoformat(iso_string)
3. 本番環境では単純な日時を決して使用しないでください
# ❌ BAD: Naive datetime
naive = datetime.now()
# ✅ GOOD: Always timezone-aware
aware = datetime.now(timezone.utc)
4. 計算には UTC を使用します
from datetime import datetime, timedelta, timezone
# ✅ GOOD: Calculate in UTC
start_utc = datetime.now(timezone.utc)
end_utc = start_utc + timedelta(hours=24)
# Then convert to local timezone for display
local_end = end_utc.astimezone(ZoneInfo("America/New_York"))
一般的なエラーと解決策
エラー 1: 単純な日付時刻と認識された日付時刻を使用した算術演算
# ❌ ERROR: Can't mix naive and aware
naive = datetime.now()
aware = datetime.now(timezone.utc)
# difference = aware - naive # TypeError!
# ✅ SOLUTION: Make both timezone-aware
naive_aware = naive.replace(tzinfo=timezone.utc)
difference = aware - naive_aware
エラー 2: pytz の使用法が正しくありません
import pytz
# ❌ WRONG
eastern = pytz.timezone('US/Eastern')
dt = datetime(2025, 1, 15, 14, 30, tzinfo=eastern)
# ✅ CORRECT
dt = eastern.localize(datetime(2025, 1, 15, 14, 30))
エラー 3: ローカル タイムゾーンを想定している
# ❌ BAD: Assumes server timezone
dt = datetime.now() # Which timezone?
# ✅ GOOD: Explicit timezone
dt = datetime.now(timezone.utc)
実用的な例: 会議スケジューラ
from datetime import datetime
from zoneinfo import ZoneInfo
class MeetingScheduler:
"""Schedule meetings across timezones"""
def __init__(self):
self.meetings = []
def schedule_meeting(self, date_str, time_str, timezone_str, duration_hours):
"""
Schedule a meeting in a specific timezone
Args:
date_str: Date as 'YYYY-MM-DD'
time_str: Time as 'HH:MM'
timezone_str: IANA timezone (e.g., 'America/New_York')
duration_hours: Meeting duration in hours
"""
# Parse date and time
year, month, day = map(int, date_str.split('-'))
hour, minute = map(int, time_str.split(':'))
# Create timezone-aware datetime
tz = ZoneInfo(timezone_str)
meeting_time = datetime(year, month, day, hour, minute, tzinfo=tz)
# Convert to UTC for storage
meeting_utc = meeting_time.astimezone(ZoneInfo("UTC"))
meeting = {
'start_utc': meeting_utc,
'timezone': timezone_str,
'duration': duration_hours
}
self.meetings.append(meeting)
return meeting
def get_meeting_time(self, meeting, display_timezone):
"""Get meeting time in any timezone"""
tz = ZoneInfo(display_timezone)
local_time = meeting['start_utc'].astimezone(tz)
return {
'time': local_time.strftime("%Y-%m-%d %H:%M %Z"),
'timezone': display_timezone
}
# Usage example
scheduler = MeetingScheduler()
# Schedule meeting in New York
meeting = scheduler.schedule_meeting(
'2025-02-15', '14:00', 'America/New_York', 1
)
# Display for different participants
print("Meeting times:")
print(f" New York: {scheduler.get_meeting_time(meeting, 'America/New_York')['time']}")
print(f" London: {scheduler.get_meeting_time(meeting, 'Europe/London')['time']}")
print(f" Tokyo: {scheduler.get_meeting_time(meeting, 'Asia/Tokyo')['time']}")
出力:
Meeting times:
New York: 2025-02-15 14:00 EST
London: 2025-02-15 19:00 GMT
Tokyo: 2025-02-16 04:00 JST
タイムゾーンコードのテスト
import unittest
from datetime import datetime
from zoneinfo import ZoneInfo
class TestTimezoneConversion(unittest.TestCase):
def test_utc_to_eastern(self):
"""Test UTC to Eastern conversion"""
utc_time = datetime(2025, 1, 15, 19, 30, tzinfo=ZoneInfo("UTC"))
eastern_time = utc_time.astimezone(ZoneInfo("America/New_York"))
# In January, Eastern is UTC-5 (EST)
self.assertEqual(eastern_time.hour, 14)
self.assertEqual(eastern_time.minute, 30)
def test_dst_transition(self):
"""Test DST transition handling"""
# Before DST (March 10, 2024, 1:00 AM)
before_dst = datetime(2024, 3, 10, 1, 0, tzinfo=ZoneInfo("America/New_York"))
# After DST (March 10, 2024, 3:00 AM - 2:00 AM doesn't exist)
after_dst = datetime(2024, 3, 10, 3, 0, tzinfo=ZoneInfo("America/New_York"))
# Difference should be 1 hour in local time, 2 hours in absolute time
diff = after_dst - before_dst
self.assertEqual(diff.total_seconds(), 3600) # 1 hour
if __name__ == '__main__':
unittest.main()
関連ツールとリソース
タイムゾーンを操作するには、無料のタイムスタンプ ツールを使用します。
- UTC/ローカル コンバータ - UTC と現地時間間の変換
- タイムゾーン ミーティング プランナー - タイムゾーンをまたいでスケジュールを設定する
- 現在のタイムスタンプ - 複数のタイムゾーンの現在時刻を取得します
- Python タイムスタンプの例 - その他の Python コード例
概要
重要なポイント:
- 運用コードでは常にタイムゾーン対応の日時を使用する
- タイムスタンプを UTC で保存、表示用にローカルに変換します
- zoneinfo (Python 3.9+) または古いバージョンの場合は pytz を使用します
- 必要に応じて DST 移行を明示的に処理
- タイムゾーン コードを徹底的にテストします (特に DST 付近)。
- 現地のタイムゾーンを想定しないでください - 常に明示的にしてください
クイックリファレンス:
# Modern Python (3.9+)
from datetime import datetime
from zoneinfo import ZoneInfo
# Current UTC time
utc_now = datetime.now(ZoneInfo("UTC"))
# Current local time
local_now = datetime.now(ZoneInfo("America/New_York"))
# Convert between timezones
tokyo_time = local_now.astimezone(ZoneInfo("Asia/Tokyo"))
# ISO 8601 format (with timezone)
iso_string = tokyo_time.isoformat()
これらのテクニックを使用すると、Python アプリケーションで自信を持ってタイムゾーンを処理できるようになります。
最終更新日: 2025 年 1 月