Pythonの日付・時刻操作を劇的に楽にするArrowライブラリ徹底解説 🏹

Python

もう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
XUnixタイムスタンプ (秒)1723701605.987
xUnixタイムスタンプ (ミリ秒)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の導入を検討してみてください。コードがよりシンプルに、より読みやすく、そしてバグが少なくなるはずです!🚀

コメント

タイトルとURLをコピーしました