イントロダクション
日付やタイムスタンプは企業活動の至るところに組み込まれています。受注日時、配送予定、従業員の打刻時間──こうした「いつ」の情報を一元管理するために、Odoo では Datetime 型が標準で用意されています。
Date 型が日付だけを保持するのに対し、Datetime 型は日時(年月日+時分秒)を扱います。この差は単なる細部ではなく、複数のタイムゾーンにまたがるチームや分単位での運用を行う場合に実務上の影響が出ます。
本ガイドでは Odoo の Datetime フィールドについて、保存される内容、データモデル上の挙動、Odoo Studio や Python を使った作成方法、そして実務でよく使われる具体例までを網羅します。
Odoo の Datetime フィールドとは何か
Odoo の ORM では fields.Datetime が日時を秒単位まで保持します。データベース側では PostgreSQL の TIMESTAMP 型に対応し、内部的には UTC で保存され、画面表示時に利用者のタイムゾーンに合わせて変換されます。
ユーザー向けには、フォーム上で日付と時刻を一緒に選べるピッカーとして表示されます。リストやレポートでは表示言語や個々のタイムゾーン設定に基づいて整形されます。
Python のモデル定義では次のように宣言します。
from odoo import fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
x_confirmed_on = fields.Datetime(
string='Confirmed On',
default=fields.Datetime.now,
readonly=True,
copy=False,
)
string は画面ラベル、default は新規作成時に自動でセットするデフォルト値、readonly はユーザーによる編集を禁止するための属性です。監査目的のタイムスタンプに readonly を付けることが一般的です。
Odoo Studio ではこの型は Date & Time と呼ばれ、Studio 経由で作ると技術名に自動的に x_studio_ プレフィックスが付与されます。コードや XML-RPC で作る場合は自分で技術名を定義します。
フィールドの仕組み
Datetime フィールドを定義すると、モジュールのインストールやアップグレード時に対応するデータベース列が自動で作成されます。通常は手動で SQL マイグレーションを書く必要はありません。
多くの人が戸惑うのはタイムゾーンの扱いです。データベースには常に UTC で保存されます。たとえばパリのユーザーが午後3時を登録すると、DB には UTC の午後1時として保存されます。ニューヨークのユーザーが同じレコードを見れば午前9時と表示されます。ORM が各ユーザーのプロファイル設定に基づき表示時に変換を担当します。
主要なフィールド属性
Odoo の Datetime フィールドで重要なプロパティを以下にまとめます。
- default: 通常
fields.Datetime.nowを指定してレコード作成時に現在時刻を自動設定します。 - required: フォーム上およびモデルレベルで必須にする属性です。
- readonly: インターフェース上で手動編集を禁止します。自動生成タイムスタンプに有効です。
- compute: 他フィールドやビジネスロジックから Python メソッドで算出する際に使います。
- store:
computeと併用して計算結果を DB に保存し、検索や集計で利用できるようにします。 - copy: レコード複製時に値を引き継ぐかどうかを制御します。デフォルトは
True。履歴として引き継ぎたくないタイムスタンプはFalseにします。 - index: データベースにインデックスを作成します。大量テーブルでの日時フィルタに有用です。
ビューでの表示
フォームビューでは日付と時刻を同時に選べる UI が出ます。リストではユーザーの言語設定に沿って整形表示され、検索ビューでは「以前/以後/期間内」などの範囲条件が使えます。
必要に応じて date_range ウィジェットと組み合わせ、フォーム上で時間帯の範囲を直接選ばせることもできます。スケジュール管理や時間枠が重要な処理で便利です。
Datetime と Date の使い分け
どちらを使うか迷ったら簡単なルールに従ってください。時間の精度(時分)が不要なら fields.Date、時刻まで必要なら fields.Datetime を使います。
Date を使う場面: 請求の支払期限、生年月日、製品の消費期限、契約の更新日など、日付だけで事足りる場合。
Datetime を使う場面: 受注確定時刻、会議開始時刻、従業員の出退勤記録、倉庫での作業スケジュールなど、時刻の精度が重要な場合。
不要に Datetime を使うとタイムゾーンの処理だけが増え、実務上の利点はほとんどありません。本当に時刻が必要かをまず問い直しましょう。
業務での利用例
Datetime フィールドは Odoo のほぼ全モジュールで使われています。ここでは現場でよく見られる5つの実用例を紹介します。
CRM:リードの活動追跡
CRM ではリードの状態遷移やフォローアップ日時を Datetime で管理します。たとえばリードが進捗状態になった時刻やフォローの期限を記録し、対応速度の分析や放置リードの抽出に使います。見積送信時刻や重要な通話のログを追加するカスタムフィールドもよく使われます。
営業:受注確定のタイムスタンプ
販売オーダーの date_order は Datetime で、受注が確定した瞬間を正確に記録します。時間ごとの売上分析、処理にかかる時間の算出、確定後の編集履歴チェックなどで重宝します。
在庫:出荷・入荷の予定日時
倉庫では stock.picking の scheduled_date を使っていつ作業するかを計画します。自動アクションで遅延メールを投げたり、期限切れ前のアラートを出すなど、時間比較に基づく処理が可能です。
製造:生産開始・終了時刻
製造指示では生産の開始と終了を Datetime で記録し、実績と計画の差分やシフト別の稼働分析、ボトルネック時間帯の特定に役立てます。複数シフト運用では分単位の正確さが求められます。
人事:出退勤と休暇管理
勤怠モジュールは出退勤や休暇の始終を Datetime で管理します。給与計算や残業判定は分単位のデータに依存するため、ここでの精度不足や欠損は直接的に賃金計算に影響します。
Datetime フィールドの作成・カスタマイズ方法
Datetime フィールドをモデルに追加する方法は、技術レベルや運用方針に応じて主に三通りあります。
Odoo Studio(ノーコード)
Odoo Studio はコードを書かずにフィールドを追加できるビルトインツールです。Studio で Datetime を追加する手順は以下の通りです。
- メインメニューから Odoo Studio を開く。
- フィールドを追加したいフォームに移動する。
- サイドバーから Date & Time フィールドをドラッグしてフォームに配置する。
- ラベルや必須設定、必要ならデフォルト値をプロパティパネルで設定する。
- 保存して Studio を閉じる。
Studio は自動で x_studio_ プレフィックス付きのフィールドを作成し、フォームに反映します。データベースの変更も Odoo が自動で扱うため、開発者の関与なしに手早く追加したい現場向けの方法です。
カスタムモジュールでの Python 実装
開発者がバージョン管理や環境間デプロイを行う場合は、Python でモデル定義する方法が推奨されます。
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
x_last_contact_date = fields.Datetime(
string='Last Contact Date',
default=fields.Datetime.now,
copy=False,
)
モデルにフィールドを定義したら、表示させたいビューの XML に追加します。モジュールのインストールやアップグレード時に Odoo が自動で TIMESTAMP 列を作成します。
XML-RPC API を使う場合
デプロイパイプラインやリモート設定スクリプトからプログラム的に管理するなら、XML-RPC 経由でフィールドを作成できます。
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_last_contact_date',
'field_description': 'Last Contact Date',
'model_id': model_id,
'ttype': 'datetime',
'state': 'manual',
}]
)
ttype: 'datetime' により Datetime フィールドが作られ、state: 'manual' はモジュール外で追加されたフィールドであることを示します。自動設定スクリプトでのフィールド作成に適した方法です。
ベストプラクティス
1. デフォルトには fields.Datetime.now を関数参照で渡すこと
デフォルトを指定する際は default=fields.Datetime.now と括弧を付けずに書きます。括弧を付けるとクラスロード時に一度だけ評価され、その後作成される全レコードが同じ固定時刻を共有してしまいます。括弧なしならレコード作成時に関数が呼ばれて個別の現在時刻が入ります。
2. イベント系タイムスタンプは copy=False にする
確認日時や完了時刻のように『いつ起きたか』を記録するフィールドは copy=False にしましょう。レコード複製で元のタイムスタンプが持ち越されると履歴が汚染されます。
3. API 経由の書き込み時は常に UTC で渡す
XML-RPC 経由で日時を書き込む場合は必ず UTC(例: YYYY-MM-DD HH:MM:SS)で渡してください。API 側で自動変換は行われず、与えた文字列がそのまま UTC として保存されるため、ローカル時刻をそのまま渡すと見えないズレが生じます。
4. 自動生成されるタイムスタンプは readonly にする
システムイベントを反映するタイムスタンプは画面上で編集不可にするのが原則です。本当に編集が必要な場合はフィールド単位のアクセス権で制御してください。
5. 時刻が不要なら Date を選ぶ
締切日や更新日など時刻が意味を持たない場合は fields.Date を選びましょう。余計なタイムゾーン処理を避け、データモデルをシンプルに保てます。
よくある落とし穴
生データを直接読むときのタイムゾーン混乱
Datetime の誤解の多くはここから来ます。DB や API から直接値を読むと UTC のまま返ってくるため、そのまま表示や集計に使うと時差のズレが発生します。API 結果をユーザーに見せる前にはクライアント側で明示的にタイムゾーン変換する習慣をつけてください。
API にローカル時刻を書き込む危険性
既にローカルタイムで書いた値を API に送ると DB はそれを UTC として保存してしまいます。たとえばパリ時間の 15:00 をそのまま送ると、実際の表示が季節によって 16:00 や 17:00 とずれてしまうケースがあり、本番運用で初めて気づくことが多いミスです。
default=fields.Datetime.now() と括弧を付けるミス
括弧を付けた fields.Datetime.now() はクラス読み込み時に一度だけ評価され、その後のすべてのレコードが同じ時刻を共有してしまいます。初見では正しく見えても、時間分析をすると全レコードが同じ値で歪んでいるため発見が遅れがちです。
event タイムスタンプで copy=False を忘れる問題
複製時に Datetime を引き継いでしまうと、元レコードの発生時刻が新しいレコードにも残り、報告や監査が汚染されます。小さな設定ミスがデータ品質に大きく影響します。
Date で足りる場面で Datetime を使う誤り
支払期限や消費期限のように時刻を使わない用途で Datetime を選ぶと、不要な時間表示やタイムゾーン処理が発生し、UX と運用の双方で無駄が増えます。要件に最も合う型を選びましょう。
まとめ
Datetime は時刻の精度が求められる場面で非常に有用です。リードの開封時刻から生産開始のタイミング、勤怠の打刻まで、Odoo の多くの領域で活躍します。
最も重要なのは「データベースは UTC で保存する」という考え方です。画面表示はユーザーのタイムゾーンに合わせて自動変換されますが、外部連携の読み書きではこれを明示的に扱わないと不具合の元になります。多くのタイムゾーンバグはこの誤解が原因です。
加えて、正しいデフォルト書き方、イベント系には copy=False を付けること、そして時刻が不要な場面では Date を選ぶことがデータモデルの健全性とレポートの信頼性を保つ鍵です。
Dasolo では、企業の Odoo 導入・カスタマイズ・最適化を支援しています。データモデル設計、業務に合わせたフィールド追加、モジュール開発まで、幅広くサポート可能です。 お問い合わせ あなたの Odoo プロジェクトについてぜひお話ししましょう。