Files
Odoo-Modules/fusion_clock/models/tz_utils.py
gsinghpal 1a1ab2da4f fix(fusion_clock): tz resolver uses company.partner_id.tz (res.company has no tz in Odoo 19)
_resolve_tz fell back to env.company.tz, which raises AttributeError for any
user without a personal tz (surfaced by the new list-wide pay-period filters,
which resolve a company-level tz). Use env.company.partner_id.tz. Regression
test added. Bump 3.15.0 -> 3.15.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:23:27 -04:00

78 lines
2.6 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""
Timezone helpers for Fusion Clock.
All Odoo datetimes are stored in UTC. These helpers derive "today",
date boundaries, and display strings in the **user's local timezone**
so that queries, penalties, and UI all reflect the real calendar day.
Timezone resolution order:
1. Explicit employee.tz (if an employee record is available)
2. env.user.tz (logged-in portal / backend user)
3. env.company.partner_id.tz (company-level default; res.company has
no tz field in Odoo 19 — it lives on the
company's partner)
4. 'UTC' (last resort — should rarely happen)
"""
import pytz
from datetime import datetime, timedelta
def _resolve_tz(env, employee=None):
"""Return a pytz timezone from the best available source."""
tz_name = (
(employee.tz if employee else None)
or env.user.tz
or (env.company.partner_id.tz if env.company.partner_id else None)
or 'UTC'
)
try:
return pytz.timezone(tz_name)
except pytz.UnknownTimeZoneError:
return pytz.UTC
def get_local_now(env, employee=None):
"""Return the current datetime in the resolved local timezone (aware)."""
tz = _resolve_tz(env, employee)
return datetime.now(pytz.UTC).astimezone(tz)
def get_local_today(env, employee=None):
"""Return today's date in the resolved local timezone."""
return get_local_now(env, employee).date()
def get_local_day_boundaries(env, date_val, employee=None):
"""
Return (start_utc, end_utc) as **naive** UTC datetimes representing
midnight-to-midnight of *date_val* in the local timezone.
Suitable for Odoo domain filters like:
('check_in', '>=', start_utc),
('check_in', '<', end_utc),
"""
tz = _resolve_tz(env, employee)
local_start = tz.localize(datetime.combine(date_val, datetime.min.time()))
local_end = tz.localize(datetime.combine(date_val + timedelta(days=1), datetime.min.time()))
return (
local_start.astimezone(pytz.UTC).replace(tzinfo=None),
local_end.astimezone(pytz.UTC).replace(tzinfo=None),
)
def utc_to_local_str(dt_utc, env, employee=None, fmt='%I:%M %p'):
"""
Convert a naive-UTC datetime to a formatted string in local timezone.
Returns '' if dt_utc is falsy.
"""
if not dt_utc:
return ''
tz = _resolve_tz(env, employee)
aware = pytz.UTC.localize(dt_utc)
return aware.astimezone(tz).strftime(fmt)