S-JIS[2025-12-13/2026-02-13]

pyo3 datetime型

Rustpyo3の日付型のメモ。


概要

Pythonのdatetimeモジュールの日付型は、PyO3 0.27ではpyo3::typesのPyDateTime等で扱う。


PyDateの例

日付(年月日)は、PyO3ではpyo3::types::PyDateで扱う。
関数の引数や戻り値の型としては、Bound<PyDate>(やPy<PyDate>)を指定する。

example-pyo3/src/lib.rs:

use pyo3::prelude::*;

#[pymodule]
mod example_pyo3 {
    use pyo3::{prelude::*, types::*};
〜

    #[pyfunction]
    fn inc_date<'py>(py: Python<'py>, date: &Bound<PyDate>) -> PyResult<Bound<'py, PyDate>> {
        let year = date.get_year();
        let month = date.get_month();
        let day = date.get_day();

        let next_date = PyDate::new(py, year, month, day + 1)?; // Bound<PyDate>
        Ok(next_date)
    }
}

pyが必要な場合、第1引数に「py: Python」を定義しておく。
関数からBoundで返す場合はライフタイム'pyも明示する。

get_year()やget_month()といったメソッドは、PyDateAccessトレイトで定義されている。 [2026-02-04]
pyo3::types::*で全てをインポートしている場合は自動的にインポートされるが、クラスやトレイトを個別に指定する場合はPyDateとPyDateAccessをインポートする必要がある。


呼び出す例

Pythonプロジェクト側で、datetimeモジュールを依存ライブラリーに追加しておく。

cd call-pyo3
uv add datetime

call-pyo3/main.py:

import datetime
import example_pyo3

def main():
    print("Hello from call-pyo3!")
    print(example_pyo3.inc_date(datetime.date(2025, 12, 13)))

if __name__ == "__main__":
    main()

実行例

> cd call-pyo3
> uv run main.py
Hello from call-pyo3!
2025-12-14

タイムゾーン情報を扱う例

Pythonのdatetime.datetimeやdatetime.timeはタイムゾーン(tzinfo)を持つことが出来る。[2026-01-28]

Bound<PyAny>からタイムゾーンを取得するには、getattrメソッドを使用する。

    use pyo3::{prelude::*, types::*};
    #[pyfunction]
    fn tzinfo_check(value: Bound<PyAny>) -> PyResult<()> {
        if value.is_instance_of::<PyDateTime>() || value.is_instance_of::<PyTime>() {
        let tzinfo = value.getattr("tzinfo")?; // Bound<PyAny>
        println!("tzinfo {}", tzinfo);
    }
    Ok(())

Python側でtzinfoをセットしていないときは、Rust側ではPyNoneになる。

呼び出す側の例

call-pyo3/main.py:

import datetime
import example_pyo3

def main():
    JST = datetime.timezone(datetime.timedelta(hours=9))
    example_pyo3.tzinfo_check(datetime.time(12, 34, 56))
    example_pyo3.tzinfo_check(datetime.time(12, 34, 56, tzinfo=JST))
    example_pyo3.tzinfo_check(datetime.datetime(2026, 1, 28))
    example_pyo3.tzinfo_check(datetime.datetime(2026, 1, 28, tzinfo=JST))

if __name__ == "__main__":
    main()

Bound<PyDateTime>やBound<PyTime>からは、get_tzinfoメソッドでPyTzInfoを取得できる。[2026-02-13]

    use pyo3::{prelude::*, types::*}; // types::PyDateTime, types::PyTzInfoAccess
    #[pyfunction]
    fn tzinfo_check(value: Bound<PyDateTime>) {
        let tzinfo = value.get_tzinfo(); // Option<Bound<PyTzInfo>>
        println!("tzinfo={:?}", tzinfo);
    }

タイムゾーン情報を生成する例

タイムゾーン情報(Pythonのdatetime.tzinfo)は、PyO3ではPyTzInfoで扱う。[2026-02-04]
これを生成するには、PyDeltaを使用する。

    use pyo3::{prelude::*, types::*};
    #[pyfunction]
    fn get_datetime<'py>(py: Python<'py>) -> PyResult<Bound<'py, PyDateTime>> {
        let year = 2026;
        let month = 2;
        let day = 4;
        let hour = 12;
        let minute = 34;
        let second = 56;
        let microsecond = 456789;

        // +09:00
        let offset_hour = 9;
        let offset = PyDelta::new(py, 0, offset_hour * 60 * 60, 0, false)?;
        let tzinfo = PyTzInfo::fixed_offset(py, offset)?;

        let datetime = PyDateTime::new(
            py,
            year,
            month,
            day,
            hour,
            minute,
            second,
            microsecond,
            Some(&tzinfo),
        )?;
        Ok(datetime)
    }

chronoの例

Rustで日付を扱うchronoクレートをPyO3 0.27で使う例。


まず、chronoクレートを依存ライブラリーに追加する。

cd example-pyo3
cargo add chrono

次に、pyo3にchronoフィーチャーを追加する。

example-pyo3/Cargo.toml:

〜
[dependencies]
chrono = "0.4.42"
pyo3 = { version = "0.27.2", features = ["chrono"] }

PyDateとchrono::NaiveDateを変換する例。

example-pyo3/src/lib.rs:

use pyo3::prelude::*;

#[pymodule]
mod example_pyo3 {
    use pyo3::{prelude::*, types::*};
〜

    #[pyfunction]
    fn inc_date(py: Python, date: &Bound<PyDate>) -> PyResult<Py<PyDate>> {
        let date: chrono::NaiveDate = date.extract()?; // PyDateをchronoのNaiveDateに変換

        let next_date = date + chrono::Duration::days(1);
        let next_date = next_date.into_pyobject(py)?;   // chronoのNativeDateをPyDateに変換
        Ok(next_date.into())
    }
}

Pythonから呼び出す例


chronoを直接使用する例

pyfunctionの関数の引数や戻り値として直接chronoの日付を指定することも出来る。

use pyo3::prelude::*;

#[pymodule]
mod example_pyo3 {
    use pyo3::{prelude::*, types::*};
〜

    #[pyfunction]
    fn inc_date(date: chrono::NaiveDate) -> PyResult<chrono::NaiveDate> {
        let next_date = date + chrono::Duration::days(1);

        Ok(next_date)
    }
}

numpy.datetime64の例

numpyのdatetime64を扱う例。[2026-02-07]

datetime64クラスをPyO3で直接扱う方法は無いようなので、astypeメソッドを呼び出して変換する。

    #[pyfunction]
    fn numpy_datetime64(value: Bound<PyAny>) -> PyResult<()> {
        let value = value.call_method1("astype", ("datetime64[ns]",))?;
        let value = value.call_method1("astype", ("int64",))?;
        let value: i64 = value.extract()?;
        println!("epoch: {}", value);

        Ok(())
    }

datetime64は色々な単位で保持できるようなので、まずナノ秒単位に変換する。
その後エポック(1970-01-01からの経過ナノ秒)の数値に変換する。


pyo3へ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま