[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
|
// Network settings
|
||||||
Hostname = "pico2w-clock"
|
Hostname = "pico2w-clock"
|
||||||
EnableICMP = true
|
EnableICMP = true
|
||||||
|
|
||||||
|
// Time settings
|
||||||
|
ActiveTimezone = "UTC"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -128,30 +128,31 @@ func main() {
|
|||||||
|
|
||||||
lastSecond := -1
|
lastSecond := -1
|
||||||
|
|
||||||
|
// Remove static call before the loop, then in the loop:
|
||||||
for {
|
for {
|
||||||
currentTime := time.Now().Add(timeOffset).UTC()
|
currentTime := time.Now().Add(timeOffset).UTC()
|
||||||
currentSecond := currentTime.Second()
|
currentSecond := currentTime.Second()
|
||||||
|
|
||||||
// Skip display refreshes if the second hasn't changed to save the i2c bus
|
|
||||||
if currentSecond != lastSecond {
|
if currentSecond != lastSecond {
|
||||||
lastSecond = currentSecond
|
lastSecond = currentSecond
|
||||||
|
|
||||||
// Alternate colon (:) with space ( ) every second
|
|
||||||
timeSeperator := ":"
|
timeSeperator := ":"
|
||||||
if currentSecond % 2 == 1 {
|
if currentSecond%2 == 1 {
|
||||||
timeSeperator = " "
|
timeSeperator = " "
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the awful time string before displaying it
|
// Dynamically determine DST vs Standard time for this second
|
||||||
timeStr := fmt.Sprintf("%02d%s%02d%s%02d UTC",
|
loc, zoneName := ResolveTimezone(ActiveTimezone, currentTime)
|
||||||
currentTime.Hour(), timeSeperator,
|
localTime := currentTime.In(loc)
|
||||||
currentTime.Minute(), timeSeperator,
|
|
||||||
currentTime.Second())
|
timeStr := fmt.Sprintf("%02d%s%02d%s%02d %s",
|
||||||
|
localTime.Hour(), timeSeperator,
|
||||||
|
localTime.Minute(), timeSeperator,
|
||||||
|
localTime.Second(),
|
||||||
|
zoneName)
|
||||||
|
|
||||||
oled.ClearBuffer()
|
oled.ClearBuffer()
|
||||||
// Time
|
|
||||||
tinyfont.WriteLine(oled, defaultFont, 8, 32, timeStr, white)
|
tinyfont.WriteLine(oled, defaultFont, 8, 32, timeStr, white)
|
||||||
// IP Address
|
|
||||||
tinyfont.WriteLine(oled, tinyFont, 0, 60, fmt.Sprintf("IP: %s", ipStr), white)
|
tinyfont.WriteLine(oled, tinyFont, 0, 60, fmt.Sprintf("IP: %s", ipStr), white)
|
||||||
oled.Display()
|
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