feat: add durable schedule management and multi-node crash recovery
This commit is contained in:
27
internal/timezone/LICENSE
Normal file
27
internal/timezone/LICENSE
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
61
internal/timezone/zone.go
Normal file
61
internal/timezone/zone.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Package timezone 提供随计划冻结的时区快照,禁止读取宿主机或 ZONEINFO 环境配置。
|
||||
package timezone
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Version 标识 Go 1.27.1 附带的 IANA 快照及文件摘要;升级时须保留旧版本可读能力。
|
||||
const Version = "go1.27.1-b2d18a7c8fa8"
|
||||
|
||||
//go:embed zoneinfo.zip
|
||||
var data []byte
|
||||
var zones sync.Map
|
||||
var archive = sync.OnceValues(func() (*zip.Reader, error) { return zip.NewReader(bytes.NewReader(data), int64(len(data))) })
|
||||
|
||||
// Load 仅接受精确的内置名称,返回只读 Location;不接受 Local 或文件系统路径。
|
||||
func Load(name, version string) (*time.Location, error) {
|
||||
if version != Version || name == "" || name == "Local" || len(name) > 64 {
|
||||
return nil, errors.New("unsupported frozen timezone version or name")
|
||||
}
|
||||
if name == "UTC" {
|
||||
return time.UTC, nil
|
||||
}
|
||||
if v, ok := zones.Load(name); ok {
|
||||
return v.(*time.Location), nil
|
||||
}
|
||||
a, err := archive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range a.File {
|
||||
if entry.Name != name {
|
||||
continue
|
||||
}
|
||||
r, err := entry.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := io.ReadAll(io.LimitReader(r, 1<<20))
|
||||
closeErr := r.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if closeErr != nil {
|
||||
return nil, closeErr
|
||||
}
|
||||
loc, err := time.LoadLocationFromTZData(name, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
actual, _ := zones.LoadOrStore(name, loc)
|
||||
return actual.(*time.Location), nil
|
||||
}
|
||||
return nil, errors.New("unknown IANA timezone")
|
||||
}
|
||||
38
internal/timezone/zone_test.go
Normal file
38
internal/timezone/zone_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package timezone
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFrozenSnapshot(t *testing.T) {
|
||||
if got := fmt.Sprintf("%x", sha256.Sum256(data)); got != "b2d18a7c8fa8142097a48c99609fb3c92db5ee98bc740294e57eab8ae9f94779" {
|
||||
t.Fatal("snapshot changed without a new version")
|
||||
}
|
||||
t.Setenv("ZONEINFO", t.TempDir())
|
||||
for _, name := range []string{"UTC", "Asia/Shanghai", "America/New_York", "Australia/Lord_Howe", "Pacific/Apia"} {
|
||||
zone, err := Load(name, Version)
|
||||
if err != nil || zone == nil {
|
||||
t.Fatalf("load %s: %v", name, err)
|
||||
}
|
||||
again, err := Load(name, Version)
|
||||
if err != nil || again != zone {
|
||||
t.Fatal("cache changed")
|
||||
}
|
||||
}
|
||||
zone, _ := Load("Asia/Shanghai", Version)
|
||||
_, offset := time.Date(2026, 1, 1, 0, 0, 0, 0, zone).Zone()
|
||||
if offset != 8*3600 {
|
||||
t.Fatal("wrong frozen zone")
|
||||
}
|
||||
for _, name := range []string{"", "Local", "../UTC", "/etc/localtime"} {
|
||||
if _, err := Load(name, Version); err == nil {
|
||||
t.Fatal("accepted external timezone")
|
||||
}
|
||||
}
|
||||
if _, err := Load("UTC", "unavailable-version"); err == nil {
|
||||
t.Fatal("silently upgraded frozen version")
|
||||
}
|
||||
}
|
||||
BIN
internal/timezone/zoneinfo.zip
Normal file
BIN
internal/timezone/zoneinfo.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user