Проблема:
Независимо от того, какой диапазон дат я выбираю, диаграмма px.timeline всегда отображает ось X, начиная с 1 января 1970 года.
Тайна:
Этого не должно происходить. Я поместил print(data.dtypes) и print(data.head()) внутри своей функции @render_plotly прямо перед созданием фигуры. Консоль подтверждает, что data DataFrame не пуст и что мои столбцы fecha_inicio и fecha_fin имеют тип datetime64[ns] и содержат правильные даты (например, с 2025 года).
Похоже, что Plotly получает правильные объекты datetime 2025 года, но игнорирует их и по умолчанию используется эпоха Unix (1970 г.).
Вот весь код и данные, необходимые для воспроизведения этой ошибки.
1. app.py
(Вам необходимо установить Shiny, pandas,plotly, Shinywidgets, Shinyswatch)
Код: Выделить всё
from shiny.express import render, ui, input
import pandas as pd
from shiny import reactive
from pathlib import Path
import plotly.express as px
from shinywidgets import render_plotly
from shinyswatch import theme
try:
df_calderas = pd.read_csv('calderas.csv')
# Convert to datetime on load
df_calderas['fecha_inicio'] = pd.to_datetime(df_calderas['fecha_inicio'])
df_calderas['fecha_fin'] = pd.to_datetime(df_calderas['fecha_fin'])
df_calderas['caldera'] = df_calderas['caldera'].str.capitalize()
except FileNotFoundError:
print("ERROR: calderas.csv not found.")
df_calderas = pd.DataFrame(columns=["caldera", "fecha_inicio", "fecha_fin"])
ui.page_opts(theme=theme.united)
ui.h1("Dashboard Bug", style="text-align: center;")
# --- 2. UI DATE INPUTS (FLATPICKR) ---
with ui.card(style="margin-bottom: 20px;"):
ui.h5("Select Date Range", style="text-align: center;")
with ui.tags.div(class_="input-container"):
with ui.tags.div(class_="input-group"):
ui.tags.label("From:", _for="inicio")
ui.tags.input(id="inicio", type="text", class_="flatpickr")
with ui.tags.div(class_="input-group"):
ui.tags.label("To:", _for="fin")
ui.tags.input(id="fin", type="text", class_="flatpickr")
# --- 3. REACTIVE FILTER ---
@reactive.calc
def filtered_calderas():
start_date_str = input.inicio()
end_date_str = input.fin()
# Wait for inputs
if not start_date_str or not end_date_str:
return pd.DataFrame(columns=df_calderas.columns)
# Parse dates from flatpickr
formato_fecha = "%d-%m-%Y %H:%M"
start_date = pd.to_datetime(start_date_str, format=formato_fecha)
end_date = pd.to_datetime(end_date_str, format=formato_fecha)
# Filter logic
condicion1 = df_calderas['fecha_inicio'] = start_date
return df_calderas[condicion1 & condicion2]
# --- 4. THE PROBLEMATIC PLOTLY CHART ---
with ui.card(style="margin-top: 20px;"):
ui.tags.h4("Boiler On-Time Chart (The Problem)")
@render_plotly
def show_calderas():
data = filtered_calderas()
# If data is empty, plot an empty chart
if data.empty:
print("--- Filtered data is EMPTY. Plotting empty chart. ---")
return px.timeline(title="No data for selected dates")
# --- THIS IS THE MYSTERY ---
# My console prints prove the data is correct!
print("\n--- DATA BEING SENT TO PLOTLY ---")
print(data.dtypes)
print(data.head())
print("---------------------------------\n")
orden_eje_y = sorted(data['caldera'].unique())
fig = px.timeline(
data_frame=data,
x_start="fecha_inicio", # Passing the correct datetime col
x_end="fecha_fin", # Passing the correct datetime col
template="simple_white",
y="caldera",
color="caldera",
title="Boiler On-Time",
category_orders={"caldera": orden_eje_y}
)
fig.update_layout(
xaxis_title="Date and Time",
yaxis_title="Boilers",
xaxis=dict(tickformat="%d-%m-%Y %H:%M") # Set date format
)
fig.update_xaxes(
tickangle=60,
nticks=20,
)
return fig
# --- 5. JAVASCRIPT FOR FLATPICKR ---
ui.tags.link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css")
ui.tags.script(src="https://cdn.jsdelivr.net/npm/flatpickr")
ui.tags.script(src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/es.js") # Spanish locale
ui.tags.script("""
$(document).ready(function() {
const config = {
enableTime: true,
enableSeconds: false,
dateFormat: "d-m-Y H:i",
time_24hr: true,
locale: "es"
};
// Initialize inputs
const fp_inicio = flatpickr("#inicio", {
...config,
defaultDate: "20-10-2025 06:00", // Set default for testing
onChange: function(selectedDates, dateStr) {
if (dateStr) { Shiny.setInputValue("inicio", dateStr, {priority: "event"}); }
}
});
const fp_fin = flatpickr("#fin", {
...config,
defaultDate: "21-10-2025 18:00", // Set default for testing
onChange: function(selectedDates, dateStr) {
if (dateStr) { Shiny.setInputValue("fin", dateStr, {priority: "event"}); }
}
});
// Trigger the inputs on app load so it filters immediately
Shiny.setInputValue("inicio", "20-10-2025 06:00", {priority: "event"});
Shiny.setInputValue("fin", "21-10-2025 18:00", {priority: "event"});
});
""")
(Поместите этот файл в тот же каталог, что и app.py)
Код: Выделить всё
caldera,fecha_inicio,fecha_fin
Caldera1,2025-10-20 08:00:00,2025-10-20 10:30:00
Caldera2,2025-10-20 09:15:00,2025-10-20 11:00:00
Caldera3,2025-10-20 10:00:00,2025-10-20 14:45:00
Caldera1,2025-10-21 14:00:00,2025-10-21 15:10:00
Caldera2,2025-10-21 07:00:00,2025-10-21 12:30:00
Caldera3,2025-10-21 11:30:00,2025-10-21 16:00:00
Когда я запускаю приложение, консоль печатает это, что доказывает, что моя функция filtered_calderas() работает и отправляет правильные данные в Plotly:
Код: Выделить всё
--- DATA BEING SENT TO PLOTLY ---
caldera object
fecha_inicio datetime64[ns]
fecha_fin datetime64[ns]
dtype: object
caldera fecha_inicio fecha_fin
2 Caldera3 2025-10-20 10:00:00 2025-10-20 14:45:00
3 Caldera1 2025-10-20 14:00:00 2025-10-20 15:10:00
4 Caldera2 2025-10-21 07:00:00 2025-10-21 12:30:00
5 Caldera1 2025-10-21 11:00:00 2025-10-21 16:00:00
---------------------------------
- Часовые пояса: Я подозревал конфликт часовых поясов (наивный и осознанный). Я пробовал использовать .dt.tz_localize(None) как для данных CSV, так и для входных дат, но ошибка 1970 года сохраняется.
- Строки вручную: Я пытался преобразовать даты и время в строки вручную с помощью strftime, прежде чем передавать их в Plotly. Это также приводит к 1970 году.
- Перезапуск: я перезапускал ядро и приложение много раз.

Подробнее здесь: https://stackoverflow.com/questions/798 ... -correct-d
Мобильная версия