Skip to content

Commit 032978d

Browse files
committed
Fix select over multiple timezone interval.
E.g. when the start timestamp would be in CET and the end timestamp in CEST.
1 parent cc8babc commit 032978d

File tree

4 files changed

+70
-23
lines changed

4 files changed

+70
-23
lines changed

docs/changelog.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 0.16.1
4+
5+
**Bugfixes:**
6+
7+
* Fix getting gateway stats when start timestamp is in an other timezone than
8+
end timestamp (eg. in case of Europe/Amsterdam when changing from CET to
9+
CEST).
10+
311
## 0.16.0
412

513
**Note:** LoRa Server now requires a PostgreSQL (9.5+) database to persist the

internal/gateway/gateway.go

+55-22
Original file line numberDiff line numberDiff line change
@@ -308,21 +308,34 @@ func GetGatewaysForMACs(db *sqlx.DB, macs []lorawan.EUI64) (map[lorawan.EUI64]Ga
308308
func GetGatewayStats(db *sqlx.DB, mac lorawan.EUI64, interval string, start, end time.Time) ([]Stats, error) {
309309
var valid bool
310310
interval = strings.ToUpper(interval)
311-
zone, _ := start.In(common.TimeLocation).Zone()
312311

312+
// validate aggregation interval
313313
for _, i := range statsAggregationIntervals {
314314
if i == interval {
315315
valid = true
316316
}
317317
}
318-
319318
if !valid {
320319
return nil, ErrInvalidAggregationInterval
321320
}
322321

323-
var stats []Stats
322+
tx, err := db.Beginx()
323+
if err != nil {
324+
return nil, errors.Wrap(err, "begin transaction error")
325+
}
326+
defer tx.Rollback()
327+
328+
// set the database timezone for this transaction
329+
if common.TimeLocation != time.Local {
330+
// when TimeLocation == time.Local, it would have 'Local' as name
331+
_, err = tx.Exec(fmt.Sprintf("set local time zone '%s'", common.TimeLocation.String()))
332+
if err != nil {
333+
return nil, errors.Wrap(err, "set timezone error")
334+
}
335+
}
324336

325-
err := db.Select(&stats, `
337+
var stats []Stats
338+
err = tx.Select(&stats, `
326339
select
327340
$1::bytea as mac,
328341
$2 as interval,
@@ -339,20 +352,19 @@ func GetGatewayStats(db *sqlx.DB, mac lorawan.EUI64, interval string, start, end
339352
where
340353
mac = $1
341354
and interval = $2
342-
and "timestamp" >= cast(date_trunc($2, $3 at time zone $4) as timestamp) at time zone $4
343-
and "timestamp" < $5) gs
355+
and "timestamp" >= cast(date_trunc($2, $3::timestamptz) as timestamp with time zone)
356+
and "timestamp" < $4) gs
344357
right join (
345358
select generate_series(
346-
cast(date_trunc($2, $3 at time zone $4) as timestamp) at time zone $4,
347-
$5,
348-
$6) as "timestamp"
359+
cast(date_trunc($2, $3) as timestamp with time zone),
360+
$4,
361+
$5) as "timestamp"
349362
) s
350363
on gs.timestamp = s.timestamp
351364
order by s.timestamp`,
352365
mac[:],
353366
interval,
354367
start,
355-
zone,
356368
end,
357369
fmt.Sprintf("1 %s", interval),
358370
)
@@ -431,9 +443,29 @@ func handleStatsPacket(db *sqlx.DB, stats gw.GatewayStatsPacket) error {
431443
}
432444
}
433445

446+
comitted := false
447+
tx, err := db.Beginx()
448+
if err != nil {
449+
return errors.Wrap(err, "begin transaction error")
450+
}
451+
defer func() {
452+
if !comitted {
453+
tx.Rollback()
454+
}
455+
}()
456+
457+
// set the database timezone for this transaction
458+
if common.TimeLocation != time.Local {
459+
// when TimeLocation == time.Local, it would have 'Local' as name
460+
_, err = tx.Exec(fmt.Sprintf("set local time zone '%s'", common.TimeLocation.String()))
461+
if err != nil {
462+
return errors.Wrap(err, "set timezone error")
463+
}
464+
}
465+
434466
// store the stats
435467
for _, aggr := range statsAggregationIntervals {
436-
if err := aggregateGatewayStats(db, Stats{
468+
if err := aggregateGatewayStats(tx, Stats{
437469
MAC: stats.MAC,
438470
Timestamp: stats.Time,
439471
Interval: aggr,
@@ -446,12 +478,14 @@ func handleStatsPacket(db *sqlx.DB, stats gw.GatewayStatsPacket) error {
446478
}
447479
}
448480

481+
if err := tx.Commit(); err != nil {
482+
return errors.Wrap(err, "commit error")
483+
}
484+
comitted = true
449485
return nil
450486
}
451487

452-
func aggregateGatewayStats(db *sqlx.DB, stats Stats) error {
453-
zone, _ := stats.Timestamp.In(common.TimeLocation).Zone()
454-
488+
func aggregateGatewayStats(db sqlx.Execer, stats Stats) error {
455489
_, err := db.Exec(`
456490
insert into gateway_stats (
457491
mac,
@@ -463,23 +497,22 @@ func aggregateGatewayStats(db *sqlx.DB, stats Stats) error {
463497
tx_packets_emitted
464498
) values (
465499
$1,
466-
cast(date_trunc($2, $3 at time zone $4) as timestamp) at time zone $4,
500+
cast(date_trunc($2, $3::timestamptz) as timestamp with time zone),
467501
$2,
502+
$4,
468503
$5,
469504
$6,
470-
$7,
471-
$8
505+
$7
472506
)
473507
on conflict (mac, "timestamp", "interval")
474508
do update set
475-
rx_packets_received = gateway_stats.rx_packets_received + $5,
476-
rx_packets_received_ok = gateway_stats.rx_packets_received_ok + $6,
477-
tx_packets_received = gateway_stats.tx_packets_received + $7,
478-
tx_packets_emitted = gateway_stats.tx_packets_emitted + $8`,
509+
rx_packets_received = gateway_stats.rx_packets_received + $4,
510+
rx_packets_received_ok = gateway_stats.rx_packets_received_ok + $5,
511+
tx_packets_received = gateway_stats.tx_packets_received + $6,
512+
tx_packets_emitted = gateway_stats.tx_packets_emitted + $7`,
479513
stats.MAC[:],
480514
stats.Interval,
481515
stats.Timestamp,
482-
zone,
483516
stats.RXPacketsReceived,
484517
stats.RXPacketsReceivedOK,
485518
stats.TXPacketsReceived,

internal/test/test.go

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ func init() {
3131
common.BandName = band.EU_863_870
3232
common.DeduplicationDelay = 5 * time.Millisecond
3333
common.GetDownlinkDataDelay = 5 * time.Millisecond
34+
35+
loc, err := time.LoadLocation("Europe/Amsterdam")
36+
if err != nil {
37+
panic(err)
38+
}
39+
common.TimeLocation = loc
3440
}
3541

3642
// Config contains the test configuration.

mkdocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pages:
1515
- changelog.md
1616

1717
extra:
18-
version: '0.16.0'
18+
version: '0.16.1'
1919
github:
2020
download_release: true
2121

0 commit comments

Comments
 (0)