libgo: update to Go 1.15.4 release
authorIan Lance Taylor <iant@golang.org>
Sat, 7 Nov 2020 15:25:23 +0000 (07:25 -0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 10 Nov 2020 15:25:32 +0000 (07:25 -0800)
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/268177

17 files changed:
gcc/go/gofrontend/MERGE
libgo/MERGE
libgo/VERSION
libgo/go/cmd/go/internal/modfetch/coderepo_test.go
libgo/go/compress/flate/deflate_test.go
libgo/go/compress/flate/deflatefast.go
libgo/go/net/http/h2_bundle.go
libgo/go/net/http/request.go
libgo/go/net/http/request_test.go
libgo/go/reflect/deepequal.go
libgo/go/reflect/value.go
libgo/go/runtime/netpoll.go
libgo/go/runtime/proc.go
libgo/go/runtime/signal_unix.go
libgo/go/syscall/exec_unix_test.go
libgo/go/time/zoneinfo_read.go
libgo/go/time/zoneinfo_test.go

index 149f20077e3cc71eb8c8adfac31c86b6740c8fdd..e62578fc781e19061153563bc5e4e66cb8700008 100644 (file)
@@ -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.
index 4b158c05c6e12188ab748b7bf1f31352027746cc..ad239c92fa95cea460ee147130531c8095ff35a1 100644 (file)
@@ -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.
index 93c7b2d3d7865886f0961fe6d2bf582985e48b9e..baff2224d623f7da1f7d5053325e707bc064d2cc 100644 (file)
@@ -1 +1 @@
-go1.15.3
+go1.15.4
index f69c193b86c1048d3b6d0bb28014837b61a38786..9a0cd7dba69743cbc55b37432725007a5053983c 100644 (file)
@@ -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",
index 3362d256cf497dc9c4d4d9fbf86bb08865059a1e..8a22b8e6f928fcf53ade7123f4ea1f3b4d60144e 100644 (file)
@@ -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.
index 24f8be9d5db86c79764e6c234ac2a7ac4a91ad54..6aa439f13d9772afe28ec0a24510b0cf4564a02e 100644 (file)
@@ -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
 }
index 779da4fdfdf8cd5f464e160b9fea30f902f94475..71592e976752d9e5b97f2ad894eb86893cba54c7 100644 (file)
@@ -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)
index fe6b60982cd5668230fb9c40ee8e7e87adc4e7d5..54ec1c5593cca96b7dcf3334583c511d8bc7f264 100644 (file)
@@ -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)
index 42c16d00ea8c60cd76c75b994c26fcaffc5a703d..461d66e05d70798a22d2aad0f549df59e6f48980 100644 (file)
@@ -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)
 }
index 8a2bf8b09e23a42274fc70d741605566c765d323..b99c345e7bf1832b3400b8acbd28c5dce4eba2c4 100644 (file)
@@ -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()
                }
index 64f74323160f4a761227a4912821cbda2064e7fe..1394dd308c4910985479639bc89cbb6a2ba54551 100644 (file)
@@ -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()
index 5157e4dd9e2614862c4ba46bcebf6e421422bbb3..72a136d363f1927de720a065a093e8c9c52fd2ab 100644 (file)
@@ -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
+)
index 84070e42cfa5a3ae08cf1e2caeff4fb19b6f1321..0ca6c02bce85b182ef3d08cc8f9ec8ea7c155ea6 100644 (file)
@@ -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.
index 17c15c5a205cff4379f629998b9b1a12ce3656ed..6b69dcf06d258c1d819bad3ac8b5d93327dd08ca 100644 (file)
@@ -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)
index fab80e75c5854881b330832c0e1d5bf690b6f5ff..13991491c2fa6edc5e705ec8c8fa46ac84cd2029 100644 (file)
@@ -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")
+}
index c2429726f13db88532aecef3ca2218c917694868..1e3586fdaf220c6c55f31d08daf0c67933a290f9 100644 (file)
@@ -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
                }
        }
 
index dac05e01e37ade8250c62c757f3fdb3e9b7371e3..d543f93e3b71598d1ec5fd8fec3eff0400331b3a 100644 (file)
@@ -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