もうdatetimeで消耗しない!Arrowでスマートなコードを。
Pythonで日付や時刻を扱う際、標準ライブラリのdatetime
モジュールを使うことが多いでしょう。しかし、datetime
は時に冗長で、タイムゾーンの扱いなどが直感的でない場面もあります。例えば、複数のモジュール (datetime
, time
, calendar
, dateutil
, pytz
など) をインポートする必要があったり、多様な型 (date
, time
, datetime
, tzinfo
, timedelta
, relativedelta
など) を使い分ける必要があったりします。さらに、タイムゾーンを意識しないNaiveなオブジェクトがデフォルトであったり、ISO 8601形式のパースや相対時間の表現(Humanization)などの機能が不足しているという課題もあります。
そこで登場するのがArrowライブラリです!🎉 Arrowは、Pythonにおける日付・時刻・タイムスタンプの作成、操作、フォーマット、変換を、より人間にとって分かりやすく、直感的に行えるように設計されたライブラリです。datetime
の機能を完全に置き換え可能でありながら、より少ないコード量で、より多くのことを実現できます。
Arrowは、JavaScriptの有名な日付ライブラリであるMoment.jsや、PythonのHTTPライブラリであるRequestsに強くインスパイアされており、「時間の矢(Arrow of Time)」という概念から名付けられました。この記事では、Arrowの基本的な使い方から便利な機能まで、詳しく解説していきます。
Arrowの公式ドキュメントはこちらで確認できます。
🎯 Arrowの主な特徴
datetime
の完全な代替: 標準ライブラリの機能を網羅し、より使いやすく拡張されています。- Python 3.8以上をサポート: 最新のPython環境に対応しています。(過去バージョンでは古いPythonもサポートしていました)
- タイムゾーン対応が標準: デフォルトでUTCを扱い、タイムゾーン変換も容易です。
- シンプルなオブジェクト生成: 様々な入力形式(文字列、タイムスタンプ、datetimeオブジェクトなど)から簡単にArrowオブジェクトを作成できます。
shift
メソッド: 週を含む相対的な時間移動(加算・減算)が直感的に行えます。- 自動フォーマット・パース: 文字列のフォーマット指定やパース(解析)が簡単です。
- ISO 8601形式の強力なサポート: 国際標準形式の扱いに優れています。
- 他のタイムゾーンライブラリとの互換性:
dateutil
,pytz
,ZoneInfo
(Python 3.9+) のタイムゾーンオブジェクトをサポートしています。 - 期間・範囲の生成: マイクロ秒から年までの時間単位で、期間(span)、範囲(range)、切り捨て(floor)、切り上げ(ceiling)を生成できます。
- Humanization(相対時間表現): 「1時間前」「明日」のような人間にとって分かりやすい形式で表示できます。多言語対応も進んでいます。
- 拡張性: 独自のArrow派生型を作成できます。
- 型ヒントの完全サポート: PEP 484スタイルの型ヒントに対応しており、静的解析ツールとの連携もスムーズです。
💻 インストール
Arrowのインストールはpipを使って簡単に行えます。最新版をインストールするには-U
オプションを付けると良いでしょう。
pip install -U arrow
もしpipenv
を使っている場合は、以下のコマンドでインストールできます。
pipenv install arrow
🚀 基本的な使い方
Arrowを使い始めるのは非常に簡単です。datetime
を使う場合、複数のモジュールから必要なクラスをインポートすることが多いですが、Arrowでは基本的にarrow
モジュールをインポートするだけで済みます。
import arrow
現在時刻の取得
現在のUTC時刻を取得するにはutcnow()
を、ローカルタイムゾーンでの現在時刻を取得するにはnow()
を使います。
# 現在のUTC時刻を取得
utc_now = arrow.utcnow()
print(f"UTC: {utc_now}")
# 現在のローカル時刻を取得 (システムのタイムゾーン設定に依存)
local_now = arrow.now()
print(f"Local: {local_now}")
# 特定のタイムゾーンでの現在時刻を取得
tokyo_now = arrow.now('Asia/Tokyo')
print(f"Tokyo: {tokyo_now}")
出力例 (実行日時によって異なります):
UTC: 2025-04-05T11:26:00.123456+00:00
Local: 2025-04-05T20:26:00.123456+09:00
Tokyo: 2025-04-05T20:26:00.123456+09:00
Arrowオブジェクトはデフォルトでタイムゾーン情報を持っている(タイムゾーンアウェアである)ことに注目してください。utcnow()
は+00:00
(UTC) を、now()
や特定のタイムゾーンを指定した場合はそのオフセット (例: +09:00
) を含んでいます。
Arrowオブジェクトの生成 (arrow.get())
arrow.get()
関数は非常に強力で、様々な入力からArrowオブジェクトを賢く生成してくれます。
文字列から生成:
ISO 8601形式など、一般的な日付・時刻フォーマットの文字列は自動的に解釈されます。
# ISO 8601形式の文字列
dt1 = arrow.get('2023-10-26T15:30:00+09:00')
print(dt1)
# 日付のみ
dt2 = arrow.get('2023-11-01')
print(dt2) # 時刻は00:00:00、タイムゾーンはUTCになる
# タイムゾーン情報がない文字列 (デフォルトでUTCとして解釈)
dt3 = arrow.get('2023/12/25 09:00:00')
print(dt3)
出力例:
<Arrow [2023-10-26T15:30:00+09:00]>
<Arrow [2023-11-01T00:00:00+00:00]>
<Arrow [2023-12-25T09:00:00+00:00]>
特定のフォーマットを指定してパースすることも可能です。
# フォーマットを指定
dt4 = arrow.get('15-Mar-2024 10:20:30 PM', 'DD-MMM-YYYY hh:mm:ss A')
print(dt4)
出力例:
<Arrow [2024-03-15T22:20:30+00:00]>
タイムスタンプから生成:
Unixタイムスタンプ(数値)からも生成できます。
# Unixタイムスタンプ (秒)
ts = 1678886400 # 2023-03-15 13:20:00 UTC
dt5 = arrow.get(ts)
print(dt5)
# ミリ秒単位のタイムスタンプ (JavaScriptなど)
ts_ms = 1700000000000 # 2023-11-14 22:13:20 UTC
dt6 = arrow.get(ts_ms / 1000) # 秒に変換して渡すか、専用のメソッドを使う(後述)
print(dt6)
# arrow.get()はfloatも受け付ける
dt7 = arrow.get(1678886400.5) # 小数点以下も考慮される
print(dt7)
出力例:
<Arrow [2023-03-15T13:20:00+00:00]>
<Arrow [2023-11-14T22:13:20+00:00]>
<Arrow [2023-03-15T13:20:00.500000+00:00]>
datetimeオブジェクトから生成:
標準のdatetime
オブジェクトやdate
オブジェクトからも生成できます。
from datetime import datetime, date
std_dt = datetime(2024, 1, 1, 12, 0, 0)
arrow_dt = arrow.get(std_dt)
print(arrow_dt) # NaiveなdatetimeはUTCとして扱われる
std_date = date(2024, 5, 5)
arrow_date = arrow.get(std_date)
print(arrow_date) # 時刻は00:00:00 UTC
出力例:
<Arrow [2024-01-01T12:00:00+00:00]>
<Arrow [2024-05-05T00:00:00+00:00]>
年、月、日などを直接指定して生成:
dt8 = arrow.get(2025, 4, 5, 11, 30, 0) # 年, 月, 日, 時, 分, 秒
print(dt8)
出力例:
<Arrow [2025-04-05T11:30:00+00:00]>
Arrowオブジェクトの属性
Arrowオブジェクトは、datetime
オブジェクトと同様の属性(year
, month
, day
, hour
, minute
, second
, microsecond
)や、timestamp
(Unixタイムスタンプ)、tzinfo
(タイムゾーン情報)などにアクセスできます。
a = arrow.get('2024-07-07T10:30:45.123456+09:00')
print(f"Year: {a.year}")
print(f"Month: {a.month}")
print(f"Day: {a.day}")
print(f"Hour: {a.hour}")
print(f"Minute: {a.minute}")
print(f"Second: {a.second}")
print(f"Microsecond: {a.microsecond}")
print(f"Timestamp: {a.timestamp()}") # メソッドとして呼び出す (float)
print(f"Float Timestamp: {a.float_timestamp}") # プロパティとしてもアクセス可能
print(f"Timezone Info: {a.tzinfo}")
print(f"Timezone Name: {a.tzinfo.key}") # タイムゾーン名 (pytzなどが必要な場合あり)
出力例:
Year: 2024
Month: 7
Day: 7
Hour: 10
Minute: 30
Second: 45
Microsecond: 123456
Timestamp: 1720315845.123456
Float Timestamp: 1720315845.123456
Timezone Info: tzfile('/usr/share/zoneinfo/Asia/Tokyo')
Timezone Name: Asia/Tokyo
Unixタイムスタンプ(Epoch Time)は、UTCでの1970年1月1日0時0分0秒からの経過秒数(浮動小数点数)です。
📜 フォーマットとパース
フォーマット (format())
Arrowオブジェクトを特定の書式文字列に変換するにはformat()
メソッドを使用します。Moment.jsに似たトークンを使用できます。
a = arrow.get('2024-08-15T14:20:05.987+09:00')
# 一般的なフォーマット
print(a.format()) # デフォルトはISO 8601形式
print(a.format('YYYY-MM-DD HH:mm:ss'))
print(a.format('dddd, MMMM Do YYYY, h:mm:ss a ZZ')) # 曜日、月名、序数、12時間制、午前/午後、タイムゾーンオフセット
# Unixタイムスタンプ (秒)
print(a.format('X'))
# Unixタイムスタンプ (ミリ秒)
print(a.format('x'))
# 日本語ロケールでのフォーマット (事前にロケール設定が必要な場合あり)
# import locale
# locale.setlocale(locale.LC_TIME, 'ja_JP.UTF-8') # 環境により設定方法が異なる
# print(a.format('YYYY年M月D日(dddd) HH時mm分ss秒')) # ロケール依存のフォーマット
print(a.format('YYYY年M月D日 HH時mm分ss秒')) # ロケール非依存の書き方
# 事前定義されたフォーマット
print(a.format('ATOM')) # RFC3339 / ISO 8601
print(a.format('COOKIE'))
print(a.format('RFC822'))
print(a.format('RFC3339'))
print(a.format('W3C'))
出力例:
2024-08-15T14:20:05.987000+09:00
2024-08-15 14:20:05
Thursday, August 15th 2024, 2:20:05 pm +09:00
1723701605.987
1723701605987
2024年8月15日 14時20分05秒
2024-08-15T14:20:05+09:00
Thursday, 15-Aug-2024 14:20:05 JST
Thu, 15 Aug 24 14:20:05 +0900
2024-08-15T14:20:05.987+09:00
2024-08-15T14:20:05+09:00
利用可能なフォーマットトークンは多岐にわたります。主なものを以下に示します。
トークン | 説明 | 例 |
---|---|---|
YYYY | 年 (4桁) | 2024 |
YY | 年 (2桁) | 24 |
MMMM | 月名 (フル) | August |
MMM | 月名 (短縮) | Aug |
MM | 月 (2桁ゼロ埋め) | 08 |
M | 月 (1桁または2桁) | 8 |
DDDD | 年の日 (1-366) | 228 |
DDD | 年の日 (1-366) | 228 |
DD | 日 (2桁ゼロ埋め) | 15 |
D | 日 (1桁または2桁) | 15 |
Do | 日 (序数) | 15th |
dddd | 曜日 (フル) | Thursday |
ddd | 曜日 (短縮) | Thu |
d | 曜日 (数値, 0=日曜) | 4 |
HH | 時 (24時間制, 2桁ゼロ埋め) | 14 |
H | 時 (24時間制, 1桁または2桁) | 14 |
hh | 時 (12時間制, 2桁ゼロ埋め) | 02 |
h | 時 (12時間制, 1桁または2桁) | 2 |
mm | 分 (2桁ゼロ埋め) | 20 |
m | 分 (1桁または2桁) | 20 |
ss | 秒 (2桁ゼロ埋め) | 05 |
s | 秒 (1桁または2桁) | 5 |
SSSSSS | マイクロ秒 (6桁) | 987000 |
A | 午前/午後 (大文字) | PM |
a | 午前/午後 (小文字) | pm |
ZZ | タイムゾーンオフセット (+HH:mm) | +09:00 |
Z | タイムゾーンオフセット (+HHmm) | +0900 |
X | Unixタイムスタンプ (秒) | 1723701605.987 |
x | Unixタイムスタンプ (ミリ秒) | 1723701605987 |
フォーマット文字列中のトークン以外の文字(例えば、日本語など)はそのまま出力されます。もしトークンとして解釈される可能性のある文字をそのまま出力したい場合は、[]
で囲みます。
a = arrow.get('2024-03-10 09:00:00')
# "YYYY"という文字列をそのまま出力したい場合
print(a.format('MM/DD/[YYYY] HH:mm'))
出力例:
03/10/YYYY 09:00
パース (arrow.get()のフォーマット指定)
arrow.get()
の第二引数にフォーマット文字列(またはそのリスト)を指定することで、特定のフォーマットを持つ文字列をパースできます。
date_str1 = 'June 1, 2024 11:00 AM'
date_str2 = '2024/12/31'
# 単一のフォーマットを指定
dt1 = arrow.get(date_str1, 'MMMM D, YYYY h:mm A')
print(dt1)
# 複数のフォーマット候補を指定 (リストで渡す)
dt2 = arrow.get(date_str2, ['YYYY/MM/DD', 'YYYY-MM-DD'])
print(dt2)
# タイムゾーンも同時に指定
dt3 = arrow.get('2024年9月10日 15時', 'YYYY年M月D日 H時', tzinfo='Asia/Tokyo')
print(dt3)
出力例:
<Arrow [2024-06-01T11:00:00+00:00]>
<Arrow [2024-12-31T00:00:00+00:00]>
<Arrow [2024-09-10T15:00:00+09:00]>
ログファイルのように空白の数が不定な場合、normalize_whitespace=True
を指定すると、余分な空白(スペース、タブ、改行)を正規化してパースしてくれます。
log_entry = "2024-04-01\t 10:30:00 \n INFO User logged in"
# 通常のパースでは失敗する可能性がある
try:
arrow.get(log_entry, 'YYYY-MM-DD HH:mm:ss')
except arrow.parser.ParserError as e:
print(f"ParserError: {e}")
# normalize_whitespace=True を使う
dt_log = arrow.get(log_entry, 'YYYY-MM-DD HH:mm:ss', normalize_whitespace=True)
print(dt_log)
出力例:
ParserError: Could not parse '2024-04-01 10:30:00 \n INFO User logged in'
<Arrow [2024-04-01T10:30:00+00:00]>
🌍 タイムゾーンの扱い
Arrowの大きな利点の一つは、タイムゾーンの扱いが非常に簡単であることです。Arrowオブジェクトはデフォルトでタイムゾーンアウェア(UTC)であり、to()
メソッドを使って簡単にタイムゾーンを変換できます。
utc_time = arrow.utcnow()
print(f"UTC: {utc_time}")
# 日本時間に変換
jst_time = utc_time.to('Asia/Tokyo')
print(f"JST: {jst_time}")
# ニューヨーク時間に変換
nyc_time = jst_time.to('America/New_York')
print(f"NYC: {nyc_time}")
# datetimeオブジェクトに変換してもタイムゾーン情報は維持される
jst_datetime = jst_time.datetime
print(f"JST (datetime): {jst_datetime}, TZ: {jst_datetime.tzinfo}")
出力例 (実行日時によって異なります):
UTC: 2025-04-05T11:26:00.543210+00:00
JST: 2025-04-05T20:26:00.543210+09:00
NYC: 2025-04-05T07:26:00.543210-04:00
JST (datetime): 2025-04-05 20:26:00.543210+09:00, TZ: tzfile('/usr/share/zoneinfo/Asia/Tokyo')
arrow.get()
でタイムゾーン情報のない文字列やNaiveなdatetime
オブジェクトをパースする際、デフォルトではUTCとして扱われますが、tzinfo
引数で初期タイムゾーンを指定することも可能です。
naive_str = '2024-10-27 02:30:00' # タイムゾーン情報なし
# デフォルト (UTC)
dt_utc = arrow.get(naive_str)
print(f"Default (UTC): {dt_utc}")
# 初期タイムゾーンを指定 (例: 日本時間)
dt_jst = arrow.get(naive_str, tzinfo='Asia/Tokyo')
print(f"Specified (JST): {dt_jst}")
# dateutilのtzオブジェクトなども利用可能
from dateutil import tz
local_tz = tz.gettz('Europe/Paris')
dt_paris = arrow.get(naive_str, tzinfo=local_tz)
print(f"Specified (Paris): {dt_paris}")
出力例:
Default (UTC): <Arrow [2024-10-27T02:30:00+00:00]>
Specified (JST): <Arrow [2024-10-27T02:30:00+09:00]>
Specified (Paris): <Arrow [2024-10-27T02:30:00+02:00]>
標準ライブラリのdatetime
ではタイムゾーンの扱いが煩雑になりがちですが、Arrowを使えば非常にシンプルに、かつ間違いなくタイムゾーンを扱うことができます。
↔️ 時間の移動 (Shift) と置換 (Replace)
shift()メソッド
特定の日時に対して、時間や日数を加算・減算したい場合にshift()
メソッドが便利です。キーワード引数で移動させたい単位(years
, months
, weeks
, days
, hours
, minutes
, seconds
, microseconds
)と量を指定します。
a = arrow.get('2024-04-01 12:00:00+09:00')
# 3時間進める
shifted_1 = a.shift(hours=3)
print(f"+3 hours: {shifted_1}")
# 2日戻す
shifted_2 = a.shift(days=-2)
print(f"-2 days: {shifted_2}")
# 1年と6ヶ月進める
shifted_3 = a.shift(years=1, months=6)
print(f"+1 year 6 months: {shifted_3}")
# 2週間戻す
shifted_4 = a.shift(weeks=-2)
print(f"-2 weeks: {shifted_4}")
# 組み合わせ
shifted_5 = a.shift(days=10, hours=-5, minutes=30)
print(f"+10 days -5 hours +30 minutes: {shifted_5}")
出力例:
+3 hours: 2024-04-01T15:00:00+09:00
-2 days: 2024-03-30T12:00:00+09:00
+1 year 6 months: 2025-10-01T12:00:00+09:00
-2 weeks: 2024-03-18T12:00:00+09:00
+10 days -5 hours +30 minutes: 2024-04-11T07:30:00+09:00
shift()
はdatetime.timedelta
よりも柔軟で、特に月や年の単位での移動(うるう年や月の日数を考慮)を簡単に行えます。
replace()メソッド
日時オブジェクトの一部(年、月、日、時、分、秒など)を特定の値に置き換えたい場合にreplace()
メソッドを使用します。
a = arrow.get('2024-08-15T10:30:45.123+09:00')
# 年を2025年に変更
replaced_1 = a.replace(year=2025)
print(f"Year -> 2025: {replaced_1}")
# 時を9時、分を0分、秒を0秒、マイクロ秒を0に変更 (月初めの時刻)
replaced_2 = a.replace(day=1, hour=9, minute=0, second=0, microsecond=0)
print(f"Reset time to 9:00 on 1st: {replaced_2}")
# タイムゾーンを変更 (UTCに)
replaced_3 = a.replace(tzinfo='UTC') # a.to('UTC') と同じ効果
print(f"Timezone -> UTC: {replaced_3}")
# 組み合わせ
replaced_4 = a.replace(month=12, day=25, hour=19)
print(f"Date to Dec 25, Hour to 19: {replaced_4}")
出力例:
Year -> 2025: 2025-08-15T10:30:45.123000+09:00
Reset time to 9:00 on 1st: 2024-08-01T09:00:00+09:00
Timezone -> UTC: 2024-08-15T01:30:45.123000+00:00
Date to Dec 25, Hour to 19: 2024-12-25T19:30:45.123000+09:00
replace()
は特定の日時に設定したり、時刻部分をリセットしたりする際に非常に便利です。
🗣️ Humanization (相対時間表現)
Arrowの非常に便利な機能の一つがhumanize()
メソッドです。これは、特定の日時を現在時刻からの相対的な時間として、人間にとって分かりやすい文字列で表現してくれます。
now = arrow.utcnow()
past_time = now.shift(hours=-2)
future_time = now.shift(days=3)
very_past_time = now.shift(years=-1, months=-6)
print(f"Now: {now}")
print(f"Past (-2h): {past_time.humanize()}") # デフォルトでは現在時刻と比較
print(f"Future (+3d): {future_time.humanize()}")
# 比較対象を指定することも可能
base_time = arrow.get('2024-01-01T00:00:00+00:00')
print(f"Very Past (-1y -6m) vs Now: {very_past_time.humanize()}")
print(f"Very Past vs 2024-01-01: {very_past_time.humanize(base_time)}")
# 日本語ロケールでの表示
# Arrowは内部で 'humanize' ライブラリを使っていることがあります
# pip install humanize が必要になる場合があります
# 環境変数やlocale設定で日本語を指定する必要がある場合もあります
try:
print(f"Past (-2h, ja): {past_time.humanize(locale='ja_jp')}")
print(f"Future (+3d, ja): {future_time.humanize(locale='ja_jp')}")
print(f"Now (ja): {now.humanize(locale='ja_jp')}") # 現在時刻の場合
except Exception as e:
print(f"日本語ロケールの設定エラー: {e}. Humanizeライブラリのインストールやロケール設定を確認してください。")
# 表示の粒度 (granularity) を指定
print(f"Future (+3d, granularity='day'): {future_time.humanize(granularity='day')}")
print(f"Past (-2h, granularity=['hour', 'minute']): {past_time.humanize(granularity=['hour', 'minute'])}")
# 時間差のみ表示 (only_distance=True)
print(f"Past (-2h, distance only): {past_time.humanize(only_distance=True)}")
print(f"Future (+3d, distance only): {future_time.humanize(only_distance=True)}")
出力例 (実行日時やロケール設定によって異なります):
Now: 2025-04-05T11:26:00.987654+00:00
Past (-2h): 2 hours ago
Future (+3d): in 3 days
Very Past (-1y -6m) vs Now: a year ago
Very Past vs 2024-01-01: 2 years ago
Past (-2h, ja): 2時間前
Future (+3d, ja): 3日後
Now (ja): たった今
Future (+3d, granularity='day'): in 3 days
Past (-2h, granularity=['hour', 'minute']): 2 hours ago
Past (-2h, distance only): 2 hours
Future (+3d, distance only): 3 days
locale
引数で言語を指定できます(’en_us’, ‘ja_jp’, ‘fr_fr’ など)。対応しているロケールはArrowのバージョンや依存ライブラリによって異なります。
granularity
引数を使うと、表示する時間の単位を細かく制御できます。例えば、granularity='day'
とすると、「3日後」のように日まで表示し、それ以下の時間は無視します。リストで['hour', 'minute']
のように指定すると、「2時間15分前」のような表示が可能になります。
only_distance=True
を指定すると、「〜前」「〜後」といった相対的な表現を除き、時間差(距離)のみを表示します。
humanize()
は、ブログ記事の投稿時間、メッセージの受信時間などをユーザーフレンドリーに表示したい場合に非常に役立ちます。
↔️ 範囲 (Range) と期間 (Span)
Arrowは、特定の時間範囲内の日時オブジェクトを生成したり、ある時間単位での開始・終了日時(期間)を取得したりする機能も提供します。
range()メソッド
arrow.Arrow.range()
メソッドは、指定した時間単位で、開始日時から終了日時までのArrowオブジェクトを順番に生成するイテレータを返します。
start_date = arrow.get('2024-05-01')
end_date = arrow.get('2024-05-05')
# 1日ごとの範囲
print("Daily range:")
for dt in arrow.Arrow.range('day', start_date, end_date):
print(dt)
# 12時間ごとの範囲
print("\n12-hour range:")
start_time = arrow.get('2024-05-01T08:00:00')
end_time = arrow.get('2024-05-02T12:00:00')
for dt in arrow.Arrow.range('hour', start_time, end_time, interval=12):
print(dt)
# 特定のタイムゾーンでの範囲
print("\nHourly range in Tokyo:")
tz = 'Asia/Tokyo'
start_jst = arrow.get('2024-05-01 22:00:00', tzinfo=tz)
end_jst = arrow.get('2024-05-02 03:00:00', tzinfo=tz)
for dt in arrow.Arrow.range('hour', start_jst, end_jst):
print(dt)
出力例:
Daily range:
2024-05-01T00:00:00+00:00
2024-05-02T00:00:00+00:00
2024-05-03T00:00:00+00:00
2024-05-04T00:00:00+00:00
2024-05-05T00:00:00+00:00
12-hour range:
2024-05-01T08:00:00+00:00
2024-05-01T20:00:00+00:00
2024-05-02T08:00:00+00:00
Hourly range in Tokyo:
2024-05-01T22:00:00+09:00
2024-05-01T23:00:00+09:00
2024-05-02T00:00:00+09:00
2024-05-02T01:00:00+09:00
2024-05-02T02:00:00+09:00
2024-05-02T03:00:00+09:00
第一引数には範囲の単位 (‘year’, ‘month’, ‘day’, ‘hour’, ‘minute’, ‘second’, ‘microsecond’) を指定します。interval
引数で間隔を調整できます(デフォルトは1)。
span(), floor(), ceil() メソッド
span()
メソッドは、指定した時間単位における、ある日時が含まれる期間の開始日時と終了日時をタプルで返します。floor()
はその期間の開始日時、ceil()
は終了日時のみを返します。
dt = arrow.get('2024-08-15T14:35:20+09:00')
# 'hour' (時間) の期間
hour_span = dt.span('hour')
print(f"Hour Span: {hour_span}")
print(f"Hour Floor: {dt.floor('hour')}")
print(f"Hour Ceil: {dt.ceil('hour')}")
# 'day' (日) の期間
day_span = dt.span('day')
print(f"\nDay Span: {day_span}")
print(f"Day Floor: {dt.floor('day')}")
print(f"Day Ceil: {dt.ceil('day')}")
# 'month' (月) の期間
month_span = dt.span('month')
print(f"\nMonth Span: {month_span}")
print(f"Month Floor: {dt.floor('month')}")
print(f"Month Ceil: {dt.ceil('month')}")
# 'week' (週) の期間 (デフォルトでは月曜始まり)
week_span = dt.span('week')
print(f"\nWeek Span (Mon start): {week_span}")
# 週の始まりを日曜日にする場合 (Arrow 1.0以降)
try:
week_span_sun = dt.span('week', week_start=7) # 1=月曜, 7=日曜
print(f"Week Span (Sun start): {week_span_sun}")
print(f"Week Floor (Sun start): {dt.floor('week', week_start=7)}")
print(f"Week Ceil (Sun start): {dt.ceil('week', week_start=7)}")
except TypeError:
print("\nweek_start 引数はArrow 1.0以降で利用可能です。")
出力例 (週の始まりの設定はArrowのバージョンによります):
Hour Span: (<Arrow [2024-08-15T14:00:00+09:00]>, <Arrow [2024-08-15T14:59:59.999999+09:00]>)
Hour Floor: 2024-08-15T14:00:00+09:00
Hour Ceil: 2024-08-15T14:59:59.999999+09:00
Day Span: (<Arrow [2024-08-15T00:00:00+09:00]>, <Arrow [2024-08-15T23:59:59.999999+09:00]>)
Day Floor: 2024-08-15T00:00:00+09:00
Day Ceil: 2024-08-15T23:59:59.999999+09:00
Month Span: (<Arrow [2024-08-01T00:00:00+09:00]>, <Arrow [2024-08-31T23:59:59.999999+09:00]>)
Month Floor: 2024-08-01T00:00:00+09:00
Month Ceil: 2024-08-31T23:59:59.999999+09:00
Week Span (Mon start): (<Arrow [2024-08-12T00:00:00+09:00]>, <Arrow [2024-08-18T23:59:59.999999+09:00]>)
Week Span (Sun start): (<Arrow [2024-08-11T00:00:00+09:00]>, <Arrow [2024-08-17T23:59:59.999999+09:00]>)
Week Floor (Sun start): 2024-08-11T00:00:00+09:00
Week Ceil (Sun start): 2024-08-17T23:59:59.999999+09:00
これらのメソッドは、ログデータを時間単位で集計したり、レポートを作成したりする際に、期間の開始・終了時刻を簡単に取得するのに役立ちます。
🆚 標準ライブラリ datetime との比較
Arrowはdatetime
の多くの課題を解決し、より直感的で使いやすいAPIを提供します。ここで主な違いをまとめてみましょう。
機能/側面 | datetime (標準ライブラリ) | Arrow |
---|---|---|
インポート | from datetime import datetime, timezone, timedelta など、複数必要になることが多い | import arrow だけで済むことが多い |
オブジェクト生成 | datetime(...) , strptime() など用途に応じて使い分け | arrow.get() が様々な入力(文字列、数値、datetime)を賢く処理 |
タイムゾーン | デフォルトはNaive (タイムゾーン情報なし)。Awareにするにはtzinfo の明示的な指定やpytz などの外部ライブラリが必要。変換も煩雑。 | デフォルトでAware (UTC)。to('Timezone/Name') で簡単に変換可能。 |
フォーマット/パース | strftime() , strptime() を使用。フォーマットコードを覚える必要あり。 | format() , get(str, format) を使用。Moment.jsライクなトークンで直感的。ISO 8601は自動認識。 |
時間計算 | timedelta を使用。月や年の加算・減算は複雑(relativedelta などが必要)。 | shift() メソッドで年・月・週を含む相対的な移動が容易。 |
Humanization | 標準機能なし。自前で実装するか、humanize などの別ライブラリが必要。 | humanize() メソッドで簡単に実現。ロケール対応、粒度指定も可能。 |
範囲/期間 | 自前でループ処理などを書く必要あり。 | range() , span() , floor() , ceil() で簡単に生成・取得。 |
APIの統一感 | 複数のモジュール、クラス、関数に機能が分散。 | Arrow オブジェクト中心の統一されたAPI。 |
もちろん、標準ライブラリには外部依存がないというメリットがあります。しかし、日付・時刻操作のコードを頻繁に書く場合や、タイムゾーン、フォーマット、相対時間などを多用するアプリケーションでは、Arrowを導入することで開発効率とコードの可読性が大幅に向上するでしょう 💪。
🌟 発展的な機能
ロケール
humanize()
やformat()
で月名・曜日名を特定の言語で表示するには、ロケール設定が関係します。Arrowは内部で利用可能なロケール情報を持っていますが、システムのロケール設定や、humanize
ライブラリなどの依存関係に影響されることがあります。
a = arrow.now()
# フランス語で表示
try:
print(f"French Day: {a.format('dddd', locale='fr_fr')}")
print(f"French Month: {a.format('MMMM', locale='fr_fr')}")
print(f"French Humanize: {a.shift(days=-1).humanize(locale='fr_fr')}")
except Exception as e:
print(f"ロケール 'fr_fr' の設定エラー: {e}")
# ドイツ語で表示
try:
print(f"German Day: {a.format('dddd', locale='de_de')}")
print(f"German Month: {a.format('MMMM', locale='de_de')}")
print(f"German Humanize: {a.shift(hours=-5).humanize(locale='de_de')}")
except Exception as e:
print(f"ロケール 'de_de' の設定エラー: {e}")
出力例 (ロケールが利用可能な場合):
French Day: samedi
French Month: avril
French Humanize: hier
German Day: Samstag
German Month: April
German Humanize: vor 5 Stunden
もし特定のロケールが利用できない場合は、エラーが発生するか、デフォルトの英語表示になることがあります。必要なロケールがシステムにインストールされているか、関連ライブラリが対応しているかを確認してください。
ファクトリ (Factory)
Arrowの動作をカスタマイズしたい場合(例えば、特定のタイムゾーンをデフォルトにする、独自のArrow派生クラスを使うなど)、ArrowFactory
を使用できます。
from arrow.factory import ArrowFactory
# デフォルトで日本時間 (JST) を使うファクトリを作成
class JSTArrowFactory(ArrowFactory):
def __init__(self):
super().__init__(tzinfo='Asia/Tokyo')
# ファクトリのインスタンスを作成
jst_factory = JSTArrowFactory()
# ファクトリ経由で現在時刻を取得
now_jst = jst_factory.now()
print(f"Factory Now (JST): {now_jst}")
# ファクトリ経由でパース (タイムゾーン情報がない場合はJSTになる)
parsed_jst = jst_factory.get('2024-12-01 10:00:00')
print(f"Factory Parsed (JST): {parsed_jst}")
出力例:
Factory Now (JST): 2025-04-05T20:26:01.123456+09:00
Factory Parsed (JST): 2024-12-01T10:00:00+09:00
ファクトリを使うことで、アプリケーション全体で一貫した日時設定(特にタイムゾーン)を適用しやすくなります。
🏁 まとめ
Arrowは、Pythonでの日付・時刻操作を劇的に改善する強力なライブラリです。標準のdatetime
モジュールが持つ煩雑さや機能不足を解消し、より直感的で、より少ないコードで、より多くのことを可能にします。
特に以下の点でArrowは優れています:
- ✨ シンプルなAPI:
arrow.get()
,format()
,shift()
,to()
など、直感的で使いやすいメソッド群。 - 🌍 簡単なタイムゾーン処理: デフォルトでタイムゾーンアウェアであり、変換も
to()
メソッド一つで簡単。 - 🗣️ Humanization:
humanize()
メソッドによる相対時間の簡単表示。 - 📜 柔軟なフォーマットとパース: ISO 8601の自動認識と、Moment.jsライクなトークンによる柔軟な指定。
- ↔️ 期間と範囲:
range()
,span()
などによる期間処理の簡略化。
もしあなたがPythonで日付や時刻を扱うプロジェクトに取り組んでいるなら、ぜひArrowの導入を検討してみてください。コードがよりシンプルに、より読みやすく、そしてバグが少なくなるはずです!🚀
コメント