Commit efe3d35f authored by Russ Cox's avatar Russ Cox

time: new Time, Duration, ZoneInfo types

R=r, bradfitz, gri, dsymonds, iant
CC=golang-dev
https://golang.org/cl/5392041
parent 849fc19c
...@@ -60,6 +60,22 @@ TEXT runtime·setitimer(SB),7,$0 ...@@ -60,6 +60,22 @@ TEXT runtime·setitimer(SB),7,$0
INT $0x80 INT $0x80
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
LEAL 12(SP), AX // must be non-nil, unused
MOVL AX, 4(SP)
MOVL $0, 8(SP) // time zone pointer
MOVL $116, AX
INT $0x80
MOVL DX, BX
// sec is in AX, usec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really // int64 nanotime(void) so really
// void nanotime(int64 *nsec) // void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB), 7, $32 TEXT runtime·nanotime(SB), 7, $32
......
...@@ -55,6 +55,19 @@ TEXT runtime·setitimer(SB), 7, $0 ...@@ -55,6 +55,19 @@ TEXT runtime·setitimer(SB), 7, $0
SYSCALL SYSCALL
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
MOVQ SP, DI // must be non-nil, unused
MOVQ $0, SI
MOVL $(0x2000000+116), AX
SYSCALL
// sec is in AX, usec in DX
MOVQ AX, sec+0(FP)
IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
// int64 nanotime(void) // int64 nanotime(void)
TEXT runtime·nanotime(SB), 7, $32 TEXT runtime·nanotime(SB), 7, $32
MOVQ SP, DI // must be non-nil, unused MOVQ SP, DI // must be non-nil, unused
......
...@@ -106,6 +106,23 @@ TEXT runtime·setitimer(SB), 7, $-4 ...@@ -106,6 +106,23 @@ TEXT runtime·setitimer(SB), 7, $-4
INT $0x80 INT $0x80
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
MOVL $116, AX
LEAL 12(SP), BX
MOVL BX, 4(SP)
MOVL $0, 8(SP)
INT $0x80
MOVL 12(SP), AX // sec
MOVL 16(SP), BX // usec
// sec is in AX, usec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really // int64 nanotime(void) so really
// void nanotime(int64 *nsec) // void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB), 7, $32 TEXT runtime·nanotime(SB), 7, $32
......
...@@ -85,6 +85,21 @@ TEXT runtime·setitimer(SB), 7, $-8 ...@@ -85,6 +85,21 @@ TEXT runtime·setitimer(SB), 7, $-8
SYSCALL SYSCALL
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
MOVL $116, AX
LEAQ 8(SP), DI
MOVQ $0, SI
SYSCALL
MOVQ 8(SP), AX // sec
MOVL 16(SP), DX // usec
// sec is in AX, usec in DX
MOVQ AX, sec+0(FP)
IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB), 7, $32 TEXT runtime·nanotime(SB), 7, $32
MOVL $116, AX MOVL $116, AX
LEAQ 8(SP), DI LEAQ 8(SP), DI
......
...@@ -95,6 +95,23 @@ TEXT runtime·mincore(SB),7,$0-24 ...@@ -95,6 +95,23 @@ TEXT runtime·mincore(SB),7,$0-24
CALL *runtime·_vdso(SB) CALL *runtime·_vdso(SB)
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
MOVL $78, AX // syscall - gettimeofday
LEAL 8(SP), BX
MOVL $0, CX
MOVL $0, DX
CALL *runtime·_vdso(SB)
MOVL 8(SP), AX // sec
MOVL 12(SP), BX // usec
// sec is in AX, usec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really // int64 nanotime(void) so really
// void nanotime(int64 *nsec) // void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB), 7, $32 TEXT runtime·nanotime(SB), 7, $32
......
...@@ -93,6 +93,21 @@ TEXT runtime·mincore(SB),7,$0-24 ...@@ -93,6 +93,21 @@ TEXT runtime·mincore(SB),7,$0-24
SYSCALL SYSCALL
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
LEAQ 8(SP), DI
MOVQ $0, SI
MOVQ $0xffffffffff600000, AX
CALL AX
MOVQ 8(SP), AX // sec
MOVL 16(SP), DX // usec
// sec is in AX, usec in DX
MOVQ AX, sec+0(FP)
IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB), 7, $32 TEXT runtime·nanotime(SB), 7, $32
LEAQ 8(SP), DI LEAQ 8(SP), DI
MOVQ $0, SI MOVQ $0, SI
......
...@@ -127,6 +127,23 @@ TEXT runtime·mincore(SB),7,$0 ...@@ -127,6 +127,23 @@ TEXT runtime·mincore(SB),7,$0
SWI $0 SWI $0
RET RET
TEXT time·now(SB), 7, $32
MOVW $8(R13), R0 // timeval
MOVW $0, R1 // zone
MOVW $SYS_gettimeofday, R7
SWI $0
MOVW 8(R13), R0 // sec
MOVW 12(R13), R2 // usec
MOVW R0, 0(FP)
MOVW $0, R1
MOVW R1, 4(FP)
MOVW $1000, R3
MUL R3, R2
MOVW R2, 8(FP)
RET
// int64 nanotime(void) so really // int64 nanotime(void) so really
// void nanotime(int64 *nsec) // void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB),7,$32 TEXT runtime·nanotime(SB),7,$32
......
...@@ -91,6 +91,23 @@ TEXT runtime·setitimer(SB),7,$-4 ...@@ -91,6 +91,23 @@ TEXT runtime·setitimer(SB),7,$-4
INT $0x80 INT $0x80
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
MOVL $116, AX
LEAL 12(SP), BX
MOVL BX, 4(SP)
MOVL $0, 8(SP)
INT $0x80
MOVL 12(SP), AX // sec
MOVL 16(SP), BX // usec
// sec is in AX, usec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really // int64 nanotime(void) so really
// void nanotime(int64 *nsec) // void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB),7,$32 TEXT runtime·nanotime(SB),7,$32
......
...@@ -133,6 +133,21 @@ TEXT runtime·setitimer(SB),7,$-8 ...@@ -133,6 +133,21 @@ TEXT runtime·setitimer(SB),7,$-8
SYSCALL SYSCALL
RET RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
LEAQ 8(SP), DI // arg 1 - tp
MOVQ $0, SI // arg 2 - tzp
MOVL $116, AX // sys_gettimeofday
SYSCALL
MOVQ 8(SP), AX // sec
MOVL 16(SP), DX // usec
// sec is in AX, usec in DX
MOVQ AX, sec+0(FP)
IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB),7,$32 TEXT runtime·nanotime(SB),7,$32
LEAQ 8(SP), DI // arg 1 - tp LEAQ 8(SP), DI // arg 1 - tp
MOVQ $0, SI // arg 2 - tzp MOVQ $0, SI // arg 2 - tzp
......
...@@ -19,10 +19,7 @@ static bool deltimer(Timer*); ...@@ -19,10 +19,7 @@ static bool deltimer(Timer*);
// Package time APIs. // Package time APIs.
// Godoc uses the comments in package time, not these. // Godoc uses the comments in package time, not these.
// Nanoseconds returns the current time in nanoseconds. // time.now is implemented in assembly.
func Nanoseconds() (ret int64) {
ret = runtime·nanotime();
}
// Sleep puts the current goroutine to sleep for at least ns nanoseconds. // Sleep puts the current goroutine to sleep for at least ns nanoseconds.
func Sleep(ns int64) { func Sleep(ns int64) {
......
...@@ -219,6 +219,18 @@ runtime·nanotime(void) ...@@ -219,6 +219,18 @@ runtime·nanotime(void)
return (filetime - 116444736000000000LL) * 100LL; return (filetime - 116444736000000000LL) * 100LL;
} }
void
time·now(int64 sec, int32 usec)
{
int64 ns;
ns = runtime·nanotime();
sec = ns / 1000000000LL;
usec = ns - sec * 1000000000LL;
FLUSH(&sec);
FLUSH(&usec);
}
// Calling stdcall on os stack. // Calling stdcall on os stack.
#pragma textflag 7 #pragma textflag 7
void * void *
......
...@@ -11,25 +11,22 @@ GOFILES=\ ...@@ -11,25 +11,22 @@ GOFILES=\
sys.go\ sys.go\
tick.go\ tick.go\
time.go\ time.go\
zoneinfo.go\
GOFILES_freebsd=\ GOFILES_freebsd=\
sys_unix.go\ sys_unix.go\
zoneinfo_posix.go\
zoneinfo_unix.go\ zoneinfo_unix.go\
GOFILES_darwin=\ GOFILES_darwin=\
sys_unix.go\ sys_unix.go\
zoneinfo_posix.go\
zoneinfo_unix.go\ zoneinfo_unix.go\
GOFILES_linux=\ GOFILES_linux=\
sys_unix.go\ sys_unix.go\
zoneinfo_posix.go\
zoneinfo_unix.go\ zoneinfo_unix.go\
GOFILES_openbsd=\ GOFILES_openbsd=\
sys_unix.go\ sys_unix.go\
zoneinfo_posix.go\
zoneinfo_unix.go\ zoneinfo_unix.go\
GOFILES_windows=\ GOFILES_windows=\
...@@ -38,7 +35,6 @@ GOFILES_windows=\ ...@@ -38,7 +35,6 @@ GOFILES_windows=\
GOFILES_plan9=\ GOFILES_plan9=\
sys_plan9.go\ sys_plan9.go\
zoneinfo_posix.go\
zoneinfo_plan9.go\ zoneinfo_plan9.go\
GOFILES+=$(GOFILES_$(GOOS)) GOFILES+=$(GOFILES_$(GOOS))
......
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package time_test
import (
"fmt"
"time"
)
func expensiveCall() {}
func ExampleDuration() {
t0 := time.Now()
expensiveCall()
t1 := time.Now()
fmt.Printf("The call took %v to run.\n", t1.Sub(t0))
}
var c chan int
func handle(int) {}
func ExampleAfter() {
select {
case m := <-c:
handle(m)
case <-time.After(5 * time.Minute):
fmt.Println("timed out")
}
}
func ExampleSleep() {
time.Sleep(100 * time.Millisecond)
}
func statusUpdate() string { return "" }
func ExampleTick() {
c := time.Tick(1 * time.Minute)
for now := range c {
fmt.Printf("%v %s\n", now, statusUpdate())
}
}
func ExampleMonth() {
_, month, day := time.Now().Date()
if month == time.November && day == 10 {
fmt.Println("Happy Go day!")
}
}
// Go launched at Tue Nov 10 15:00:00 -0800 PST 2009
func ExampleDate() {
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
fmt.Printf("Go launched at %s\n", t.Local())
}
This diff is collapsed.
...@@ -6,7 +6,7 @@ package time ...@@ -6,7 +6,7 @@ package time
func init() { func init() {
// force US/Pacific for time zone tests // force US/Pacific for time zone tests
onceSetupZone.Do(setupTestingZone) localOnce.Do(initTestingZone)
} }
var Interrupt = interrupt var Interrupt = interrupt
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
package time package time
func nano() int64 {
sec, nsec := now()
return sec*1e9 + int64(nsec)
}
// Interface to timers implemented in package runtime. // Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/runtime.h:/^struct.Timer$ // Must be in sync with ../runtime/runtime.h:/^struct.Timer$
type runtimeTimer struct { type runtimeTimer struct {
...@@ -21,7 +26,7 @@ func stopTimer(*runtimeTimer) bool ...@@ -21,7 +26,7 @@ func stopTimer(*runtimeTimer) bool
// When the Timer expires, the current time will be sent on C, // When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc. // unless the Timer was created by AfterFunc.
type Timer struct { type Timer struct {
C <-chan int64 C <-chan Time
r runtimeTimer r runtimeTimer
} }
...@@ -34,12 +39,12 @@ func (t *Timer) Stop() (ok bool) { ...@@ -34,12 +39,12 @@ func (t *Timer) Stop() (ok bool) {
// NewTimer creates a new Timer that will send // NewTimer creates a new Timer that will send
// the current time on its channel after at least ns nanoseconds. // the current time on its channel after at least ns nanoseconds.
func NewTimer(ns int64) *Timer { func NewTimer(d Duration) *Timer {
c := make(chan int64, 1) c := make(chan Time, 1)
t := &Timer{ t := &Timer{
C: c, C: c,
r: runtimeTimer{ r: runtimeTimer{
when: Nanoseconds() + ns, when: nano() + int64(d),
f: sendTime, f: sendTime,
arg: c, arg: c,
}, },
...@@ -55,16 +60,16 @@ func sendTime(now int64, c interface{}) { ...@@ -55,16 +60,16 @@ func sendTime(now int64, c interface{}) {
// the desired behavior when the reader gets behind, // the desired behavior when the reader gets behind,
// because the sends are periodic. // because the sends are periodic.
select { select {
case c.(chan int64) <- now: case c.(chan Time) <- Unix(0, now):
default: default:
} }
} }
// After waits at least ns nanoseconds before sending the current time // After waits for the duration to elapse and then sends the current time
// on the returned channel. // on the returned channel.
// It is equivalent to NewTimer(ns).C. // It is equivalent to NewTimer(ns).C.
func After(ns int64) <-chan int64 { func After(d Duration) <-chan Time {
return NewTimer(ns).C return NewTimer(d).C
} }
// AfterFunc waits at least ns nanoseconds before calling f // AfterFunc waits at least ns nanoseconds before calling f
...@@ -73,7 +78,7 @@ func After(ns int64) <-chan int64 { ...@@ -73,7 +78,7 @@ func After(ns int64) <-chan int64 {
func AfterFunc(ns int64, f func()) *Timer { func AfterFunc(ns int64, f func()) *Timer {
t := &Timer{ t := &Timer{
r: runtimeTimer{ r: runtimeTimer{
when: Nanoseconds() + ns, when: nano() + ns,
f: goFunc, f: goFunc,
arg: f, arg: f,
}, },
......
...@@ -15,16 +15,16 @@ import ( ...@@ -15,16 +15,16 @@ import (
) )
func TestSleep(t *testing.T) { func TestSleep(t *testing.T) {
const delay = int64(100e6) const delay = 100 * Millisecond
go func() { go func() {
Sleep(delay / 2) Sleep(delay / 2)
Interrupt() Interrupt()
}() }()
start := Nanoseconds() start := Now()
Sleep(delay) Sleep(delay)
duration := Nanoseconds() - start duration := Now().Sub(start)
if duration < delay { if duration < delay {
t.Fatalf("Sleep(%d) slept for only %d ns", delay, duration) t.Fatalf("Sleep(%s) slept for only %s", delay, duration)
} }
} }
...@@ -96,32 +96,32 @@ func BenchmarkStop(b *testing.B) { ...@@ -96,32 +96,32 @@ func BenchmarkStop(b *testing.B) {
} }
func TestAfter(t *testing.T) { func TestAfter(t *testing.T) {
const delay = int64(100e6) const delay = 100 * Millisecond
start := Nanoseconds() start := Now()
end := <-After(delay) end := <-After(delay)
if duration := Nanoseconds() - start; duration < delay { if duration := Now().Sub(start); duration < delay {
t.Fatalf("After(%d) slept for only %d ns", delay, duration) t.Fatalf("After(%s) slept for only %d ns", delay, duration)
} }
if min := start + delay; end < min { if min := start.Add(delay); end.Before(min) {
t.Fatalf("After(%d) expect >= %d, got %d", delay, min, end) t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
} }
} }
func TestAfterTick(t *testing.T) { func TestAfterTick(t *testing.T) {
const ( const (
Delta = 100 * 1e6 Delta = 100 * Millisecond
Count = 10 Count = 10
) )
t0 := Nanoseconds() t0 := Now()
for i := 0; i < Count; i++ { for i := 0; i < Count; i++ {
<-After(Delta) <-After(Delta)
} }
t1 := Nanoseconds() t1 := Now()
ns := t1 - t0 d := t1.Sub(t0)
target := int64(Delta * Count) target := Delta * Count
slop := target * 2 / 10 slop := target * 2 / 10
if ns < target-slop || ns > target+slop { if d < target-slop || d > target+slop {
t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target)) t.Fatalf("%d ticks of %s took %s, expected %s", Count, Delta, d, target)
} }
} }
...@@ -170,37 +170,37 @@ var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0} ...@@ -170,37 +170,37 @@ var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}
type afterResult struct { type afterResult struct {
slot int slot int
t int64 t Time
} }
func await(slot int, result chan<- afterResult, ac <-chan int64) { func await(slot int, result chan<- afterResult, ac <-chan Time) {
result <- afterResult{slot, <-ac} result <- afterResult{slot, <-ac}
} }
func testAfterQueuing(t *testing.T) error { func testAfterQueuing(t *testing.T) error {
const ( const (
Delta = 100 * 1e6 Delta = 100 * Millisecond
) )
// make the result channel buffered because we don't want // make the result channel buffered because we don't want
// to depend on channel queueing semantics that might // to depend on channel queueing semantics that might
// possibly change in the future. // possibly change in the future.
result := make(chan afterResult, len(slots)) result := make(chan afterResult, len(slots))
t0 := Nanoseconds() t0 := Now()
for _, slot := range slots { for _, slot := range slots {
go await(slot, result, After(int64(slot)*Delta)) go await(slot, result, After(Duration(slot)*Delta))
} }
sort.Ints(slots) sort.Ints(slots)
for _, slot := range slots { for _, slot := range slots {
r := <-result r := <-result
if r.slot != slot { if r.slot != slot {
return fmt.Errorf("after queue got slot %d, expected %d", r.slot, slot) return fmt.Errorf("after slot %d, expected %d", r.slot, slot)
} }
ns := r.t - t0 dt := r.t.Sub(t0)
target := int64(slot * Delta) target := Duration(slot) * Delta
slop := int64(Delta) / 4 slop := Delta / 4
if ns < target-slop || ns > target+slop { if dt < target-slop || dt > target+slop {
return fmt.Errorf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop)) return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-slop, target+slop)
} }
} }
return nil return nil
......
...@@ -4,17 +4,33 @@ ...@@ -4,17 +4,33 @@
package time package time
// Seconds reports the number of seconds since the Unix epoch, import "syscall"
// January 1, 1970 00:00:00 UTC.
func Seconds() int64 {
return Nanoseconds() / 1e9
}
// Nanoseconds is implemented by package runtime.
// Nanoseconds reports the number of nanoseconds since the Unix epoch, // Sleep pauses the current goroutine for the duration d.
// January 1, 1970 00:00:00 UTC. func Sleep(d Duration)
func Nanoseconds() int64
// Sleep pauses the current goroutine for at least ns nanoseconds. // readFile reads and returns the content of the named file.
func Sleep(ns int64) // It is a trivial implementation of ioutil.ReadFile, reimplemented
// here to avoid depending on io/ioutil or os.
func readFile(name string) ([]byte, error) {
f, err := syscall.Open(name, syscall.O_RDONLY, 0)
if err != nil {
return nil, err
}
defer syscall.Close(f)
var (
buf [4096]byte
ret []byte
n int
)
for {
n, err = syscall.Read(f, buf[:])
if n > 0 {
ret = append(ret, buf[:n]...)
}
if n == 0 || err != nil {
break
}
}
return ret, err
}
...@@ -6,12 +6,9 @@ ...@@ -6,12 +6,9 @@
package time package time
import ( import "syscall"
"os"
"syscall"
)
// for testing: whatever interrupts a sleep // for testing: whatever interrupts a sleep
func interrupt() { func interrupt() {
syscall.Kill(os.Getpid(), syscall.SIGCHLD) syscall.Kill(syscall.Getpid(), syscall.SIGCHLD)
} }
...@@ -9,27 +9,27 @@ import "errors" ...@@ -9,27 +9,27 @@ import "errors"
// A Ticker holds a synchronous channel that delivers `ticks' of a clock // A Ticker holds a synchronous channel that delivers `ticks' of a clock
// at intervals. // at intervals.
type Ticker struct { type Ticker struct {
C <-chan int64 // The channel on which the ticks are delivered. C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer r runtimeTimer
} }
// NewTicker returns a new Ticker containing a channel that will // NewTicker returns a new Ticker containing a channel that will send the
// send the time, in nanoseconds, every ns nanoseconds. It adjusts the // time, in nanoseconds, with a period specified by the duration argument.
// intervals to make up for pauses in delivery of the ticks. The value of // It adjusts the intervals or drops ticks to make up for slow receivers.
// ns must be greater than zero; if not, NewTicker will panic. // The duration d must be greater than zero; if not, NewTicker will panic.
func NewTicker(ns int64) *Ticker { func NewTicker(d Duration) *Ticker {
if ns <= 0 { if d <= 0 {
panic(errors.New("non-positive interval for NewTicker")) panic(errors.New("non-positive interval for NewTicker"))
} }
// Give the channel a 1-element time buffer. // Give the channel a 1-element time buffer.
// If the client falls behind while reading, we drop ticks // If the client falls behind while reading, we drop ticks
// on the floor until the client catches up. // on the floor until the client catches up.
c := make(chan int64, 1) c := make(chan Time, 1)
t := &Ticker{ t := &Ticker{
C: c, C: c,
r: runtimeTimer{ r: runtimeTimer{
when: Nanoseconds() + ns, when: nano() + int64(d),
period: ns, period: int64(d),
f: sendTime, f: sendTime,
arg: c, arg: c,
}, },
...@@ -45,9 +45,9 @@ func (t *Ticker) Stop() { ...@@ -45,9 +45,9 @@ func (t *Ticker) Stop() {
// Tick is a convenience wrapper for NewTicker providing access to the ticking // Tick is a convenience wrapper for NewTicker providing access to the ticking
// channel only. Useful for clients that have no need to shut down the ticker. // channel only. Useful for clients that have no need to shut down the ticker.
func Tick(ns int64) <-chan int64 { func Tick(d Duration) <-chan Time {
if ns <= 0 { if d <= 0 {
return nil return nil
} }
return NewTicker(ns).C return NewTicker(d).C
} }
...@@ -11,21 +11,21 @@ import ( ...@@ -11,21 +11,21 @@ import (
func TestTicker(t *testing.T) { func TestTicker(t *testing.T) {
const ( const (
Delta = 100 * 1e6 Delta = 100 * Millisecond
Count = 10 Count = 10
) )
ticker := NewTicker(Delta) ticker := NewTicker(Delta)
t0 := Nanoseconds() t0 := Now()
for i := 0; i < Count; i++ { for i := 0; i < Count; i++ {
<-ticker.C <-ticker.C
} }
ticker.Stop() ticker.Stop()
t1 := Nanoseconds() t1 := Now()
ns := t1 - t0 dt := t1.Sub(t0)
target := int64(Delta * Count) target := Delta * Count
slop := target * 2 / 10 slop := target * 2 / 10
if ns < target-slop || ns > target+slop { if dt < target-slop || dt > target+slop {
t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target)) t.Fatalf("%d %s ticks took %s, expected [%s,%s]", Count, Delta, dt, target-slop, target+slop)
} }
// Now test that the ticker stopped // Now test that the ticker stopped
Sleep(2 * Delta) Sleep(2 * Delta)
......
This diff is collapsed.
This diff is collapsed.
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package time
import "sync"
// A Location maps time instants to the zone in use at that time.
// Typically, the Location represents the collection of time offsets
// in use in a geographical area, such as CEST and CET for central Europe.
type Location struct {
name string
zone []zone
tx []zoneTrans
// Most lookups will be for the current time.
// To avoid the binary search through tx, keep a
// static one-element cache that gives the correct
// zone for the time when the Location was created.
// if cacheStart <= t <= cacheEnd,
// lookup can return cacheZone.
// The units for cacheStart and cacheEnd are seconds
// since January 1, 1970 UTC, to match the argument
// to lookup.
cacheStart int64
cacheEnd int64
cacheZone *zone
}
// A zone represents a single time zone such as CEST or CET.
type zone struct {
name string // abbreviated name, "CET"
offset int // seconds east of UTC
isDST bool // is this zone Daylight Savings Time?
}
// A zoneTrans represents a single time zone transition.
type zoneTrans struct {
when int64 // transition time, in seconds since 1970 GMT
index uint8 // the index of the zone that goes into effect at that time
isstd, isutc bool // ignored - no idea what these mean
}
// UTC represents Universal Coordinated Time (UTC).
var UTC *Location = &utcLoc
// utcLoc is separate so that get can refer to &utcLoc
// and ensure that it never returns a nil *Location,
// even if a badly behaved client has changed UTC.
var utcLoc = Location{name: "UTC"}
// Local represents the system's local time zone.
var Local *Location = &localLoc
// localLoc is separate so that initLocal can initialize
// it even if a client has changed Local.
var localLoc Location
var localOnce sync.Once
func (l *Location) get() *Location {
if l == nil {
return &utcLoc
}
if l == &localLoc {
localOnce.Do(initLocal)
}
return l
}
// String returns a descriptive name for the time zone information,
// corresponding to the argument to LoadLocation.
func (l *Location) String() string {
return l.get().name
}
// FixedZone returns a Location that always uses
// the given zone name and offset (seconds east of UTC).
func FixedZone(name string, offset int) *Location {
l := &Location{
name: name,
zone: []zone{{name, offset, false}},
tx: []zoneTrans{{-1 << 63, 0, false, false}},
cacheStart: -1 << 63,
cacheEnd: 1<<63 - 1,
}
l.cacheZone = &l.zone[0]
return l
}
// lookup returns information about the time zone in use at an
// instant in time expressed as seconds since January 1, 1970 00:00:00 UTC.
//
// The returned information gives the name of the zone (such as "CET"),
// the start and end times bracketing sec when that zone is in effect,
// the offset in seconds east of UTC (such as -5*60*60), and whether
// the daylight savings is being observed at that time.
func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) {
l = l.get()
if len(l.tx) == 0 {
name = "UTC"
offset = 0
isDST = false
start = -1 << 63
end = 1<<63 - 1
return
}
if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = l.cacheStart
end = l.cacheEnd
return
}
// Binary search for entry with largest time <= sec.
// Not using sort.Search to avoid dependencies.
tx := l.tx
end = 1<<63 - 1
for len(tx) > 1 {
m := len(tx) / 2
lim := tx[m].when
if sec < lim {
end = lim
tx = tx[0:m]
} else {
tx = tx[m:]
}
}
zone := &l.zone[tx[0].index]
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = tx[0].when
// end = maintained during the search
return
}
// lookupName returns information about the time zone with
// the given name (such as "EST").
func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) {
l = l.get()
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
return zone.offset, zone.isDST, true
}
}
return
}
// lookupOffset returns information about the time zone with
// the given offset (such as -5*60*60).
func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
l = l.get()
for i := range l.zone {
zone := &l.zone[i]
if zone.offset == offset {
return zone.name, zone.isDST, true
}
}
return
}
// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
// syntax too, but I don't feel like implementing it today.
// NOTE(rsc): Using the IANA names below means ensuring we have access
// to the database. Probably we will ship the files in $GOROOT/lib/zoneinfo/
// and only look there if there are no system files available (such as on Windows).
// The files total 200 kB.
// LoadLocation returns the Location with the given name.
//
// If the name is "" or "UTC", LoadLocation returns UTC.
// If the name is "Local", LoadLocation returns Local.
//
// Otherwise, the name is taken to be a location name corresponding to a file
// in the IANA Time Zone database, such as "America/New_York".
func LoadLocation(name string) (*Location, error) {
if name == "" || name == "UTC" {
return UTC, nil
}
if name == "Local" {
return Local, nil
}
return loadLocation(name)
}
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
package time package time
import ( import (
"os"
"strconv" "strconv"
"strings" "strings"
) )
...@@ -49,7 +48,7 @@ func parseZones(s string) (zt []zonetime) { ...@@ -49,7 +48,7 @@ func parseZones(s string) (zt []zonetime) {
return return
} }
func setupZone() { func initLocal() {
t, err := os.Getenverror("timezone") t, err := os.Getenverror("timezone")
if err != nil { if err != nil {
// do nothing: use UTC // do nothing: use UTC
...@@ -58,16 +57,8 @@ func setupZone() { ...@@ -58,16 +57,8 @@ func setupZone() {
zones = parseZones(t) zones = parseZones(t)
} }
func setupTestingZone() { func initTestingZone() {
f, err := os.Open("/adm/timezone/US_Pacific") buf, err := readFile("/adm/timezone/US_Pacific")
if err != nil {
return
}
defer f.Close()
l, _ := f.Seek(0, 2)
f.Seek(0, 0)
buf := make([]byte, l)
_, err = f.Read(buf)
if err != nil { if err != nil {
return return
} }
......
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd linux openbsd plan9
package time
import "sync"
// Parsed representation
type zone struct {
utcoff int
isdst bool
name string
}
type zonetime struct {
time int32 // transition time, in seconds since 1970 GMT
zone *zone // the zone that goes into effect at that time
isstd, isutc bool // ignored - no idea what these mean
}
var zones []zonetime
var onceSetupZone sync.Once
// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
func lookupTimezone(sec int64) (zone string, offset int) {
onceSetupZone.Do(setupZone)
if len(zones) == 0 {
return "UTC", 0
}
// Binary search for entry with largest time <= sec
tz := zones
for len(tz) > 1 {
m := len(tz) / 2
if sec < int64(tz[m].time) {
tz = tz[0:m]
} else {
tz = tz[m:]
}
}
z := tz[0].zone
return z.name, z.utcoff
}
// lookupByName returns the time offset for the
// time zone with the given abbreviation. It only considers
// time zones that apply to the current system.
// For example, for a system configured as being in New York,
// it only recognizes "EST" and "EDT".
// For a system in San Francisco, "PST" and "PDT".
// For a system in Sydney, "EST" and "EDT", though they have
// different meanings than they do in New York.
func lookupByName(name string) (off int, found bool) {
onceSetupZone.Do(setupZone)
for _, z := range zones {
if name == z.zone.name {
return z.zone.utcoff, true
}
}
return 0, false
}
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
package time package time
import ( import (
"bytes" "errors"
"os" "syscall"
) )
const ( const (
...@@ -65,18 +65,20 @@ func byteString(p []byte) string { ...@@ -65,18 +65,20 @@ func byteString(p []byte) string {
return string(p) return string(p)
} }
func parseinfo(bytes []byte) (zt []zonetime, ok bool) { var badData = errors.New("malformed time zone information")
func loadZoneData(bytes []byte) (l *Location, err error) {
d := data{bytes, false} d := data{bytes, false}
// 4-byte magic "TZif" // 4-byte magic "TZif"
if magic := d.read(4); string(magic) != "TZif" { if magic := d.read(4); string(magic) != "TZif" {
return nil, false return nil, badData
} }
// 1-byte version, then 15 bytes of padding // 1-byte version, then 15 bytes of padding
var p []byte var p []byte
if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' { if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
return nil, false return nil, badData
} }
// six big-endian 32-bit integers: // six big-endian 32-bit integers:
...@@ -98,7 +100,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { ...@@ -98,7 +100,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
for i := 0; i < 6; i++ { for i := 0; i < 6; i++ {
nn, ok := d.big4() nn, ok := d.big4()
if !ok { if !ok {
return nil, false return nil, badData
} }
n[i] = int(nn) n[i] = int(nn)
} }
...@@ -127,7 +129,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { ...@@ -127,7 +129,7 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
isutc := d.read(n[NUTCLocal]) isutc := d.read(n[NUTCLocal])
if d.error { // ran out of data if d.error { // ran out of data
return nil, false return nil, badData
} }
// If version == 2, the entire file repeats, this time using // If version == 2, the entire file repeats, this time using
...@@ -137,90 +139,119 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { ...@@ -137,90 +139,119 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
// Now we can build up a useful data structure. // Now we can build up a useful data structure.
// First the zone information. // First the zone information.
// utcoff[4] isdst[1] nameindex[1] // utcoff[4] isdst[1] nameindex[1]
z := make([]zone, n[NZone]) zone := make([]zone, n[NZone])
for i := 0; i < len(z); i++ { for i := range zone {
var ok bool var ok bool
var n uint32 var n uint32
if n, ok = zonedata.big4(); !ok { if n, ok = zonedata.big4(); !ok {
return nil, false return nil, badData
} }
z[i].utcoff = int(n) zone[i].offset = int(n)
var b byte var b byte
if b, ok = zonedata.byte(); !ok { if b, ok = zonedata.byte(); !ok {
return nil, false return nil, badData
} }
z[i].isdst = b != 0 zone[i].isDST = b != 0
if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
return nil, false return nil, badData
} }
z[i].name = byteString(abbrev[b:]) zone[i].name = byteString(abbrev[b:])
} }
// Now the transition time info. // Now the transition time info.
zt = make([]zonetime, n[NTime]) tx := make([]zoneTrans, n[NTime])
for i := 0; i < len(zt); i++ { for i := range tx {
var ok bool var ok bool
var n uint32 var n uint32
if n, ok = txtimes.big4(); !ok { if n, ok = txtimes.big4(); !ok {
return nil, false return nil, badData
} }
zt[i].time = int32(n) tx[i].when = int64(int32(n))
if int(txzones[i]) >= len(z) { if int(txzones[i]) >= len(zone) {
return nil, false return nil, badData
} }
zt[i].zone = &z[txzones[i]] tx[i].index = txzones[i]
if i < len(isstd) { if i < len(isstd) {
zt[i].isstd = isstd[i] != 0 tx[i].isstd = isstd[i] != 0
} }
if i < len(isutc) { if i < len(isutc) {
zt[i].isutc = isutc[i] != 0 tx[i].isutc = isutc[i] != 0
} }
} }
return zt, true
}
func readinfofile(name string) ([]zonetime, bool) { // Commited to succeed.
var b bytes.Buffer l = &Location{zone: zone, tx: tx}
f, err := os.Open(name) // Fill in the cache with information about right now,
if err != nil { // since that will be the most common lookup.
return nil, false sec, _ := now()
for i := range tx {
if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
l.cacheStart = tx[i].when
l.cacheEnd = 1<<63 - 1
if i+1 < len(tx) {
l.cacheEnd = tx[i+1].when
}
l.cacheZone = &l.zone[tx[i].index]
}
} }
defer f.Close()
if _, err := b.ReadFrom(f); err != nil { return l, nil
return nil, false }
func loadZoneFile(name string) (l *Location, err error) {
buf, err := readFile(name)
if err != nil {
return
} }
return parseinfo(b.Bytes()) return loadZoneData(buf)
} }
func setupTestingZone() { func initTestingZone() {
os.Setenv("TZ", "America/Los_Angeles") syscall.Setenv("TZ", "America/Los_Angeles")
setupZone() initLocal()
} }
func setupZone() { // Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
var zoneDirs = []string{
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
}
func initLocal() {
// consult $TZ to find the time zone to use. // consult $TZ to find the time zone to use.
// no $TZ means use the system default /etc/localtime. // no $TZ means use the system default /etc/localtime.
// $TZ="" means use UTC. // $TZ="" means use UTC.
// $TZ="foo" means use /usr/share/zoneinfo/foo. // $TZ="foo" means use /usr/share/zoneinfo/foo.
// Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
zoneDirs := []string{"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/"}
tz, err := os.Getenverror("TZ") tz, ok := syscall.Getenv("TZ")
switch { switch {
case err == os.ENOENV: case !ok:
zones, _ = readinfofile("/etc/localtime") z, err := loadZoneFile("/etc/localtime")
case len(tz) > 0: if err == nil {
for _, zoneDir := range zoneDirs { localLoc = *z
var ok bool localLoc.name = "Local"
if zones, ok = readinfofile(zoneDir + tz); ok { return
break }
} case tz != "" && tz != "UTC":
if z, err := loadLocation(tz); err == nil {
localLoc = *z
return
}
}
// Fall back to UTC.
localLoc.name = "UTC"
}
func loadLocation(name string) (*Location, error) {
for _, zoneDir := range zoneDirs {
if z, err := loadZoneFile(zoneDir + name); err == nil {
z.name = name
return z, nil
} }
case len(tz) == 0:
// do nothing: use UTC
} }
return nil, errors.New("unknown time zone " + name)
} }
...@@ -5,34 +5,21 @@ ...@@ -5,34 +5,21 @@
package time package time
import ( import (
"os" "errors"
"sync"
"syscall" "syscall"
) )
// BUG(brainman): The Windows implementation assumes that // TODO(rsc): Fall back to copy of zoneinfo files.
// this year's rules for daylight savings time apply to all previous
// and future years as well.
// TODO(brainman): use GetDynamicTimeZoneInformation, whenever possible (Vista and up),
// to improve on situation described in the bug above.
type zone struct {
name string
offset int
year int64
month, day, dayofweek int
hour, minute, second int
abssec int64
prev *zone
}
// BUG(rsc): On Windows, time zone abbreviations are unavailable. // BUG(brainman,rsc): On Windows, the operating system does not provide complete
// This package constructs them using the capital letters from a longer // time zone information.
// time zone description. // The implementation assumes that this year's rules for daylight savings
// time apply to all previous and future years as well.
// Also, time zone abbreviations are unavailable. The implementation constructs
// them using the capital letters from a longer time zone description.
// Populate zone struct with Windows supplied information. Returns true, if data is valid. // abbrev returns the abbreviation to use for the given zone name.
func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uint16) (dateisgood bool) { func abbrev(name []uint16) string {
// name is 'Pacific Standard Time' but we want 'PST'. // name is 'Pacific Standard Time' but we want 'PST'.
// Extract just capital letters. It's not perfect but the // Extract just capital letters. It's not perfect but the
// information we need is not available from the kernel. // information we need is not available from the kernel.
...@@ -41,147 +28,98 @@ func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uin ...@@ -41,147 +28,98 @@ func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uin
// //
// http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb // http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
// http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net // http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
short := make([]uint16, len(name)) short := make([]rune, len(name))
w := 0 w := 0
for _, c := range name { for _, c := range name {
if 'A' <= c && c <= 'Z' { if 'A' <= c && c <= 'Z' {
short[w] = c short[w] = rune(c)
w++ w++
} }
} }
z.name = syscall.UTF16ToString(short[:w]) return string(short)
z.offset = int(bias)
z.year = int64(d.Year)
z.month = int(d.Month)
z.day = int(d.Day)
z.dayofweek = int(d.DayOfWeek)
z.hour = int(d.Hour)
z.minute = int(d.Minute)
z.second = int(d.Second)
dateisgood = d.Month != 0
if dateisgood {
z.offset += int(biasdelta)
}
z.offset = -z.offset * 60
return
}
// Pre-calculate cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format.
func (z *zone) preCalculateAbsSec() {
if z.year != 0 {
t := &Time{
Year: z.year,
Month: int(z.month),
Day: int(z.day),
Hour: int(z.hour),
Minute: int(z.minute),
Second: int(z.second),
}
z.abssec = t.Seconds()
// Time given is in "local" time. Adjust it for "utc".
z.abssec -= int64(z.prev.offset)
}
} }
// Convert zone cutoff time to sec in number of seconds since the Unix epoch, given particular year. // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
func (z *zone) cutoffSeconds(year int64) int64 { // denoted by the system date+time d in the given year.
// It is up to the caller to convert this local time into a UTC-based time.
func pseudoUnix(year int, d *syscall.Systemtime) int64 {
// Windows specifies daylight savings information in "day in month" format: // Windows specifies daylight savings information in "day in month" format:
// z.month is month number (1-12) // d.Month is month number (1-12)
// z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6) // d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
// z.day is week within the month (1 to 5, where 5 is last week of the month) // d.Day is week within the month (1 to 5, where 5 is last week of the month)
// z.hour, z.minute and z.second are absolute time // d.Hour, d.Minute and d.Second are absolute time
t := &Time{ day := 1
Year: year, t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
Month: int(z.month), i := int(d.DayOfWeek) - int(t.Weekday())
Day: 1,
Hour: int(z.hour),
Minute: int(z.minute),
Second: int(z.second),
}
t = SecondsToUTC(t.Seconds())
i := int(z.dayofweek) - t.Weekday()
if i < 0 { if i < 0 {
i += 7 i += 7
} }
t.Day += i day += i
if week := int(z.day) - 1; week < 4 { if week := int(d.Day) - 1; week < 4 {
t.Day += week * 7 day += week * 7
} else { } else {
// "Last" instance of the day. // "Last" instance of the day.
t.Day += 4 * 7 day += 4 * 7
if t.Day > months(year)[t.Month] { if day > daysIn(Month(d.Month), year) {
t.Day -= 7 day -= 7
} }
} }
// Result is in "local" time. Adjust it for "utc". return t.sec + int64(day-1)*secondsPerDay
return t.Seconds() - int64(z.prev.offset)
} }
// Is t before the cutoff for switching to z? func initLocalFromTZI(i *syscall.Timezoneinformation) {
func (z *zone) isBeforeCutoff(t *Time) bool { l := &localLoc
var coff int64
if z.year == 0 {
// "day in month" format used
coff = z.cutoffSeconds(t.Year)
} else {
// "absolute" format used
coff = z.abssec
}
return t.Seconds() < coff
}
type zoneinfo struct { nzone := 1
disabled bool // daylight saving time is not used locally if i.StandardDate.Month > 0 {
offsetIfDisabled int nzone++
januaryIsStd bool // is january 1 standard time? }
std, dst zone l.zone = make([]zone, nzone)
}
// Pick zone (std or dst) t time belongs to. std := &l.zone[0]
func (zi *zoneinfo) pickZone(t *Time) *zone { std.name = abbrev(i.StandardName[0:])
z := &zi.std std.offset = -int(i.StandardBias) * 60
if tz.januaryIsStd { if nzone == 1 {
if !zi.dst.isBeforeCutoff(t) && zi.std.isBeforeCutoff(t) { // No daylight savings.
// after switch to daylight time and before the switch back to standard l.cacheStart = -1 << 63
z = &zi.dst l.cacheEnd = 1<<63 - 1
} l.cacheZone = std
} else { return
if zi.std.isBeforeCutoff(t) || !zi.dst.isBeforeCutoff(t) {
// before switch to standard time or after the switch back to daylight
z = &zi.dst
}
} }
return z
}
var tz zoneinfo dst := &l.zone[1]
var initError error dst.name = abbrev(i.DaylightName[0:])
var onceSetupZone sync.Once dst.offset = std.offset + -int(i.DaylightBias)*60
dst.isDST = true
func setupZone() { // Arrange so that d0 is first transition date, d1 second,
var i syscall.Timezoneinformation // i0 is index of zone after first transition, i1 second.
if _, e := syscall.GetTimeZoneInformation(&i); e != nil { d0 := &i.StandardDate
initError = os.NewSyscallError("GetTimeZoneInformation", e) d1 := &i.DaylightDate
return i0 := 0
i1 := 1
if d0.Month > d1.Month {
d0, d1 = d1, d0
i0, i1 = i1, i0
} }
setupZoneFromTZI(&i)
}
func setupZoneFromTZI(i *syscall.Timezoneinformation) { // 2 tx per year, 100 years on each side of this year
if !tz.std.populate(i.Bias, i.StandardBias, &i.StandardDate, i.StandardName[0:]) { l.tx = make([]zoneTrans, 400)
tz.disabled = true
tz.offsetIfDisabled = tz.std.offset t := Now().UTC()
return year := t.Year()
txi := 0
for y := year - 100; y < year+100; y++ {
tx := &l.tx[txi]
tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
tx.index = uint8(i0)
txi++
tx = &l.tx[txi]
tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
tx.index = uint8(i1)
txi++
} }
tz.std.prev = &tz.dst
tz.dst.populate(i.Bias, i.DaylightBias, &i.DaylightDate, i.DaylightName[0:])
tz.dst.prev = &tz.std
tz.std.preCalculateAbsSec()
tz.dst.preCalculateAbsSec()
// Is january 1 standard time this year?
t := UTC()
tz.januaryIsStd = tz.dst.cutoffSeconds(t.Year) < tz.std.cutoffSeconds(t.Year)
} }
var usPacific = syscall.Timezoneinformation{ var usPacific = syscall.Timezoneinformation{
...@@ -197,53 +135,20 @@ var usPacific = syscall.Timezoneinformation{ ...@@ -197,53 +135,20 @@ var usPacific = syscall.Timezoneinformation{
DaylightBias: -60, DaylightBias: -60,
} }
func setupTestingZone() { func initTestingZone() {
setupZoneFromTZI(&usPacific) initLocalFromTZI(&usPacific)
} }
// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location. func initLocal() {
func lookupTimezone(sec int64) (zone string, offset int) { var i syscall.Timezoneinformation
onceSetupZone.Do(setupZone) if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
if initError != nil { localLoc.name = "UTC"
return "", 0 return
}
if tz.disabled {
return "", tz.offsetIfDisabled
}
t := SecondsToUTC(sec)
z := &tz.std
if tz.std.year == 0 {
// "day in month" format used
z = tz.pickZone(t)
} else {
// "absolute" format used
if tz.std.year == t.Year {
// we have rule for the year in question
z = tz.pickZone(t)
} else {
// we do not have any information for that year,
// will assume standard offset all year around
}
} }
return z.name, z.offset initLocalFromTZI(&i)
} }
// lookupByName returns the time offset for the // TODO(rsc): Implement.
// time zone with the given abbreviation. It only considers func loadLocation(name string) (*Location, error) {
// time zones that apply to the current system. return nil, errors.New("unknown time zone " + name)
func lookupByName(name string) (off int, found bool) {
onceSetupZone.Do(setupZone)
if initError != nil {
return 0, false
}
if tz.disabled {
return tz.offsetIfDisabled, false
}
switch name {
case tz.std.name:
return tz.std.offset, true
case tz.dst.name:
return tz.dst.offset, true
}
return 0, false
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment