Enable Dark Mode!
how-to-handle-timezones-and-date-fields-from-odoo-for-mobo.jpg
By: Shalbin MP

How to Handle Timezones and Date Fields from Odoo for Mobo

Technical Flutter mobo

When building Flutter applications that integrate with Odoo, one of the most common challenges developers face is handling datetime fields correctly. Odoo stores all datetime values in UTC in the database, but displays them in the user's timezone in the web interface. If your Flutter app doesn't handle this conversion properly, you'll show incorrect times to users—a critical bug for any business application.

In this comprehensive guide, we'll explore how to properly handle time zones and date fields when working with Odoo's API in Flutter, ensuring your app displays accurate times regardless of where your users are located.

Understanding the Problem: UTC vs Local Time

Odoo follows a best practice for datetime storage: all datetime fields are stored in UTC (Coordinated Universal Time) in the PostgreSQL database. When a user creates a record with a datetime field, Odoo converts their local time to UTC before saving it.

For example, if a user in New York (UTC-5) creates a meeting scheduled for 2:00 PM, Odoo stores "2025-02-14 19:00:00" in the database (7:00 PM UTC). The Odoo web interface automatically converts this back to 2:00 PM when displaying it to the New York user.

The Challenge in Flutter

When you fetch data from Odoo's API, you receive the raw UTC datetime string. If you display this directly in your Flutter app without conversion, users will see the wrong time. You need to:

  1. Parse the UTC datetime string from Odoo
  2. Convert it to the user's local timezone
  3. Display it in a user-friendly format
  4. When sending data back to Odoo, convert local time back to UTC

Odoo Date Field Types

Odoo has two main date-related field types that you'll encounter when building Flutter applications:

1. Date Fields

Date fields store only the date without time information. These are timezone-agnostic and much simpler to handle. Odoo returns date fields in the format YYYY-MM-DD, for example: "2025-02-14."

2. Datetime Fields

Datetime fields store both date and time, always in UTC. These require timezone conversion for proper display. Odoo returns datetime fields in the format YYYY-MM-DD HH:MM:SS, for example: "2025-02-14 19:00:00".

// Odoo returns: "2025-02-14 19:00:00"
// Format: YYYY-MM-DD HH:MM:SS (always UTC)
// Must convert to user's timezone

Setting Up Your Flutter Project

To handle time zones effectively in Flutter, you'll need to add specific dependencies to your project. These packages provide the necessary tools for parsing, formatting, and converting datetime values.

Step 1: Add Dependencies

First, add the necessary dependencies to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  intl: ^0.19.0          # For date formatting
  timezone: ^0.9.2       # For timezone conversions

Install the packages by running:

flutter pub get

Step 2: Import Required Packages

Import the necessary packages in your Dart file:

import 'package:intl/intl.dart';
import 'package:timezone/timezone.dart' as tz;

Creating a Timezone Utility Class

The best approach is to create a utility class that handles all timezone conversions in one centralized location. This makes your code cleaner and more maintainable and reduces the chance of errors.

Here's a comprehensive utility class for handling Odoo date and datetime fields:

import 'package:intl/intl.dart';
class OdooDateTimeHelper {
  /// Parse Odoo datetime string (UTC) to local DateTime
  static DateTime? parseOdooDateTime(String? odooDateTime) {
    if (odooDateTime == null || odooDateTime.isEmpty) return null;
    
    try {
      // Parse as UTC and convert to local
      final utcDateTime = DateTime.parse(odooDateTime).toUtc();
      return utcDateTime.toLocal();
    } catch (e) {
      print('Error parsing Odoo datetime: $e');
      return null;
    }
  }
  /// Parse Odoo date string to DateTime
  static DateTime? parseOdooDate(String? odooDate) {
    if (odooDate == null || odooDate.isEmpty) return null;
    
    try {
      return DateTime.parse(odooDate);
    } catch (e) {
      print('Error parsing Odoo date: $e');
      return null;
    }
  }
  /// Convert local DateTime to Odoo format (UTC string)
  static String toOdooDateTime(DateTime localDateTime) {
    final utcDateTime = localDateTime.toUtc();
    return DateFormat('yyyy-MM-dd HH:mm:ss').format(utcDateTime);
  }
  /// Convert DateTime to Odoo date format
  static String toOdooDate(DateTime date) {
    return DateFormat('yyyy-MM-dd').format(date);
  }
  /// Format DateTime for display
  static String formatDateTime(DateTime? dateTime, {String? format}) {
    if (dateTime == null) return '';
    final formatter = DateFormat(format ?? 'MMM dd, yyyy hh:mm a');
    return formatter.format(dateTime);
  }
  /// Format date for display
  static String formatDate(DateTime? date, {String? format}) {
    if (date == null) return '';
    final formatter = DateFormat(format ?? 'MMM dd, yyyy');
    return formatter.format(date);
  }
}

Understanding the Key Methods

Let's break down each method in the OdooDateTimeHelper class and understand how they work:

1. parseOdooDateTime(String? odooDateTime)

This method takes a UTC datetime string from Odoo and converts it to the user's local timezone. The method:

  • Checks if the input is null or empty
  • Parses the string as a UTC datetime
  • Converts it to the device's local timezone
  • Returns null if parsing fails

Example usage:

// Input from Odoo: "2025-02-14 19:00:00" (UTC)
final localTime = OdooDateTimeHelper.parseOdooDateTime(
  "2025-02-14 19:00:00"
);
// Output for user in UTC-5: 2025-02-14 14:00:00

2. toOdooDateTime(DateTime localDateTime)

This method converts a local datetime back to UTC format for sending to Odoo. When creating or updating records in Odoo, you must use this method to ensure the datetime is stored correctly.

Example usage:

// User selects: 2:00 PM local time
final selectedTime = DateTime(2025, 2, 14, 14, 0);
final odooFormat = OdooDateTimeHelper.toOdooDateTime(selectedTime);
// Output: "2025-02-14 19:00:00" (UTC)

3. formatDateTime() and formatDate()

These formatting methods convert DateTime objects into user-friendly strings for display in your UI. They support custom format patterns using the DateFormat syntax.

Example usage:

final dateTime = OdooDateTimeHelper.parseOdooDateTime(
  "2025-02-14 19:00:00"
);
// Default format: "Feb 14, 2025 02:00 PM"
print(OdooDateTimeHelper.formatDateTime(dateTime));
// Custom format: "14/02/2025 14:00"
print(OdooDateTimeHelper.formatDateTime(
  dateTime,
  format: 'dd/MM/yyyy HH:mm'
));

Practical Example: Building a Meeting Application

Let's create a complete example that demonstrates how to fetch calendar events from Odoo and display them with correct local times in your Flutter application.

Step 1: Define the Meeting Model

Create a model class that represents a calendar meeting. This class includes methods to parse data from Odoo and convert it back for API calls.

class Meeting {
  final int id;
  final String name;
  final DateTime? startDate;
  final DateTime? endDate;
  final String? location;
  Meeting({
    required this.id,
    required this.name,
    this.startDate,
    this.endDate,
    this.location,
  });
  /// Create Meeting from Odoo data
  factory Meeting.fromOdoo(Map<String, dynamic> data) {
    return Meeting(
      id: data['id'],
      name: data['name'] ?? '',
      // Parse UTC datetime from Odoo to local time
      startDate: OdooDateTimeHelper.parseOdooDateTime(
        data['start_datetime']
      ),
      endDate: OdooDateTimeHelper.parseOdooDateTime(
        data['stop_datetime']
      ),
      location: data['location'],
    );
  }
  /// Convert Meeting to Odoo format
  Map<String, dynamic> toOdoo() {
    return {
      'name': name,
      // Convert local time back to UTC for Odoo
      'start_datetime': startDate != null
          ? OdooDateTimeHelper.toOdooDateTime(startDate!)
          : false,
      'stop_datetime': endDate != null
          ? OdooDateTimeHelper.toOdooDateTime(endDate!)
          : false,
      'location': location ?? '',
    };
  }
}

Key Points:

  1. factory Meeting.fromOdoo(Map<String, dynamic> data)
  2. This factory constructor creates a Meeting instance from Odoo data. It automatically converts UTC datetime strings to local DateTime objects using OdooDateTimeHelper.parseOdooDateTime().

  3. Map<String, dynamic> toOdoo()
  4. This method prepares the meeting data for sending to Odoo's API. It converts local DateTime objects back to UTC strings using OdooDateTimeHelper.toOdooDateTime(). Note that we set the value to false if the datetime is null, as this is how Odoo expects it.

Step 2: Display Meetings in the UI

Create a widget to display meeting information with properly formatted dates and times:

class MeetingCard extends StatelessWidget {
  final Meeting meeting;
  const MeetingCard({required this.meeting});
  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(8),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              meeting.name,
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 8),
            Row(
              children: [
                Icon(Icons.access_time, size: 16),
                SizedBox(width: 4),
                Text(
                  'Start: ${OdooDateTimeHelper.formatDateTime(
                    meeting.startDate
                  )}',
                ),
              ],
            ),
            Row(
              children: [
                Icon(Icons.access_time, size: 16),
                SizedBox(width: 4),
                Text(
                  'End: ${OdooDateTimeHelper.formatDateTime(
                    meeting.endDate
                  )}',
                ),
              ],
            ),
            if (meeting.location != null) ...[
              SizedBox(height: 4),
              Row(
                children: [
                  Icon(Icons.location_on, size: 16),
                  SizedBox(width: 4),
                  Text(meeting.location!),
                ],
              ),
            ],
          ],
        ),
      ),
    );
  }
}

Common Pitfalls and How to Avoid Them

When working with time zones in Flutter apps that integrate with Odoo, developers often encounter these common mistakes. Here's how to avoid them:

1. Displaying Raw UTC Strings

Wrong Approach:

// BAD: Displays UTC time to user
Text(data['start_datetime'])  // Shows "2025-02-14 19:00:00"

Correct Approach:

// GOOD: Converts to local time first
final localTime = OdooDateTimeHelper.parseOdooDateTime(
  data['start_datetime']
);
Text(OdooDateTimeHelper.formatDateTime(localTime));

2. Forgetting to Convert Back to UTC

Wrong Approach:

// BAD: Sends local time, Odoo treats it as UTC
await odooClient.create('calendar.event', {
  'start': selectedDateTime.toString(),
});

Correct Approach:

// GOOD: Converts local to UTC before sending
await odooClient.create('calendar.event', {
  'start': OdooDateTimeHelper.toOdooDateTime(selectedDateTime),
});

3. Confusing Date and Datetime Fields

Remember that date fields and datetime fields require different handling. Date fields don't need timezone conversion:

// Date field - no conversion needed
final birthDate = OdooDateTimeHelper.parseOdooDate(
  data['date_of_birth']  // "2025-02-14"
);
// Datetime field - conversion required
final createDate = OdooDateTimeHelper.parseOdooDateTime(
  data['create_date']  // "2025-02-14 19:00:00"
);

Best Practices for Time Zone Handling

Follow these best practices to ensure your Flutter app handles time zones correctly when working with Odoo:

  1. Always Convert UTC to Local: Never display raw datetime strings from Odoo directly to users. Always parse and convert them to local time first.
  2. Always Convert Local to UTC: Before sending datetime data back to Odoo, convert it from local time to UTC using the utility methods.
  3. Centralize Timezone Logic: Use a utility class for all timezone conversions. This makes your code more maintainable and reduces errors.
  4. Know Your Field Types: Understand whether you're working with a date field or a datetime field. Date fields don't need timezone conversion.
  5. Handle Null Values: Always check for null datetime fields from Odoo and handle them gracefully in your code.
  6. Use Consistent Formatting: Define standard date and time formats for your app and use them consistently throughout.
  7. Test Across Time Zones: Test your app in different time zone settings to ensure it works correctly for all users.

Handling time zones correctly is essential for any Mobo mobile application that integrates with Odoo. By understanding how Odoo stores datetime values in UTC and implementing proper conversion logic in your Flutter app, you ensure that users always see accurate local times.

The utility class and patterns demonstrated in this guide provide a solid foundation for timezone handling in your projects. Remember the golden rule: always convert UTC to local when reading from Odoo, and always convert local to UTC when writing to Odoo. With these practices in place, your Flutter app will handle date and time data reliably across all time zones.

To read more about How to Implement Offline Mode in Mobo with Odoo Data Sync, refer to our blog How to Implement Offline Mode in Mobo with Odoo Data Sync.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, KINFRA Techno Park
Kakkanchery, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message