Guys, I found the problem 
Problem Summary
- The plugin converted the time range twice:
-
The range coming from Grafana was forced to UTC and,
-
On the backend, the API converted it again to a hard-coded time zone (Europe/Berlin / Avrupa/Istanbul).
-
This double offset caused a ~3 hour cutoff at the beginning or end of the graphs when the environment was in America/Sao_Paulo (UTC-3).
-
The parser also converted times to time.Local, which could apply another offset depending on the host.
What I did
-
Standardized the timezone via the datasource settings and defined UTC as the fallback.
-
Removed .UTC() when calling the API — the API itself handles the conversion with the TZ configured.
-
In the parser:
-
Parses in the configured timezone;
-
Returns always in UTC (consistent epoch);
-
No extra conversion to time.Local.
- In the historical call:
Altered excerpts
pkg/plugin/parsetime.go
Main changes:
- var defaultTimezone = "Avrupa/Istanbul"
+ var defaultTimezone = "UTC" // safe fallback
+ func SetDefaultTimezone(timezone string) {
+ tz := strings.TrimSpace(timezone)
+ if tz == "" { backend.Logger.Info("No timezone provided..."); return }
+ if _, err := time.LoadLocation(tz); err != nil {
+ backend.Logger.Warn("Invalid timezone..., using UTC", "provided", tz, "error", err)
+ defaultTimezone = "UTC"; return
+ }
+ defaultTimezone = tz
+ backend.Logger.Info("Setting default timezone for date parsing", "timezone", defaultTimezone)
+ }
- // inside parsePRTGDateTime(...)
- destLoc := time.Local
+ // removed: does not convert to time.Local
- parsedTime, _ := time.ParseInLocation(layout, datetime, sourceLoc)
- utcTime := parsedTime.UTC()
- localTime := utcTime.In(destLoc)
- return localTime, strconv.FormatInt(localTime.Unix(), 10), nil
+ if t, err := time.ParseInLocation(layout, datetime, sourceLoc); err == nil {
+ tUTC := t.In(time.UTC) // normalizes to UTC
+ return tUTC, strconv.FormatInt(tUTC.Unix(), 10), nil
+ }
And also: when PRTG returns “date timeA - timeB” and timeB < timeA, add +1 day to cover midnight crossing.
pkg/plugin/prtg_api.go → function GetHistoricalData(...)
Complete function change (key points highlighted):
func (a *Api) GetHistoricalData(sensorID string, startDate, endDate time.Time) (*PrtgHistoricalDataResponse, error) {
if sensorID == "" { return nil, fmt.Errorf("invalid query: missing sensor ID") }
- // old: TZ hard-coded and buffer ±1h
- loc, _ := time.LoadLocation("Europe/Berlin")
- localStartDate := startDate.In(loc).Add(-1 * time.Hour)
- localEndDate := endDate.In(loc).Add( 1 * time.Hour)
+ // new: uses TZ configured in settings; fallback UTC
+ loc, err := time.LoadLocation(defaultTimezone)
+ if err != nil { loc = time.UTC }
+ // small buffer only for edges
+ localStartDate := startDate.In(loc).Add(-5 * time.Minute)
+ localEndDate := endDate.In(loc).Add( 5 * time.Minute)
const format = "2006-01-02-15-04-05"
sdate := localStartDate.Format(format)
edate := localEndDate.Format(format)
// ... logic for choosing 'avg' preserved ...
// ... request and cache unchanged ...
log.DefaultLogger.Debug("Requesting historical data",
- "startDate", sdate, "endDate", edate, "avg", avg)
+ "startDate", sdate, "endDate", edate, "avg", avg, "timezone", defaultTimezone)
}
pkg/plugin/query.go
Ensure not passes .UTC() in the call:
- historicalData, err := d.api.GetHistoricalData(qm.SensorId, timeRange.From.UTC(), timeRange.To.UTC())
+ historicalData, err := d.api.GetHistoricalData(qm.SensorId, timeRange.From, timeRange.To)
Standardize timestamps to UTC when filling frames:
- timesRT = append(timesRT, timestamp)
+ timesRT = append(timesRT, timestamp.UTC())
and
- timesRT = []time.Time{timestamp}
+ timesRT = []time.Time{timestamp.UTC()}
Result
-
The selected interval in Grafana (e.g., Last 6/12 h) is now exactly respected;
-
Plugin logs now show Parsing datetime ... timezone=America/Sao_Paulo (or whatever you define in the settings);
-
No “cutoff” of ~3 h and no unexpected shifts between panes.
@maxmarkusprogram feel free to test and commit to Git if you want 