Introduction
If you have ever opened a sales order, a project task, or a manufacturing order in Odoo, you have already used a Date field. Deadlines, delivery dates, invoice due dates, contract start dates - most of the time-sensitive data in Odoo is stored using this field type.
For business users, Date fields are intuitive: you click, a calendar appears, you pick a date. But there is more going on under the hood. Understanding how the Date field works in the Odoo data model, how it differs from DateTime fields, and how to create or customize it will help you build more accurate workflows and avoid the kind of time zone surprises that trip up many Odoo implementations.
This guide covers everything you need to know about the Date field in Odoo, from what it stores to how to use it effectively in your business processes.
What is the Date Field in Odoo
In the Odoo ORM, the fields.Date type is designed to store calendar dates with no time component. A date like "2026-03-06" is stored exactly as-is, with no hours, minutes, or seconds attached.
In PostgreSQL, the database Odoo runs on, a Date field maps to the DATE column type. This is distinct from the DateTime field, which maps to TIMESTAMP and includes a full timestamp down to the second.
In the user interface, a Date field renders as a text input with a calendar picker. Users can either type a date directly or click the calendar icon to select one. In list views, it appears as formatted text aligned to the locale of the user.
Here is what a Date field definition looks like in a custom Python module:
from odoo import fields, models
class ProjectTask(models.Model):
_inherit = 'project.task'
x_deadline_confirmed = fields.Date(
string='Confirmed Deadline',
help='The officially confirmed deadline agreed with the customer.',
)
In Odoo Studio, this field is simply labeled Date. When created through Studio, it gets an x_studio_ prefix automatically. When created via Python code or the XML-RPC API, you choose the technical name yourself.
How the Field Works
Date vs. DateTime: what actually changes
The most important thing to understand about the Date field is what it does not include: time. There are no hours, no time zone conversions, nothing beyond the calendar date itself.
The DateTime field, by contrast, stores a full timestamp and is saved in UTC in the database. Odoo then converts it to the user's local time zone on display. This is the source of many confusing "my date shifted by one day" bugs that teams encounter. Those are almost always DateTime issues, not Date field issues.
When you use a Date field, what you store is exactly what users see. A value of 2026-03-15 is 2026-03-15 for every user, regardless of where in the world they are located.
Key Field Attributes
Here are the most important properties you can configure on a Date field in the Odoo framework:
- required: Makes the field mandatory at both the interface and model level.
- default: Sets an automatic default. For example,
fields.Date.todaydefaults to today's date on new records. - index: Creates a database index for faster filtering and search on that field.
- compute: Links a Python method that calculates the value dynamically.
- store: When combined with
compute, persists the computed value in the database. - readonly: Prevents users from editing the field directly in the interface.
- copy: Controls whether the value is carried over when duplicating a record. Defaults to
True.
How It Appears in Views
In form views, a Date field renders as a text input with a calendar popup. In list views, it shows as formatted text. In search views, it supports date range filters out of the box: "this week", "this month", "this quarter", as well as exact comparison operators such as before, after, and on a specific date.
The display format follows the user's language settings in Odoo. An American user sees MM/DD/YYYY while a European user sees DD/MM/YYYY. The underlying stored value is always in ISO format (YYYY-MM-DD), regardless of how it appears on screen.
Interaction with the Odoo ORM
When reading a Date field in code, the Odoo ORM returns a Python datetime.date object, or False if the field is empty. When writing through the ORM, you can pass either a datetime.date object or a string in "YYYY-MM-DD" format. Through the XML-RPC API, dates are always passed and received as strings.
There are no complex transformations involved when working with Date fields in the Odoo data model. The framework handles formatting and storage automatically, which is part of what makes this field type so practical for everyday use in Odoo development.
Business Use Cases
The Date field appears across almost every part of an Odoo implementation. Here are five practical examples from real business workflows.
CRM: Contract Start and End Dates
In sales teams using Odoo CRM, contracts often have defined start and end dates. A Date field on the lead or a custom contract model makes it easy to track when deals activate and when they expire.
Combined with automated actions, you can trigger email reminders or status changes when a contract is approaching its end date, without any manual follow-up from the team. This kind of automation is straightforward to set up and saves a lot of missed renewals over time.
Sales: Requested Delivery Dates
Customers frequently request a specific delivery date when placing orders. Many companies add a "Customer Requested Date" Date field on sale orders to give the operations team a clean target date to plan around.
Using a Date field rather than DateTime keeps the data simple and avoids time zone confusion during warehouse coordination. The warehouse team sees the same date the sales team entered, with no risk of conversion issues.
Inventory: Expiration Dates on Lots
In food, pharmaceutical, and chemical industries, product lots need expiration dates tracked at the warehouse level. Odoo's lot management uses Date fields to store expiration and best-before dates on stock.lot.
These drive FEFO (First Expired First Out) picking strategies and automated alerts when stock is approaching expiry, protecting both regulatory compliance and product quality without requiring manual checks.
Accounting: Invoice Due Dates
Invoice due dates in Odoo are Date fields. They determine when automatic payment reminders are sent, how overdue invoices are identified, and how the aged payables and receivables reports are calculated.
Getting these dates right is one of the most operationally important uses of the Date field in the entire Odoo data model. A wrong due date silently breaks cash flow tracking and follow-up automation.
HR: Onboarding, Contract, and Certification Dates
HR teams rely heavily on Date fields for hire dates, probation end dates, contract start and end dates, and certification expiry dates. These values feed into automated emails, alerts, and payroll calculation rules.
A well-designed HR module in Odoo is impossible without clean, accurate Date field data across employee records. These dates are often the triggers for some of the most business-critical automation in an Odoo setup.
Creating or Customizing the Date Field
There are three main ways to add a Date field to an Odoo model, depending on your technical setup and deployment approach.
Using Odoo Studio (No Code)
Odoo Studio is the simplest way to add a Date field without writing any code. This is the recommended approach for business users and consultants who want to add date tracking to standard Odoo forms quickly:
- Open Odoo Studio from the main menu.
- Navigate to the form where you want the field.
- Drag a Date field from the sidebar onto the form.
- Set the label, required status, and a default value if needed.
- Save and close Studio.
Studio creates the field with an x_studio_ prefix and adds it to the form view immediately. No database migration is needed on your side.
Using Python in a Custom Module
For developers building Odoo modules, Date fields are defined in Python model files. This is the right approach for any customization that needs to be version-controlled and deployed across multiple environments:
from odoo import fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
x_customer_requested_date = fields.Date(
string='Customer Requested Date',
index=True,
copy=False,
help='Delivery date requested by the customer at time of order.',
)
After adding the field to the model, you also need to add it to the relevant view XML file so it appears in the interface. Odoo handles the database column creation when you install or upgrade the module.
Using the XML-RPC API
For programmatic field creation as part of a deployment pipeline or a remote configuration notebook, you can create Date fields via the Odoo XML-RPC API:
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_customer_requested_date',
'field_description': 'Customer Requested Date',
'model_id': model_id,
'ttype': 'date',
'state': 'manual',
}]
)
The ttype value for a Date field is 'date'. For a DateTime field it would be 'datetime'. Using 'manual' as the state tells Odoo this field was created manually, not by a module installation. This is how Dasolo manages remote field creation for clients as part of automated configuration scripts.
Best Practices
1. Use Date, not DateTime, when time is irrelevant
If the only thing that matters is the calendar date (a deadline, a contract date, a birth date, an expiration date), use fields.Date. Choosing DateTime for purely date-based data adds time zone complexity with no practical benefit, and can cause confusing off-by-one-day bugs depending on the server's time zone configuration.
2. Set a meaningful default when it makes sense
For fields like "Expected Delivery Date" or "Follow-up Date", defaulting to today or today plus a number of days gives users a sensible starting point. Use default=fields.Date.today without parentheses for a dynamic default that evaluates at record creation time, not at class definition time.
3. Index date fields used in reports and search filters
If your users regularly filter records by a date field (overdue invoices, upcoming renewals, expiring certifications), add index=True. On large datasets, this dramatically improves filter and report performance. It is a small change with a significant impact at scale and costs almost nothing to implement upfront.
4. Use copy=False for dates that should not carry over
Dates like "Contract Start Date" or "Expiration Date" should not be copied when a user duplicates a record. Adding copy=False forces the user to set the date explicitly on the new record, avoiding silent errors where an outdated date persists on a duplicated contract or project task.
5. Add constraints when two date fields form a range
Whenever a model has a "Start Date" and "End Date" pair, add a Python constraint using @api.constrains to ensure the end date is not before the start date. This is straightforward to implement and prevents a whole category of data quality problems that are annoying to diagnose after the fact.
Common Pitfalls
Confusing Date and DateTime fields
This is the most frequent mistake when working with time-sensitive data in Odoo. DateTime stores a full timestamp in UTC and converts on display. Date stores only the calendar date with no conversion. Using DateTime when you only need a date leads to time zone issues: a date entered as March 15 in a UTC+2 environment may appear as March 14 for a UTC-5 user.
Always use the simplest field type that meets your actual business need.
Not accounting for server time zone in scheduled actions
When building scheduled actions or reports that filter on a Date field relative to "today", be aware that fields.Date.today() returns the server date in UTC. For most reporting use cases this is fine, but it can cause one-day discrepancies for teams in time zones that differ significantly from the server. Test date-dependent automations from multiple time zones if your team is distributed globally.
Missing constraints on date range pairs
It is common to see implementations where "Start Date" and "End Date" exist as separate fields but no validation ensures the end date is after the start date. This lets invalid data enter the system quietly, breaking duration calculations, filter results, and reports. Always add a @api.constrains check when two dates form a logical range.
Forgetting to index date fields used in filters
Date fields used in "overdue" checks, "upcoming deadlines" views, or aged balance reports are typically queried with comparisons like "date less than today". Without an index, every such query is a full table scan. On models with large record counts like invoices, tasks, or stock moves, missing indexes on Date fields noticeably slow down daily operations over time.
Storing dates as Char fields
Some teams store dates as text strings in Char fields to preserve a custom format or avoid the date picker. This completely breaks sorting, filtering, date arithmetic, and reporting. Always use the proper fields.Date type. The Odoo framework and database handle formatting and localization automatically, so there is no practical reason to store dates as plain text.
FAQ
What is the difference between a Date field and a DateTime field in Odoo?
A Date field stores only a calendar date (year, month, day) with no time component. A DateTime field stores a full timestamp including hours, minutes, and seconds, saved in UTC and displayed in the user's local time zone. Use Date when the time of day is irrelevant to the business process. Use DateTime when you need to track exactly when something happened.
How do I set today's date as the default for a Date field?
In Python, use default=fields.Date.today without parentheses. This is a callable that Odoo evaluates at record creation time, so the default is always the current date rather than a fixed date baked in at class definition. In Odoo Studio, select "Today" as the default value in the field properties panel.
Can I compute a Date field based on other field values?
Yes. Define the field with compute='_compute_my_date' and write a method decorated with @api.depends() listing the fields that trigger recomputation. Inside the method, perform date calculations using Python's datetime.date or timedelta. Add store=True if you want the result saved in the database for use in search filters, grouping, and exports.
How do I filter records by a date range in an Odoo domain?
Use standard comparison operators in the domain. For example, to find records where the date field falls in March 2026:
[
('x_date_field', '>=', '2026-03-01'),
('x_date_field', '<=', '2026-03-31')
]
Date strings in domains should always be in ISO format: YYYY-MM-DD.
Can I make a Date field mandatory only under certain conditions?
Standard Odoo does not enforce conditional required fields at the backend without code. You can use attrs in view XML to visually flag a field as required based on other values, but true backend enforcement needs a Python constraint using @api.constrains. For simple cases, Odoo Studio's conditional visibility rules provide a practical substitute without writing code.
Conclusion
The Date field is one of the most practical field types in the Odoo framework. It is simple to understand, straightforward to use, and powerful enough to drive important business logic like reminders, overdue checks, and time-sensitive reporting.
The key things to take away: use Date rather than DateTime when you only need a calendar date, understand how defaults and indexes improve usability and performance, and always add constraints when two date fields define a range.
Building clean, well-designed data models in Odoo starts with small decisions like picking the right field type. Getting those right from the beginning is one of the hallmarks of a solid Odoo implementation, and the Date field is a foundational piece of that puzzle.
At Dasolo, we help companies implement, customize, and optimize Odoo across all departments and business processes. Whether you need help designing a clean data model, adding custom fields and workflows, or building a full Odoo module from scratch, our team is here to help. Reach out to us and let's talk about your Odoo project.