🔍 What went wrong?
tgb.date and tgb.date_range used with with_time=False loose one day every time you select a new date and your browser time has a timezone with negative offset (e.g. "america/los_angeles" which is UTC-0700).
For e.g. (using US date format mm/dd/yyyy)
Date: select "06/06/2025" > date display will show "06/05/2025"
Date Range:
- initialized at 12/13/2024 - 12/05/2025
- select end = "12/10/2025" > displayed range will be 12/12/2024 - 12/09/2025
- select end = "12/12/2025" > displayed range will be 12/11/2024 - 12/11/2025
I was able to reproduce the issue with any timezone with negative offset ("america/los_angeles" which is UTC-0700, "america/new_york" which is UTC-0400, "america/sao_paulo" which is UTC-0300) when it works just fine with offset is positive (e.g. "europe/berlin" which is UTC+0200)
✅ Expected Behavior
Date: select "06/06/2025" > date display will show "06/06/2025"
Date Range:
- initialized at 12/13/2024 - 12/05/2025
- select end = "12/10/2025" > displayed range will be 12/13/2024 - 12/10/2025
- select end = "12/12/2025" > displayed range will be 12/13/2024 - 12/12/2025
🔄 Steps to Reproduce
I was able to reproduce the issue with any timezone with negative offset ("america/los_angeles" which is UTC-0700, "america/new_york" which is UTC-0400, "america/sao_paulo" which is UTC-0300) when it works just fine with offset is positive (e.g. "europe/berlin" which is UTC+0200)
"""Minimal Taipy app to exercise date_range via GUI builder."""
from __future__ import annotations
import datetime
import taipy as tp
import taipy.gui.builder as tgb
from taipy.gui import State
from taipy.gui.extension import ElementLibrary
# Date-only range (default: with_time=False)
dates = [
datetime.date(2025, 1, 1),
datetime.date(2025, 12, 31),
]
# Single date picker (date only, no time)
selected_date = datetime.date(2025, 6, 15)
def _format_date(label: str, value: datetime.date | None) -> str:
if value is None:
return f"**{label}:** (not set)"
return f"**{label}:** `{value}`"
def _format_range(label: str, value: list) -> str:
if not value or len(value) < 2:
return f"**{label}:** (incomplete range)"
start, end = value[0], value[1]
return f"**{label}:** `{start}` → `{end}`"
def _build_selection_summary(
dates_value: list,
selected_date_value: datetime.date | None,
) -> str:
return "\n\n".join(
[
_format_range("Date range", dates_value),
_format_date("Single date", selected_date_value),
]
)
selection_summary = _build_selection_summary(dates, selected_date)
def on_dates_change(state: State, var_name: str, var_value: list) -> None:
state.selection_summary = _build_selection_summary(state.dates, state.selected_date)
def on_selected_date_change(state: State, var_name: str, var_value: datetime.date) -> None:
state.selection_summary = _build_selection_summary(state.dates, state.selected_date)
class _BrowserClockLibrary(ElementLibrary):
"""Loads browser_clock.js in the app shell (inline page scripts do not run)."""
def get_name(self) -> str:
return "browser_clock"
def get_elements(self) -> dict:
return {}
def get_scripts(self) -> list[str]:
return ["browser_clock.js"]
with tgb.Page() as page:
tgb.text("# Date range test", mode="md")
with tgb.part():
tgb.text("### Browser time", mode="md")
with tgb.html("div", id="browser-clock", style="white-space: pre-line"):
tgb.html(None, "Loading…")
with tgb.part():
tgb.text("### Date range (no time)", mode="md")
tgb.date_range(
"{dates}",
label_start="Start date",
label_end="End date",
on_change=on_dates_change,
)
with tgb.part():
tgb.text("### Single date (no time)", mode="md")
tgb.date(
"{selected_date}",
label="Pick a date",
with_time=False,
on_change=on_selected_date_change,
)
with tgb.part():
tgb.text("### Current selection", mode="md")
tgb.text("{selection_summary}", mode="md")
if __name__ == "__main__":
gui = tp.Gui(page, libraries=[_BrowserClockLibrary()])
gui.run(title="Date range test", use_reloader=True, port="auto")
with browser_clock.js:
(function () {
const CLOCK_SELECTOR = "#browser-clock";
function formatOffset(date) {
const mins = -date.getTimezoneOffset();
const sign = mins >= 0 ? "+" : "-";
const abs = Math.abs(mins);
const h = String(Math.floor(abs / 60)).padStart(2, "0");
const m = String(abs % 60).padStart(2, "0");
return "UTC" + sign + h + ":" + m;
}
function tick(el) {
const now = new Date();
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const local = now.toLocaleString(undefined, {
dateStyle: "full",
timeStyle: "long",
});
el.textContent =
"Local: " +
local +
"\nTimezone: " +
tz +
"\nOffset: " +
formatOffset(now) +
"\nISO (UTC): " +
now.toISOString();
}
let started = false;
function startClock() {
if (started) {
return true;
}
const el = document.querySelector(CLOCK_SELECTOR);
if (!el) {
return false;
}
started = true;
tick(el);
window.setInterval(function () {
tick(el);
}, 1000);
return true;
}
function init() {
if (startClock()) {
return;
}
const root = document.getElementById("root");
if (!root) {
return;
}
const observer = new MutationObserver(function () {
if (startClock()) {
observer.disconnect();
}
});
observer.observe(root, { childList: true, subtree: true });
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();
The follow this to change your browser timezone in Chrome
💡 Possible Solution (Optional)
I believe the issue is how internally taipy is going from displayed date string to datetime representation to displayed date string.
select "06/06/2025" > becomes datetime.datetime(2025, 6, 6, 0, 0, tzinfo=datetime.timezone.utc) > which converted to browser time (UTC-7) will be datetime.datetime(2025, 6, 5, 17, 0, 0, tzinfo=ZoneInfo("America/Los_Angeles")) and thus "06/05/2025" is displayed.
Possibly related existing issue and question
🖼️ Screenshots (Optional)
Gif:
https://drive.google.com/file/d/13UXycsWza6a1TLUtmFQI_XsyHqp-3RJF/view?usp=sharing
💻 Runtime Environment
macOS 26.4 + Chrome Version 146.0.7680.178 (Official Build) (arm64)
🌐 Browser (if applicable)
Chrome
🖥️ Operating System
Mac
📦 Taipy Version
taipy==4.1.1 taipy-common==4.1.1 taipy-core==4.1.1 taipy-gui==4.1.2 taipy-rest==4.1.1 taipy-templates==4.1.1
📋 Additional Context (Optional)
📜 Code of Conduct
✅ Acceptance Criteria
🔍 What went wrong?
tgb.dateandtgb.date_rangeused withwith_time=Falseloose one day every time you select a new date and your browser time has a timezone with negative offset (e.g. "america/los_angeles" which is UTC-0700).For e.g. (using US date format mm/dd/yyyy)
Date: select "06/06/2025" > date display will show "06/05/2025"
Date Range:
I was able to reproduce the issue with any timezone with negative offset ("america/los_angeles" which is UTC-0700, "america/new_york" which is UTC-0400, "america/sao_paulo" which is UTC-0300) when it works just fine with offset is positive (e.g. "europe/berlin" which is UTC+0200)
✅ Expected Behavior
Date: select "06/06/2025" > date display will show "06/06/2025"
Date Range:
🔄 Steps to Reproduce
I was able to reproduce the issue with any timezone with negative offset ("america/los_angeles" which is UTC-0700, "america/new_york" which is UTC-0400, "america/sao_paulo" which is UTC-0300) when it works just fine with offset is positive (e.g. "europe/berlin" which is UTC+0200)
with
browser_clock.js:The follow this to change your browser timezone in Chrome
💡 Possible Solution (Optional)
I believe the issue is how internally taipy is going from displayed date string to datetime representation to displayed date string.
select "06/06/2025" > becomes
datetime.datetime(2025, 6, 6, 0, 0, tzinfo=datetime.timezone.utc)> which converted to browser time (UTC-7) will bedatetime.datetime(2025, 6, 5, 17, 0, 0, tzinfo=ZoneInfo("America/Los_Angeles"))and thus "06/05/2025" is displayed.Possibly related existing issue and question
🖼️ Screenshots (Optional)
Gif:
https://drive.google.com/file/d/13UXycsWza6a1TLUtmFQI_XsyHqp-3RJF/view?usp=sharing
💻 Runtime Environment
macOS 26.4 + Chrome Version 146.0.7680.178 (Official Build) (arm64)
🌐 Browser (if applicable)
Chrome
🖥️ Operating System
Mac
📦 Taipy Version
taipy==4.1.1 taipy-common==4.1.1 taipy-core==4.1.1 taipy-gui==4.1.2 taipy-rest==4.1.1 taipy-templates==4.1.1
📋 Additional Context (Optional)
📜 Code of Conduct
✅ Acceptance Criteria