Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.

Commit 1a99c65

Browse files
fix: time zone retrieval and logging (#906)
1 parent 5818398 commit 1a99c65

File tree

5 files changed

+74
-51
lines changed

5 files changed

+74
-51
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
# Change Log
44

5+
## 2025-06-23 - Runtime 0.18.0-alpha.13
6+
7+
- fix: time zone retrieval and logging [#906](https://github.com/hypermodeinc/modus/pull/906)
8+
59
## 2025-06-23 - Runtime 0.18.0-alpha.12
610

711
- fix: ensure valid UTF8 byte sequence in HTTP response [#904](https://github.com/hypermodeinc/modus/pull/904)

runtime/hostfunctions/system.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,25 +55,39 @@ func LogMessage(ctx context.Context, level, message string) {
5555
func GetTimeInZone(ctx context.Context, tz *string) *string {
5656
now := time.Now()
5757

58-
var loc *time.Location
58+
var zoneId string
5959
if tz != nil && *tz != "" {
60-
loc = timezones.GetLocation(*tz)
60+
zoneId = *tz
6161
} else if tz, ok := ctx.Value(utils.TimeZoneContextKey).(string); ok {
62-
loc = timezones.GetLocation(tz)
62+
zoneId = tz
63+
} else {
64+
logger.Error(ctx).Msg("Time zone not specified.")
65+
return nil
6366
}
6467

65-
if loc == nil {
68+
loc, err := timezones.GetLocation(ctx, zoneId)
69+
if err != nil {
70+
logger.Err(ctx, err).Str("tz", zoneId).Msg("Failed to get time zone location.")
6671
return nil
6772
}
6873

6974
s := now.In(loc).Format(time.RFC3339Nano)
7075
return &s
7176
}
7277

73-
func GetTimeZoneData(tz, format *string) []byte {
78+
func GetTimeZoneData(ctx context.Context, tz, format *string) []byte {
7479
if tz == nil {
80+
logger.Error(ctx).Msg("Time zone not specified.")
7581
return nil
7682
}
77-
78-
return timezones.GetTimeZoneData(*tz, *format)
83+
if format == nil {
84+
logger.Error(ctx).Msg("Time zone format not specified.")
85+
return nil
86+
}
87+
data, err := timezones.GetTimeZoneData(ctx, *tz, *format)
88+
if err != nil {
89+
logger.Error(ctx).Err(err).Str("tz", *tz).Msg("Failed to get time zone data.")
90+
return nil
91+
}
92+
return data
7993
}

runtime/timezones/timezones.go

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@
1010
package timezones
1111

1212
import (
13+
"context"
14+
"errors"
15+
"fmt"
1316
"os"
1417
"time"
1518

1619
"github.com/puzpuzpuz/xsync/v4"
1720
)
1821

19-
type tzInfo struct {
20-
location *time.Location
21-
data []byte
22-
}
23-
2422
var systemTimeZone string
25-
var tzCache = *xsync.NewMap[string, *tzInfo]()
23+
var tzDataCache = *xsync.NewMap[string, []byte]()
24+
var tzLocationCache = *xsync.NewMap[string, *time.Location]()
2625

2726
func init() {
2827
if tz, err := getSystemLocalTimeZone(); err == nil {
@@ -42,48 +41,62 @@ func GetLocalTimeZone() string {
4241
return systemTimeZone
4342
}
4443

45-
func GetLocation(tz string) *time.Location {
44+
func GetLocation(ctx context.Context, tz string) (*time.Location, error) {
4645
if tz == "" {
47-
return nil
46+
return nil, errors.New("time zone cannot be empty")
4847
}
4948

50-
info, err := getTimeZoneInfo(tz)
49+
loc, err := getTimeZoneLocation(tz)
5150
if err != nil {
52-
return nil
51+
return nil, fmt.Errorf("failed to load time zone location for %s: %w", tz, err)
5352
}
5453

55-
return info.location
54+
return loc, nil
5655
}
5756

58-
func GetTimeZoneData(tz, format string) []byte {
57+
func GetTimeZoneData(ctx context.Context, tz, format string) ([]byte, error) {
5958
if tz == "" {
60-
return nil
59+
return nil, errors.New("time zone cannot be empty")
6160
}
6261

6362
// only support tzif format for now
6463
// we can expand this to support other formats as needed
6564
if format != "tzif" {
66-
return nil
65+
return nil, fmt.Errorf("unsupported time zone format: %s", format)
66+
}
67+
68+
data, err := getTimeZoneData(tz)
69+
if err != nil {
70+
return nil, fmt.Errorf("failed to load time zone data for %s: %w", tz, err)
6771
}
6872

69-
info, err := getTimeZoneInfo(tz)
73+
return data, nil
74+
}
75+
76+
func getTimeZoneLocation(tz string) (*time.Location, error) {
77+
if loc, ok := tzLocationCache.Load(tz); ok {
78+
return loc, nil
79+
}
80+
81+
loc, err := time.LoadLocation(tz)
7082
if err != nil {
71-
return nil
83+
return nil, err
7284
}
7385

74-
return info.data
86+
tzLocationCache.Store(tz, loc)
87+
return loc, nil
7588
}
7689

77-
func getTimeZoneInfo(tz string) (*tzInfo, error) {
78-
if info, ok := tzCache.Load(tz); ok {
79-
return info, nil
90+
func getTimeZoneData(tz string) ([]byte, error) {
91+
if data, ok := tzDataCache.Load(tz); ok {
92+
return data, nil
8093
}
8194

82-
info, err := loadTimeZoneInfo(tz)
95+
data, err := loadTimeZoneData(tz)
8396
if err != nil {
8497
return nil, err
8598
}
8699

87-
tzCache.Store(tz, info)
88-
return info, nil
100+
tzDataCache.Store(tz, data)
101+
return data, nil
89102
}

runtime/timezones/tzdata_other.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,23 @@ import (
1717
"os"
1818
"path"
1919
"strings"
20-
"time"
2120
)
2221

23-
func loadTimeZoneInfo(tz string) (*tzInfo, error) {
22+
func loadTimeZoneData(tz string) ([]byte, error) {
2423
tzFile := "/usr/share/zoneinfo/" + tz
2524
if _, err := os.Stat(tzFile); err != nil {
26-
return nil, fmt.Errorf("could not find timezone file: %v", err)
25+
return nil, fmt.Errorf("could not find time zone file: %v", err)
2726
}
2827

29-
bytes, err := os.ReadFile(tzFile)
28+
data, err := os.ReadFile(tzFile)
3029
if err != nil {
31-
return nil, fmt.Errorf("could not read timezone file: %v", err)
30+
return nil, fmt.Errorf("could not read time zone file: %v", err)
3231
}
3332

34-
loc, err := time.LoadLocationFromTZData(tz, bytes)
35-
if err != nil {
36-
return nil, fmt.Errorf("could not load timezone data: %v", err)
33+
if len(data) == 0 {
34+
return nil, errors.New("time zone data is empty")
3735
}
38-
39-
info := &tzInfo{loc, bytes}
40-
return info, nil
36+
return data, nil
4137
}
4238

4339
func getSystemLocalTimeZone() (string, error) {

runtime/timezones/tzdata_windows.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
package timezones
1313

1414
import (
15+
"errors"
1516
"fmt"
16-
"time"
1717
"unsafe"
1818

1919
_ "time/tzdata"
@@ -27,22 +27,18 @@ import (
2727
//go:linkname loadFromEmbeddedTZData time/tzdata.loadFromEmbeddedTZData
2828
func loadFromEmbeddedTZData(string) (string, error)
2929

30-
func loadTimeZoneInfo(tz string) (*tzInfo, error) {
31-
30+
func loadTimeZoneData(tz string) ([]byte, error) {
3231
var data []byte
3332
if s, err := loadFromEmbeddedTZData(tz); err != nil {
34-
return nil, fmt.Errorf("could not load timezone data: %v", err)
33+
return nil, fmt.Errorf("could not load time zone data: %v", err)
3534
} else {
3635
data = []byte(s)
3736
}
3837

39-
loc, err := time.LoadLocationFromTZData(tz, data)
40-
if err != nil {
41-
return nil, fmt.Errorf("could not load timezone data: %v", err)
38+
if len(data) == 0 {
39+
return nil, errors.New("time zone data is empty")
4240
}
43-
44-
info := &tzInfo{loc, data}
45-
return info, nil
41+
return data, nil
4642
}
4743

4844
func getSystemLocalTimeZone() (string, error) {

0 commit comments

Comments
 (0)