libgo: update to Go 1.15.4 release
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/268177
This commit is contained in:
parent
0000ea4fb4
commit
cf392dbdf1
@ -1,4 +1,4 @@
|
||||
ae20684902b82883d3d65f2cde0894c7cb3b995b
|
||||
893fa057e36ae6c9b2ac5ffdf74634c35b3489c6
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
@ -1,4 +1,4 @@
|
||||
1984ee00048b63eacd2155cd6d74a2d13e998272
|
||||
0e953add9656c32a788e06438cd7b533e968b7f8
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
@ -1 +1 @@
|
||||
go1.15.3
|
||||
go1.15.4
|
||||
|
@ -655,11 +655,6 @@ var codeRepoVersionsTests = []struct {
|
||||
path: "swtch.com/testmod",
|
||||
versions: []string{"v1.0.0", "v1.1.1"},
|
||||
},
|
||||
{
|
||||
vcs: "git",
|
||||
path: "gopkg.in/russross/blackfriday.v2",
|
||||
versions: []string{"v2.0.0", "v2.0.1"},
|
||||
},
|
||||
{
|
||||
vcs: "git",
|
||||
path: "gopkg.in/natefinch/lumberjack.v2",
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
@ -896,6 +897,62 @@ func TestBestSpeedMaxMatchOffset(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestSpeedShiftOffsets(t *testing.T) {
|
||||
// Test if shiftoffsets properly preserves matches and resets out-of-range matches
|
||||
// seen in https://github.com/golang/go/issues/4142
|
||||
enc := newDeflateFast()
|
||||
|
||||
// testData may not generate internal matches.
|
||||
testData := make([]byte, 32)
|
||||
rng := rand.New(rand.NewSource(0))
|
||||
for i := range testData {
|
||||
testData[i] = byte(rng.Uint32())
|
||||
}
|
||||
|
||||
// Encode the testdata with clean state.
|
||||
// Second part should pick up matches from the first block.
|
||||
wantFirstTokens := len(enc.encode(nil, testData))
|
||||
wantSecondTokens := len(enc.encode(nil, testData))
|
||||
|
||||
if wantFirstTokens <= wantSecondTokens {
|
||||
t.Fatalf("test needs matches between inputs to be generated")
|
||||
}
|
||||
// Forward the current indicator to before wraparound.
|
||||
enc.cur = bufferReset - int32(len(testData))
|
||||
|
||||
// Part 1 before wrap, should match clean state.
|
||||
got := len(enc.encode(nil, testData))
|
||||
if wantFirstTokens != got {
|
||||
t.Errorf("got %d, want %d tokens", got, wantFirstTokens)
|
||||
}
|
||||
|
||||
// Verify we are about to wrap.
|
||||
if enc.cur != bufferReset {
|
||||
t.Errorf("got %d, want e.cur to be at bufferReset (%d)", enc.cur, bufferReset)
|
||||
}
|
||||
|
||||
// Part 2 should match clean state as well even if wrapped.
|
||||
got = len(enc.encode(nil, testData))
|
||||
if wantSecondTokens != got {
|
||||
t.Errorf("got %d, want %d token", got, wantSecondTokens)
|
||||
}
|
||||
|
||||
// Verify that we wrapped.
|
||||
if enc.cur >= bufferReset {
|
||||
t.Errorf("want e.cur to be < bufferReset (%d), got %d", bufferReset, enc.cur)
|
||||
}
|
||||
|
||||
// Forward the current buffer, leaving the matches at the bottom.
|
||||
enc.cur = bufferReset
|
||||
enc.shiftOffsets()
|
||||
|
||||
// Ensure that no matches were picked up.
|
||||
got = len(enc.encode(nil, testData))
|
||||
if wantFirstTokens != got {
|
||||
t.Errorf("got %d, want %d tokens", got, wantFirstTokens)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxStackSize(t *testing.T) {
|
||||
// This test must not run in parallel with other tests as debug.SetMaxStack
|
||||
// affects all goroutines.
|
||||
|
@ -270,6 +270,7 @@ func (e *deflateFast) matchLen(s, t int32, src []byte) int32 {
|
||||
func (e *deflateFast) reset() {
|
||||
e.prev = e.prev[:0]
|
||||
// Bump the offset, so all matches will fail distance check.
|
||||
// Nothing should be >= e.cur in the table.
|
||||
e.cur += maxMatchOffset
|
||||
|
||||
// Protect against e.cur wraparound.
|
||||
@ -288,17 +289,21 @@ func (e *deflateFast) shiftOffsets() {
|
||||
for i := range e.table[:] {
|
||||
e.table[i] = tableEntry{}
|
||||
}
|
||||
e.cur = maxMatchOffset
|
||||
e.cur = maxMatchOffset + 1
|
||||
return
|
||||
}
|
||||
|
||||
// Shift down everything in the table that isn't already too far away.
|
||||
for i := range e.table[:] {
|
||||
v := e.table[i].offset - e.cur + maxMatchOffset
|
||||
v := e.table[i].offset - e.cur + maxMatchOffset + 1
|
||||
if v < 0 {
|
||||
// We want to reset e.cur to maxMatchOffset + 1, so we need to shift
|
||||
// all table entries down by (e.cur - (maxMatchOffset + 1)).
|
||||
// Because we ignore matches > maxMatchOffset, we can cap
|
||||
// any negative offsets at 0.
|
||||
v = 0
|
||||
}
|
||||
e.table[i].offset = v
|
||||
}
|
||||
e.cur = maxMatchOffset
|
||||
e.cur = maxMatchOffset + 1
|
||||
}
|
||||
|
@ -5265,6 +5265,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
|
||||
if len(data) > 0 {
|
||||
wrote, err := st.body.Write(data)
|
||||
if err != nil {
|
||||
sc.sendWindowUpdate(nil, int(f.Length)-wrote)
|
||||
return http2streamError(id, http2ErrCodeStreamClosed)
|
||||
}
|
||||
if wrote != len(data) {
|
||||
@ -7167,6 +7168,7 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
|
||||
cc.inflow.add(http2transportDefaultConnFlow + http2initialWindowSize)
|
||||
cc.bw.Flush()
|
||||
if cc.werr != nil {
|
||||
cc.Close()
|
||||
return nil, cc.werr
|
||||
}
|
||||
|
||||
@ -7532,6 +7534,15 @@ func (cc *http2ClientConn) roundTrip(req *Request) (res *Response, gotErrAfterRe
|
||||
bodyWriter := cc.t.getBodyWriterState(cs, body)
|
||||
cs.on100 = bodyWriter.on100
|
||||
|
||||
defer func() {
|
||||
cc.wmu.Lock()
|
||||
werr := cc.werr
|
||||
cc.wmu.Unlock()
|
||||
if werr != nil {
|
||||
cc.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
cc.wmu.Lock()
|
||||
endStream := !hasBody && !hasTrailers
|
||||
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
|
||||
|
@ -382,7 +382,7 @@ func (r *Request) Clone(ctx context.Context) *Request {
|
||||
if s := r.TransferEncoding; s != nil {
|
||||
s2 := make([]string, len(s))
|
||||
copy(s2, s)
|
||||
r2.TransferEncoding = s
|
||||
r2.TransferEncoding = s2
|
||||
}
|
||||
r2.Form = cloneURLValues(r.Form)
|
||||
r2.PostForm = cloneURLValues(r.PostForm)
|
||||
|
@ -828,6 +828,27 @@ func TestWithContextDeepCopiesURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that Request.Clone creates a deep copy of TransferEncoding.
|
||||
// See issue 41907.
|
||||
func TestRequestCloneTransferEncoding(t *testing.T) {
|
||||
body := strings.NewReader("body")
|
||||
req, _ := NewRequest("POST", "https://example.org/", body)
|
||||
req.TransferEncoding = []string{
|
||||
"encoding1",
|
||||
}
|
||||
|
||||
clonedReq := req.Clone(context.Background())
|
||||
// modify original after deep copy
|
||||
req.TransferEncoding[0] = "encoding2"
|
||||
|
||||
if req.TransferEncoding[0] != "encoding2" {
|
||||
t.Error("expected req.TransferEncoding to be changed")
|
||||
}
|
||||
if clonedReq.TransferEncoding[0] != "encoding1" {
|
||||
t.Error("expected clonedReq.TransferEncoding to be unchanged")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoPanicOnRoundTripWithBasicAuth_h1(t *testing.T) {
|
||||
testNoPanicWithBasicAuth(t, h1Mode)
|
||||
}
|
||||
|
@ -37,7 +37,17 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
|
||||
// and it's safe and valid to get Value's internal pointer.
|
||||
hard := func(v1, v2 Value) bool {
|
||||
switch v1.Kind() {
|
||||
case Map, Slice, Ptr, Interface:
|
||||
case Ptr:
|
||||
if v1.typ.ptrdata == 0 {
|
||||
// go:notinheap pointers can't be cyclic.
|
||||
// At least, all of our current uses of go:notinheap have
|
||||
// that property. The runtime ones aren't cyclic (and we don't use
|
||||
// DeepEqual on them anyway), and the cgo-generated ones are
|
||||
// all empty structs.
|
||||
return false
|
||||
}
|
||||
fallthrough
|
||||
case Map, Slice, Interface:
|
||||
// Nil pointers cannot be cyclic. Avoid putting them in the visited map.
|
||||
return !v1.IsNil() && !v2.IsNil()
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ func (f flag) ro() flag {
|
||||
|
||||
// pointer returns the underlying pointer represented by v.
|
||||
// v.Kind() must be Ptr, Map, Chan, Func, or UnsafePointer
|
||||
// if v.Kind() == Ptr, the base type must not be go:notinheap.
|
||||
func (v Value) pointer() unsafe.Pointer {
|
||||
if v.typ.size != ptrSize || !v.typ.pointers() {
|
||||
panic("can't call pointer on a non-pointer Value")
|
||||
@ -1263,7 +1264,16 @@ func (v Value) Pointer() uintptr {
|
||||
// TODO: deprecate
|
||||
k := v.kind()
|
||||
switch k {
|
||||
case Chan, Map, Ptr, UnsafePointer:
|
||||
case Ptr:
|
||||
if v.typ.ptrdata == 0 {
|
||||
// Handle pointers to go:notinheap types directly,
|
||||
// so we never materialize such pointers as an
|
||||
// unsafe.Pointer. (Such pointers are always indirect.)
|
||||
// See issue 42076.
|
||||
return *(*uintptr)(v.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case Chan, Map, UnsafePointer:
|
||||
return uintptr(v.pointer())
|
||||
case Func:
|
||||
p := v.pointer()
|
||||
|
@ -82,16 +82,17 @@ type pollDesc struct {
|
||||
lock mutex // protects the following fields
|
||||
fd uintptr
|
||||
closing bool
|
||||
everr bool // marks event scanning error happened
|
||||
user uint32 // user settable cookie
|
||||
rseq uintptr // protects from stale read timers
|
||||
rg uintptr // pdReady, pdWait, G waiting for read or nil
|
||||
rt timer // read deadline timer (set if rt.f != nil)
|
||||
rd int64 // read deadline
|
||||
wseq uintptr // protects from stale write timers
|
||||
wg uintptr // pdReady, pdWait, G waiting for write or nil
|
||||
wt timer // write deadline timer
|
||||
wd int64 // write deadline
|
||||
everr bool // marks event scanning error happened
|
||||
user uint32 // user settable cookie
|
||||
rseq uintptr // protects from stale read timers
|
||||
rg uintptr // pdReady, pdWait, G waiting for read or nil
|
||||
rt timer // read deadline timer (set if rt.f != nil)
|
||||
rd int64 // read deadline
|
||||
wseq uintptr // protects from stale write timers
|
||||
wg uintptr // pdReady, pdWait, G waiting for write or nil
|
||||
wt timer // write deadline timer
|
||||
wd int64 // write deadline
|
||||
self *pollDesc // storage for indirect interface. See (*pollDesc).makeArg.
|
||||
}
|
||||
|
||||
type pollCache struct {
|
||||
@ -160,6 +161,7 @@ func poll_runtime_pollOpen(fd uintptr) (uintptr, int) {
|
||||
pd.wseq++
|
||||
pd.wg = 0
|
||||
pd.wd = 0
|
||||
pd.self = pd
|
||||
unlock(&pd.lock)
|
||||
|
||||
var errno int32
|
||||
@ -279,14 +281,14 @@ func poll_runtime_pollSetDeadline(ctx uintptr, d int64, mode int) {
|
||||
// Copy current seq into the timer arg.
|
||||
// Timer func will check the seq against current descriptor seq,
|
||||
// if they differ the descriptor was reused or timers were reset.
|
||||
pd.rt.arg = pd
|
||||
pd.rt.arg = pd.makeArg()
|
||||
pd.rt.seq = pd.rseq
|
||||
resettimer(&pd.rt, pd.rd)
|
||||
}
|
||||
} else if pd.rd != rd0 || combo != combo0 {
|
||||
pd.rseq++ // invalidate current timers
|
||||
if pd.rd > 0 {
|
||||
modtimer(&pd.rt, pd.rd, 0, rtf, pd, pd.rseq)
|
||||
modtimer(&pd.rt, pd.rd, 0, rtf, pd.makeArg(), pd.rseq)
|
||||
} else {
|
||||
deltimer(&pd.rt)
|
||||
pd.rt.f = nil
|
||||
@ -295,14 +297,14 @@ func poll_runtime_pollSetDeadline(ctx uintptr, d int64, mode int) {
|
||||
if pd.wt.f == nil {
|
||||
if pd.wd > 0 && !combo {
|
||||
pd.wt.f = netpollWriteDeadline
|
||||
pd.wt.arg = pd
|
||||
pd.wt.arg = pd.makeArg()
|
||||
pd.wt.seq = pd.wseq
|
||||
resettimer(&pd.wt, pd.wd)
|
||||
}
|
||||
} else if pd.wd != wd0 || combo != combo0 {
|
||||
pd.wseq++ // invalidate current timers
|
||||
if pd.wd > 0 && !combo {
|
||||
modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd, pd.wseq)
|
||||
modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd.makeArg(), pd.wseq)
|
||||
} else {
|
||||
deltimer(&pd.wt)
|
||||
pd.wt.f = nil
|
||||
@ -556,3 +558,21 @@ func (c *pollCache) alloc() *pollDesc {
|
||||
unlock(&c.lock)
|
||||
return pd
|
||||
}
|
||||
|
||||
// makeArg converts pd to an interface{}.
|
||||
// makeArg does not do any allocation. Normally, such
|
||||
// a conversion requires an allocation because pointers to
|
||||
// go:notinheap types (which pollDesc is) must be stored
|
||||
// in interfaces indirectly. See issue 42076.
|
||||
func (pd *pollDesc) makeArg() (i interface{}) {
|
||||
x := (*eface)(unsafe.Pointer(&i))
|
||||
x._type = pdType
|
||||
// For gccgo, we still use pd.self here, not &pd.self.
|
||||
x.data = unsafe.Pointer(pd.self)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
pdEface interface{} = (*pollDesc)(nil)
|
||||
pdType *_type = efaceOf(&pdEface)._type
|
||||
)
|
||||
|
@ -1258,6 +1258,14 @@ found:
|
||||
checkdead()
|
||||
unlock(&sched.lock)
|
||||
|
||||
if GOOS == "darwin" {
|
||||
// Make sure pendingPreemptSignals is correct when an M exits.
|
||||
// For #41702.
|
||||
if atomic.Load(&m.signalPending) != 0 {
|
||||
atomic.Xadd(&pendingPreemptSignals, -1)
|
||||
}
|
||||
}
|
||||
|
||||
if osStack {
|
||||
// Return from mstart and let the system thread
|
||||
// library free the g0 stack and terminate the thread.
|
||||
@ -3349,11 +3357,24 @@ func syscall_runtime_AfterForkInChild() {
|
||||
inForkedChild = false
|
||||
}
|
||||
|
||||
// pendingPreemptSignals is the number of preemption signals
|
||||
// that have been sent but not received. This is only used on Darwin.
|
||||
// For #41702.
|
||||
var pendingPreemptSignals uint32
|
||||
|
||||
// Called from syscall package before Exec.
|
||||
//go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec
|
||||
func syscall_runtime_BeforeExec() {
|
||||
// Prevent thread creation during exec.
|
||||
execLock.lock()
|
||||
|
||||
// On Darwin, wait for all pending preemption signals to
|
||||
// be received. See issue #41702.
|
||||
if GOOS == "darwin" {
|
||||
for int32(atomic.Load(&pendingPreemptSignals)) > 0 {
|
||||
osyield()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called from syscall package after Exec.
|
||||
|
@ -347,6 +347,10 @@ func doSigPreempt(gp *g, ctxt *sigctxt, sigpc uintptr) {
|
||||
// Acknowledge the preemption.
|
||||
atomic.Xadd(&gp.m.preemptGen, 1)
|
||||
atomic.Store(&gp.m.signalPending, 0)
|
||||
|
||||
if GOOS == "darwin" {
|
||||
atomic.Xadd(&pendingPreemptSignals, -1)
|
||||
}
|
||||
}
|
||||
|
||||
// This is false for gccgo.
|
||||
@ -404,6 +408,9 @@ func sigtrampgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) {
|
||||
// no non-Go signal handler for sigPreempt.
|
||||
// The default behavior for sigPreempt is to ignore
|
||||
// the signal, so badsignal will be a no-op anyway.
|
||||
if GOOS == "darwin" {
|
||||
atomic.Xadd(&pendingPreemptSignals, -1)
|
||||
}
|
||||
return
|
||||
}
|
||||
badsignal(uintptr(sig), &c)
|
||||
|
@ -9,12 +9,14 @@ package syscall_test
|
||||
import (
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@ -245,3 +247,46 @@ func TestInvalidExec(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestExec is for issue #41702.
|
||||
func TestExec(t *testing.T) {
|
||||
testenv.MustHaveExec(t)
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestExecHelper")
|
||||
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
|
||||
o, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("%s\n%v", o, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExecHelper is used by TestExec. It does nothing by itself.
|
||||
// In testing on macOS 10.14, this used to fail with
|
||||
// "signal: illegal instruction" more than half the time.
|
||||
func TestExecHelper(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
|
||||
return
|
||||
}
|
||||
|
||||
// We don't have to worry about restoring these values.
|
||||
// We are in a child process that only runs this test,
|
||||
// and we are going to call syscall.Exec anyhow.
|
||||
runtime.GOMAXPROCS(50)
|
||||
os.Setenv("GO_WANT_HELPER_PROCESS", "3")
|
||||
|
||||
stop := time.Now().Add(time.Second)
|
||||
for i := 0; i < 100; i++ {
|
||||
go func(i int) {
|
||||
r := rand.New(rand.NewSource(int64(i)))
|
||||
for time.Now().Before(stop) {
|
||||
r.Uint64()
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
argv := []string{os.Args[0], "-test.run=TestExecHelper"}
|
||||
syscall.Exec(os.Args[0], argv, os.Environ())
|
||||
|
||||
t.Error("syscall.Exec returned")
|
||||
}
|
||||
|
@ -325,10 +325,27 @@ func LoadLocationFromTZData(name string, data []byte) (*Location, error) {
|
||||
if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
|
||||
l.cacheStart = tx[i].when
|
||||
l.cacheEnd = omega
|
||||
zoneIdx := tx[i].index
|
||||
if i+1 < len(tx) {
|
||||
l.cacheEnd = tx[i+1].when
|
||||
} else if l.extend != "" {
|
||||
// If we're at the end of the known zone transitions,
|
||||
// try the extend string.
|
||||
if name, _, estart, eend, ok := tzset(l.extend, l.cacheEnd, sec); ok {
|
||||
l.cacheStart = estart
|
||||
l.cacheEnd = eend
|
||||
// Find the zone that is returned by tzset,
|
||||
// the last transition is not always the correct zone.
|
||||
for i, z := range l.zone {
|
||||
if z.name == name {
|
||||
zoneIdx = uint8(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
l.cacheZone = &l.zone[tx[i].index]
|
||||
l.cacheZone = &l.zone[zoneIdx]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,6 +189,25 @@ func TestMalformedTZData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadLocationFromTZDataSlim(t *testing.T) {
|
||||
// A 2020b slim tzdata for Europe/Berlin
|
||||
tzData := "TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00TZif2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00\x00\x00\x04\x00\x00\x00\x12\xff\xff\xff\xffo\xa2a\xf8\xff\xff\xff\xff\x9b\f\x17`\xff\xff\xff\xff\x9b\xd5\xda\xf0\xff\xff\xff\xff\x9cٮ\x90\xff\xff\xff\xff\x9d\xa4\xb5\x90\xff\xff\xff\xff\x9e\xb9\x90\x90\xff\xff\xff\xff\x9f\x84\x97\x90\xff\xff\xff\xff\xc8\tq\x90\xff\xff\xff\xff\xcc\xe7K\x10\xff\xff\xff\xffͩ\x17\x90\xff\xff\xff\xff\u03a2C\x10\xff\xff\xff\xffϒ4\x10\xff\xff\xff\xffЂ%\x10\xff\xff\xff\xff\xd1r\x16\x10\xff\xff\xff\xffѶ\x96\x00\xff\xff\xff\xff\xd2X\xbe\x80\xff\xff\xff\xffҡO\x10\xff\xff\xff\xff\xd3c\x1b\x90\xff\xff\xff\xff\xd4K#\x90\xff\xff\xff\xff\xd59\xd1 \xff\xff\xff\xff\xd5g\xe7\x90\xff\xff\xff\xffըs\x00\xff\xff\xff\xff\xd6)\xb4\x10\xff\xff\xff\xff\xd7,\x1a\x10\xff\xff\xff\xff\xd8\t\x96\x10\xff\xff\xff\xff\xd9\x02\xc1\x90\xff\xff\xff\xff\xd9\xe9x\x10\x00\x00\x00\x00\x13MD\x10\x00\x00\x00\x00\x143\xfa\x90\x00\x00\x00\x00\x15#\xeb\x90\x00\x00\x00\x00\x16\x13ܐ\x00\x00\x00\x00\x17\x03͐\x00\x00\x00\x00\x17\xf3\xbe\x90\x00\x00\x00\x00\x18㯐\x00\x00\x00\x00\x19Ӡ\x90\x00\x00\x00\x00\x1aÑ\x90\x00\x00\x00\x00\x1b\xbc\xbd\x10\x00\x00\x00\x00\x1c\xac\xae\x10\x00\x00\x00\x00\x1d\x9c\x9f\x10\x00\x00\x00\x00\x1e\x8c\x90\x10\x00\x00\x00\x00\x1f|\x81\x10\x00\x00\x00\x00 lr\x10\x00\x00\x00\x00!\\c\x10\x00\x00\x00\x00\"LT\x10\x00\x00\x00\x00#<E\x10\x00\x00\x00\x00$,6\x10\x00\x00\x00\x00%\x1c'\x10\x00\x00\x00\x00&\f\x18\x10\x00\x00\x00\x00'\x05C\x90\x00\x00\x00\x00'\xf54\x90\x00\x00\x00\x00(\xe5%\x90\x00\x00\x00\x00)\xd5\x16\x90\x00\x00\x00\x00*\xc5\a\x90\x00\x00\x00\x00+\xb4\xf8\x90\x00\x00\x00\x00,\xa4\xe9\x90\x00\x00\x00\x00-\x94ڐ\x00\x00\x00\x00.\x84ː\x00\x00\x00\x00/t\xbc\x90\x00\x00\x00\x000d\xad\x90\x00\x00\x00\x001]\xd9\x10\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x03\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x00\x00\f\x88\x00\x00\x00\x00\x1c \x01\x04\x00\x00\x0e\x10\x00\t\x00\x00*0\x01\rLMT\x00CEST\x00CET\x00CEMT\x00\nCET-1CEST,M3.5.0,M10.5.0/3\n"
|
||||
|
||||
reference, err := time.LoadLocationFromTZData("Europe/Berlin", []byte(tzData))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := time.Date(2020, time.October, 29, 15, 30, 0, 0, reference)
|
||||
tzName, tzOffset := d.Zone()
|
||||
if want := "CET"; tzName != want {
|
||||
t.Errorf("Zone name == %s, want %s", tzName, want)
|
||||
}
|
||||
if want := 3600; tzOffset != want {
|
||||
t.Errorf("Zone offset == %d, want %d", tzOffset, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTzset(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
inStr string
|
||||
|
Loading…
Reference in New Issue
Block a user