[Issue #2] - Added timezone support with a list of support timezones
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -8,4 +8,7 @@ const (
|
||||
// Network settings
|
||||
Hostname = "pico2w-clock"
|
||||
EnableICMP = true
|
||||
|
||||
// Time settings
|
||||
ActiveTimezone = "UTC"
|
||||
)
|
||||
|
||||
@@ -128,30 +128,31 @@ func main() {
|
||||
|
||||
lastSecond := -1
|
||||
|
||||
// Remove static call before the loop, then in the loop:
|
||||
for {
|
||||
currentTime := time.Now().Add(timeOffset).UTC()
|
||||
currentSecond := currentTime.Second()
|
||||
|
||||
// Skip display refreshes if the second hasn't changed to save the i2c bus
|
||||
if currentSecond != lastSecond {
|
||||
lastSecond = currentSecond
|
||||
|
||||
// Alternate colon (:) with space ( ) every second
|
||||
timeSeperator := ":"
|
||||
if currentSecond%2 == 1 {
|
||||
timeSeperator = " "
|
||||
}
|
||||
|
||||
// Create the awful time string before displaying it
|
||||
timeStr := fmt.Sprintf("%02d%s%02d%s%02d UTC",
|
||||
currentTime.Hour(), timeSeperator,
|
||||
currentTime.Minute(), timeSeperator,
|
||||
currentTime.Second())
|
||||
// Dynamically determine DST vs Standard time for this second
|
||||
loc, zoneName := ResolveTimezone(ActiveTimezone, currentTime)
|
||||
localTime := currentTime.In(loc)
|
||||
|
||||
timeStr := fmt.Sprintf("%02d%s%02d%s%02d %s",
|
||||
localTime.Hour(), timeSeperator,
|
||||
localTime.Minute(), timeSeperator,
|
||||
localTime.Second(),
|
||||
zoneName)
|
||||
|
||||
oled.ClearBuffer()
|
||||
// Time
|
||||
tinyfont.WriteLine(oled, defaultFont, 8, 32, timeStr, white)
|
||||
// IP Address
|
||||
tinyfont.WriteLine(oled, tinyFont, 0, 60, fmt.Sprintf("IP: %s", ipStr), white)
|
||||
oled.Display()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type TimezoneConfig struct {
|
||||
StandardName string
|
||||
StandardOffset int
|
||||
DaylightName string
|
||||
DaylightOffset int
|
||||
ObservesUSDST bool
|
||||
}
|
||||
|
||||
// Map timezone aliases to standard/daylight offsets
|
||||
var timezones = map[string]TimezoneConfig{
|
||||
// Dynamic US zones (switch automatically between Standard and Daylight)
|
||||
"US/Eastern": {"EST", -5 * 3600, "EDT", -4 * 3600, true},
|
||||
"ET": {"EST", -5 * 3600, "EDT", -4 * 3600, true},
|
||||
"US/Central": {"CST", -6 * 3600, "CDT", -5 * 3600, true},
|
||||
"CT": {"CST", -6 * 3600, "CDT", -5 * 3600, true},
|
||||
"US/Mountain": {"MST", -7 * 3600, "MDT", -6 * 3600, true},
|
||||
"MT": {"MST", -7 * 3600, "MDT", -6 * 3600, true},
|
||||
"US/Pacific": {"PST", -8 * 3600, "PDT", -7 * 3600, true},
|
||||
"PT": {"PST", -8 * 3600, "PDT", -7 * 3600, true},
|
||||
|
||||
// Fixed non-DST zones
|
||||
"UTC": {"UTC", 0, "UTC", 0, false},
|
||||
"EST": {"EST", -5 * 3600, "EST", -5 * 3600, false},
|
||||
"EDT": {"EDT", -4 * 3600, "EDT", -4 * 3600, false},
|
||||
"CST": {"CST", -6 * 3600, "CST", -6 * 3600, false},
|
||||
"CDT": {"CDT", -5 * 3600, "CDT", -5 * 3600, false},
|
||||
"MST": {"MST", -7 * 3600, "MST", -7 * 3600, false},
|
||||
"MDT": {"MDT", -6 * 3600, "MDT", -6 * 3600, false},
|
||||
"PST": {"PST", -8 * 3600, "PST", -8 * 3600, false},
|
||||
"PDT": {"PDT", -7 * 3600, "PDT", -7 * 3600, false},
|
||||
}
|
||||
|
||||
// IsUSDST checks if the given UTC time falls in US Daylight Saving Time
|
||||
// (Second Sunday in March at 02:00 local to first Sunday in November at 02:00 local).
|
||||
func IsUSDST(t time.Time, stdOffsetSec int) bool {
|
||||
month := t.Month()
|
||||
if month < time.March || month > time.November {
|
||||
return false // Dec, Jan, Feb
|
||||
}
|
||||
if month > time.March && month < time.November {
|
||||
return true // Apr through Oct
|
||||
}
|
||||
|
||||
day := t.Day()
|
||||
weekday := int(t.Weekday())
|
||||
|
||||
if month == time.March {
|
||||
// Second Sunday in March is between 8th and 14th
|
||||
prevSunday := day - weekday
|
||||
if prevSunday < 8 {
|
||||
return false
|
||||
}
|
||||
if prevSunday >= 15 {
|
||||
return true
|
||||
}
|
||||
// Transition occurs at 2:00 AM standard local time -> (2 - stdOffsetHours) in UTC
|
||||
stdOffsetHours := stdOffsetSec / 3600
|
||||
switchHourUTC := 2 - stdOffsetHours
|
||||
return weekday != int(time.Sunday) || t.Hour() >= switchHourUTC
|
||||
}
|
||||
|
||||
// November: first Sunday is between 1st and 7th
|
||||
prevSunday := day - weekday
|
||||
if prevSunday < 1 {
|
||||
return true // Before first Sunday
|
||||
}
|
||||
// Fall-back occurs at 2:00 AM daylight local time -> (2 - (stdOffsetHours + 1)) in UTC
|
||||
stdOffsetHours := stdOffsetSec / 3600
|
||||
switchHourUTC := 2 - (stdOffsetHours + 1)
|
||||
return weekday == int(time.Sunday) && t.Hour() < switchHourUTC
|
||||
}
|
||||
|
||||
// ResolveTimezone dynamically determines the *time.Location and label
|
||||
// based on whether the zone observes DST at the given UTC time.
|
||||
func ResolveTimezone(name string, utcNow time.Time) (*time.Location, string) {
|
||||
cfg, ok := timezones[name]
|
||||
if !ok {
|
||||
return time.FixedZone("UTC", 0), "UTC"
|
||||
}
|
||||
|
||||
if cfg.ObservesUSDST && IsUSDST(utcNow, cfg.StandardOffset) {
|
||||
return time.FixedZone(cfg.DaylightName, cfg.DaylightOffset), cfg.DaylightName
|
||||
}
|
||||
return time.FixedZone(cfg.StandardName, cfg.StandardOffset), cfg.StandardName
|
||||
}
|
||||
Reference in New Issue
Block a user