summaryrefslogtreecommitdiff
path: root/libgo/go/time
diff options
context:
space:
mode:
authorbstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-08 10:11:09 +0000
committerbstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4>2012-03-08 10:11:09 +0000
commit6195c0dd4e15f50ac89491b48e050751f8231304 (patch)
tree1f49de2cfcd902f18c22b5539315d7b0fb4db972 /libgo/go/time
parentd7ce7f9586bca838e0dcc7e39100ffe6edcd74f3 (diff)
downloadgcc-6195c0dd4e15f50ac89491b48e050751f8231304.tar.gz
2012-03-08 Basile Starynkevitch <basile@starynkevitch.net>
MELT branch merged with trunk [future 4.8] rev 185094 using svnmerge 2011-03-08 Basile Starynkevitch <basile@starynkevitch.net> [gcc/] * melt-build.tpl (meltframe.args): Add -Iinclude-fixed if it exists. * melt-build.mk: Regenerate. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@185096 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/time')
-rw-r--r--libgo/go/time/example_test.go2
-rw-r--r--libgo/go/time/format.go125
-rw-r--r--libgo/go/time/sleep_test.go25
-rw-r--r--libgo/go/time/sys_plan9.go38
-rw-r--r--libgo/go/time/sys_unix.go38
-rw-r--r--libgo/go/time/sys_windows.go64
-rw-r--r--libgo/go/time/tick_test.go12
-rw-r--r--libgo/go/time/time.go50
-rw-r--r--libgo/go/time/time_test.go42
-rw-r--r--libgo/go/time/zoneinfo.go23
-rw-r--r--libgo/go/time/zoneinfo_plan9.go27
-rw-r--r--libgo/go/time/zoneinfo_read.go341
-rw-r--r--libgo/go/time/zoneinfo_unix.go197
-rw-r--r--libgo/go/time/zoneinfo_windows.go6
14 files changed, 670 insertions, 320 deletions
diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go
index 153b1a3b660..944cc789c31 100644
--- a/libgo/go/time/example_test.go
+++ b/libgo/go/time/example_test.go
@@ -51,8 +51,8 @@ func ExampleMonth() {
}
}
-// 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())
+ // Output: Go launched at 2009-11-10 15:00:00 -0800 PST
}
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go
index 76bf6ff4194..ad52bab216f 100644
--- a/libgo/go/time/format.go
+++ b/libgo/go/time/format.go
@@ -6,28 +6,25 @@ package time
import "errors"
-const (
- numeric = iota
- alphabetic
- separator
- plus
- minus
-)
-
// These are predefined layouts for use in Time.Format.
// The standard time used in the layouts is:
-// Mon Jan 2 15:04:05 MST 2006 (MST is GMT-0700)
-// which is Unix time 1136243045.
-// (Think of it as 01/02 03:04:05PM '06 -0700.)
-// To define your own format, write down what the standard
-// time would look like formatted your way.
+// Mon Jan 2 15:04:05 MST 2006
+// which is Unix time 1136243045. Since MST is GMT-0700,
+// the standard time can be thought of as
+// 01/02 03:04:05PM '06 -0700
+// To define your own format, write down what the standard time would look
+// like formatted your way; see the values of constants like ANSIC,
+// StampMicro or Kitchen for examples.
//
// Within the format string, an underscore _ represents a space that may be
// replaced by a digit if the following number (a day) has two digits; for
// compatibility with fixed-width Unix time formats.
//
// A decimal point followed by one or more zeros represents a fractional
-// second. When parsing (only), the input may contain a fractional second
+// second, printed to the given number of decimal places. A decimal point
+// followed by one or more nines represents a fractional second, printed to
+// the given number of decimal places, with trailing zeros removed.
+// When parsing (only), the input may contain a fractional second
// field immediately after the seconds field, even if the layout does not
// signify its presence. In that case a decimal point followed by a maximal
// series of digits is parsed as a fractional second.
@@ -41,16 +38,17 @@ const (
// Z0700 Z or ±hhmm
// Z07:00 Z or ±hh:mm
const (
- ANSIC = "Mon Jan _2 15:04:05 2006"
- UnixDate = "Mon Jan _2 15:04:05 MST 2006"
- RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
- RFC822 = "02 Jan 06 1504 MST"
- RFC822Z = "02 Jan 06 1504 -0700" // RFC822 with numeric zone
- RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
- RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
- RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
- RFC3339 = "2006-01-02T15:04:05Z07:00"
- Kitchen = "3:04PM"
+ ANSIC = "Mon Jan _2 15:04:05 2006"
+ UnixDate = "Mon Jan _2 15:04:05 MST 2006"
+ RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
+ RFC822 = "02 Jan 06 1504 MST"
+ RFC822Z = "02 Jan 06 1504 -0700" // RFC822 with numeric zone
+ RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
+ RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
+ RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
+ RFC3339 = "2006-01-02T15:04:05Z07:00"
+ RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
+ Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
@@ -165,15 +163,17 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
if len(layout) >= i+6 && layout[i:i+6] == stdISO8601ColonTZ {
return layout[0:i], layout[i : i+6], layout[i+6:]
}
- case '.': // .000 - multiple digits of zeros (only) for fractional seconds.
- numZeros := 0
- var j int
- for j = i + 1; j < len(layout) && layout[j] == '0'; j++ {
- numZeros++
- }
- // String of digits must end here - only fractional second is all zeros.
- if numZeros > 0 && !isDigit(layout, j) {
- return layout[0:i], layout[i : i+1+numZeros], layout[i+1+numZeros:]
+ case '.': // .000 or .999 - repeated digits for fractional seconds.
+ if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
+ ch := layout[i+1]
+ j := i + 1
+ for j < len(layout) && layout[j] == ch {
+ j++
+ }
+ // String of digits must end here - only fractional second is all digits.
+ if !isDigit(layout, j) {
+ return layout[0:i], layout[i:j], layout[j:]
+ }
}
}
}
@@ -313,7 +313,7 @@ func pad(i int, padding string) string {
func zeroPad(i int) string { return pad(i, "0") }
// formatNano formats a fractional second, as nanoseconds.
-func formatNano(nanosec, n int) string {
+func formatNano(nanosec, n int, trim bool) string {
// User might give us bad data. Make sure it's positive and in range.
// They'll get nonsense output but it will have the right format.
s := itoa(int(uint(nanosec) % 1e9))
@@ -324,13 +324,21 @@ func formatNano(nanosec, n int) string {
if n > 9 {
n = 9
}
+ if trim {
+ for n > 0 && s[n-1] == '0' {
+ n--
+ }
+ if n == 0 {
+ return ""
+ }
+ }
return "." + s[:n]
}
// String returns the time formatted using the format string
-// "Mon Jan _2 15:04:05 -0700 MST 2006"
+// "2006-01-02 15:04:05.999999999 -0700 MST"
func (t Time) String() string {
- return t.Format("Mon Jan _2 15:04:05 -0700 MST 2006")
+ return t.Format("2006-01-02 15:04:05.999999999 -0700 MST")
}
type buffer []byte
@@ -345,10 +353,12 @@ func (b *buffer) String() string {
// Format returns a textual representation of the time value formatted
// according to layout. The layout defines the format by showing the
-// representation of a standard time, which is then used to describe
-// the time to be formatted. Predefined layouts ANSIC, UnixDate,
-// RFC3339 and others describe standard representations. For more
-// information about the formats, see the documentation for ANSIC.
+// representation of the standard time,
+// Mon Jan 2 15:04:05 -0700 MST 2006
+// which is then used to describe the time to be formatted. Predefined
+// layouts ANSIC, UnixDate, RFC3339 and others describe standard
+// representations. For more information about the formats and the
+// definition of the standard time, see the documentation for ANSIC.
func (t Time) Format(layout string) string {
var (
year int = -1
@@ -388,7 +398,24 @@ func (t Time) Format(layout string) string {
case stdYear:
p = zeroPad(year % 100)
case stdLongYear:
+ // Pad year to at least 4 digits.
p = itoa(year)
+ switch {
+ case year <= -1000:
+ // ok
+ case year <= -100:
+ p = p[:1] + "0" + p[1:]
+ case year <= -10:
+ p = p[:1] + "00" + p[1:]
+ case year < 0:
+ p = p[:1] + "000" + p[1:]
+ case year < 10:
+ p = "000" + p
+ case year < 100:
+ p = "00" + p
+ case year < 1000:
+ p = "0" + p
+ }
case stdMonth:
p = month.String()[:3]
case stdLongMonth:
@@ -481,8 +508,8 @@ func (t Time) Format(layout string) string {
p += zeroPad(zone % 60)
}
default:
- if len(std) >= 2 && std[0:2] == ".0" {
- p = formatNano(t.Nanosecond(), len(std)-1)
+ if len(std) >= 2 && (std[0:2] == ".0" || std[0:2] == ".9") {
+ p = formatNano(t.Nanosecond(), len(std)-1, std[1] == '9')
}
}
b.WriteString(p)
@@ -574,13 +601,15 @@ func skip(value, prefix string) (string, error) {
}
// Parse parses a formatted string and returns the time value it represents.
-// The layout defines the format by showing the representation of a standard
-// time, which is then used to describe the string to be parsed. Predefined
-// layouts ANSIC, UnixDate, RFC3339 and others describe standard
-// representations.For more information about the formats, see the
-// documentation for ANSIC.
+// The layout defines the format by showing the representation of the
+// standard time,
+// Mon Jan 2 15:04:05 -0700 MST 2006
+// which is then used to describe the string to be parsed. Predefined layouts
+// ANSIC, UnixDate, RFC3339 and others describe standard representations. For
+// more information about the formats and the definition of the standard
+// time, see the documentation for ANSIC.
//
-// Elements omitted from the value are assumed to be zero, or when
+// Elements omitted from the value are assumed to be zero or, when
// zero is impossible, one, so parsing "3:04pm" returns the time
// corresponding to Jan 1, year 0, 15:04:00 UTC.
// Years must be in the range 0000..9999. The day of the week is checked
diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go
index 3cb088c319f..440d3b42f12 100644
--- a/libgo/go/time/sleep_test.go
+++ b/libgo/go/time/sleep_test.go
@@ -108,10 +108,11 @@ func TestAfter(t *testing.T) {
}
func TestAfterTick(t *testing.T) {
- const (
- Delta = 100 * Millisecond
- Count = 10
- )
+ const Count = 10
+ Delta := 100 * Millisecond
+ if testing.Short() {
+ Delta = 10 * Millisecond
+ }
t0 := Now()
for i := 0; i < Count; i++ {
<-After(Delta)
@@ -119,8 +120,11 @@ func TestAfterTick(t *testing.T) {
t1 := Now()
d := t1.Sub(t0)
target := Delta * Count
- if d < target*9/10 || d > target*30/10 {
- t.Fatalf("%d ticks of %s took %s, expected %s", Count, Delta, d, target)
+ if d < target*9/10 {
+ t.Fatalf("%d ticks of %s too fast: took %s, expected %s", Count, Delta, d, target)
+ }
+ if !testing.Short() && d > target*30/10 {
+ t.Fatalf("%d ticks of %s too slow: took %s, expected %s", Count, Delta, d, target)
}
}
@@ -165,7 +169,7 @@ func TestAfterQueuing(t *testing.T) {
}
// For gccgo omit 0 for now because it can take too long to start the
-var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8 /*0*/ }
+var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8 /*0*/}
type afterResult struct {
slot int
@@ -177,9 +181,10 @@ func await(slot int, result chan<- afterResult, ac <-chan Time) {
}
func testAfterQueuing(t *testing.T) error {
- const (
- Delta = 100 * Millisecond
- )
+ Delta := 100 * Millisecond
+ if testing.Short() {
+ Delta = 20 * Millisecond
+ }
// make the result channel buffered because we don't want
// to depend on channel queueing semantics that might
// possibly change in the future.
diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go
index c7cfa792a29..8484729448e 100644
--- a/libgo/go/time/sys_plan9.go
+++ b/libgo/go/time/sys_plan9.go
@@ -6,7 +6,10 @@
package time
-import "syscall"
+import (
+ "errors"
+ "syscall"
+)
// for testing: whatever interrupts a sleep
func interrupt() {
@@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) {
}
return ret, err
}
+
+func open(name string) (uintptr, error) {
+ fd, err := syscall.Open(name, syscall.O_RDONLY)
+ if err != nil {
+ return 0, err
+ }
+ return uintptr(fd), nil
+}
+
+func closefd(fd uintptr) {
+ syscall.Close(int(fd))
+}
+
+func preadn(fd uintptr, buf []byte, off int) error {
+ whence := 0
+ if off < 0 {
+ whence = 2
+ }
+ if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
+ return err
+ }
+ for len(buf) > 0 {
+ m, err := syscall.Read(int(fd), buf)
+ if m <= 0 {
+ if err == nil {
+ return errors.New("short read")
+ }
+ return err
+ }
+ buf = buf[m:]
+ }
+ return nil
+}
diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go
index 56a7414e0ce..7f69b492c9f 100644
--- a/libgo/go/time/sys_unix.go
+++ b/libgo/go/time/sys_unix.go
@@ -6,7 +6,10 @@
package time
-import "syscall"
+import (
+ "errors"
+ "syscall"
+)
// for testing: whatever interrupts a sleep
func interrupt() {
@@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) {
}
return ret, err
}
+
+func open(name string) (uintptr, error) {
+ fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
+ if err != nil {
+ return 0, err
+ }
+ return uintptr(fd), nil
+}
+
+func closefd(fd uintptr) {
+ syscall.Close(int(fd))
+}
+
+func preadn(fd uintptr, buf []byte, off int) error {
+ whence := 0
+ if off < 0 {
+ whence = 2
+ }
+ if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
+ return err
+ }
+ for len(buf) > 0 {
+ m, err := syscall.Read(int(fd), buf)
+ if m <= 0 {
+ if err == nil {
+ return errors.New("short read")
+ }
+ return err
+ }
+ buf = buf[m:]
+ }
+ return nil
+}
diff --git a/libgo/go/time/sys_windows.go b/libgo/go/time/sys_windows.go
index 8c7242f4275..de63b4bf4bb 100644
--- a/libgo/go/time/sys_windows.go
+++ b/libgo/go/time/sys_windows.go
@@ -4,6 +4,70 @@
package time
+import (
+ "errors"
+ "syscall"
+)
+
// for testing: whatever interrupts a sleep
func interrupt() {
}
+
+// readFile reads and returns the content of the named file.
+// 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
+}
+
+func open(name string) (uintptr, error) {
+ fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
+ if err != nil {
+ return 0, err
+ }
+ return uintptr(fd), nil
+}
+
+func closefd(fd uintptr) {
+ syscall.Close(syscall.Handle(fd))
+}
+
+func preadn(fd uintptr, buf []byte, off int) error {
+ whence := 0
+ if off < 0 {
+ whence = 2
+ }
+ if _, err := syscall.Seek(syscall.Handle(fd), int64(off), whence); err != nil {
+ return err
+ }
+ for len(buf) > 0 {
+ m, err := syscall.Read(syscall.Handle(fd), buf)
+ if m <= 0 {
+ if err == nil {
+ return errors.New("short read")
+ }
+ return err
+ }
+ buf = buf[m:]
+ }
+ return nil
+}
diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go
index 36349349ce0..914f02c861c 100644
--- a/libgo/go/time/tick_test.go
+++ b/libgo/go/time/tick_test.go
@@ -10,10 +10,8 @@ import (
)
func TestTicker(t *testing.T) {
- const (
- Delta = 100 * Millisecond
- Count = 10
- )
+ const Count = 10
+ Delta := 100 * Millisecond
ticker := NewTicker(Delta)
t0 := Now()
for i := 0; i < Count; i++ {
@@ -39,8 +37,12 @@ func TestTicker(t *testing.T) {
// Test that a bug tearing down a ticker has been fixed. This routine should not deadlock.
func TestTeardown(t *testing.T) {
+ Delta := 100 * Millisecond
+ if testing.Short() {
+ Delta = 20 * Millisecond
+ }
for i := 0; i < 3; i++ {
- ticker := NewTicker(1e8)
+ ticker := NewTicker(Delta)
<-ticker.C
ticker.Stop()
}
diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go
index 39d4b95dd06..f7ded24d292 100644
--- a/libgo/go/time/time.go
+++ b/libgo/go/time/time.go
@@ -152,7 +152,7 @@ func (d Weekday) String() string { return days[d] }
// rely heavily on division and modulus by positive constants. For
// calendrical calculations we want these divisions to round down, even
// for negative values, so that the remainder is always positive, but
-// Go's division (like most hardware divison instructions) rounds to
+// Go's division (like most hardware division instructions) rounds to
// zero. We can still do those computations and then adjust the result
// for a negative numerator, but it's annoying to write the adjustment
// over and over. Instead, we can change to a different epoch so long
@@ -384,6 +384,15 @@ type Duration int64
// Common durations. There is no definition for units of Day or larger
// to avoid confusion across daylight savings time zone transitions.
+//
+// To count the number of units in a Duration, divide:
+// second := time.Second
+// fmt.Print(int64(second/time.Millisecond)) // prints 1000
+//
+// To convert an integer number of units to a Duration, multiply:
+// seconds := 10
+// fmt.Print(time.Duration(seconds)*time.Second) // prints 10s
+//
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
@@ -758,10 +767,6 @@ func (t Time) UnixNano() int64 {
return (t.sec+internalToUnix)*1e9 + int64(t.nsec)
}
-type gobError string
-
-func (g gobError) Error() string { return string(g) }
-
const timeGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
@@ -841,46 +846,17 @@ func (t *Time) GobDecode(buf []byte) error {
// MarshalJSON implements the json.Marshaler interface.
// Time is formatted as RFC3339.
func (t Time) MarshalJSON() ([]byte, error) {
- yearInt := t.Year()
- if yearInt < 0 || yearInt > 9999 {
+ if y := t.Year(); y < 0 || y >= 10000 {
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}
-
- // We need a four-digit year, but Format produces variable-width years.
- year := itoa(yearInt)
- year = "0000"[:4-len(year)] + year
-
- var formattedTime string
- if t.nsec == 0 {
- // RFC3339, no fractional second
- formattedTime = t.Format("-01-02T15:04:05Z07:00")
- } else {
- // RFC3339 with fractional second
- formattedTime = t.Format("-01-02T15:04:05.000000000Z07:00")
-
- // Trim trailing zeroes from fractional second.
- const nanoEnd = 24 // Index of last digit of fractional second
- var i int
- for i = nanoEnd; formattedTime[i] == '0'; i-- {
- // Seek backwards until first significant digit is found.
- }
-
- formattedTime = formattedTime[:i+1] + formattedTime[nanoEnd+1:]
- }
-
- buf := make([]byte, 0, 1+len(year)+len(formattedTime)+1)
- buf = append(buf, '"')
- buf = append(buf, year...)
- buf = append(buf, formattedTime...)
- buf = append(buf, '"')
- return buf, nil
+ return []byte(t.Format(`"` + RFC3339Nano + `"`)), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// Time is expected in RFC3339 format.
func (t *Time) UnmarshalJSON(data []byte) (err error) {
- *t, err = Parse("\""+RFC3339+"\"", string(data))
// Fractional seconds are handled implicitly by Parse.
+ *t, err = Parse(`"`+RFC3339+`"`, string(data))
return
}
diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go
index cdc1c39c5f5..3430526b8bf 100644
--- a/libgo/go/time/time_test.go
+++ b/libgo/go/time/time_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"encoding/gob"
"encoding/json"
+ "fmt"
"math/rand"
"strconv"
"strings"
@@ -227,6 +228,7 @@ var formatTests = []FormatTest{
{"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"},
{"RFC1123Z", RFC1123Z, "Wed, 04 Feb 2009 21:00:57 -0800"},
{"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"},
+ {"RFC3339Nano", RFC3339Nano, "2009-02-04T21:00:57.0123456-08:00"},
{"Kitchen", Kitchen, "9:00PM"},
{"am/pm", "3pm", "9pm"},
{"AM/PM", "3PM", "9PM"},
@@ -235,12 +237,12 @@ var formatTests = []FormatTest{
{"Stamp", Stamp, "Feb 4 21:00:57"},
{"StampMilli", StampMilli, "Feb 4 21:00:57.012"},
{"StampMicro", StampMicro, "Feb 4 21:00:57.012345"},
- {"StampNano", StampNano, "Feb 4 21:00:57.012345678"},
+ {"StampNano", StampNano, "Feb 4 21:00:57.012345600"},
}
func TestFormat(t *testing.T) {
- // The numeric time represents Thu Feb 4 21:00:57.012345678 PST 2010
- time := Unix(0, 1233810057012345678)
+ // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2010
+ time := Unix(0, 1233810057012345600)
for _, test := range formatTests {
result := time.Format(test.format)
if result != test.result {
@@ -249,6 +251,38 @@ func TestFormat(t *testing.T) {
}
}
+func TestFormatShortYear(t *testing.T) {
+ years := []int{
+ -100001, -100000, -99999,
+ -10001, -10000, -9999,
+ -1001, -1000, -999,
+ -101, -100, -99,
+ -11, -10, -9,
+ -1, 0, 1,
+ 9, 10, 11,
+ 99, 100, 101,
+ 999, 1000, 1001,
+ 9999, 10000, 10001,
+ 99999, 100000, 100001,
+ }
+
+ for _, y := range years {
+ time := Date(y, January, 1, 0, 0, 0, 0, UTC)
+ result := time.Format("2006.01.02")
+ var want string
+ if y < 0 {
+ // The 4 in %04d counts the - sign, so print -y instead
+ // and introduce our own - sign.
+ want = fmt.Sprintf("-%04d.%02d.%02d", -y, 1, 1)
+ } else {
+ want = fmt.Sprintf("%04d.%02d.%02d", y, 1, 1)
+ }
+ if result != want {
+ t.Errorf("(jan 1 %d).Format(\"2006.01.02\") = %q, want %q", y, result, want)
+ }
+ }
+}
+
type ParseTest struct {
name string
format string
@@ -782,7 +816,7 @@ func TestTimeJSON(t *testing.T) {
if jsonBytes, err := json.Marshal(tt.time); err != nil {
t.Errorf("%v json.Marshal error = %v, want nil", tt.time, err)
} else if string(jsonBytes) != tt.json {
- t.Errorf("%v JSON = %q, want %q", tt.time, string(jsonBytes), tt.json)
+ t.Errorf("%v JSON = %#q, want %#q", tt.time, string(jsonBytes), tt.json)
} else if err = json.Unmarshal(jsonBytes, &jsonTime); err != nil {
t.Errorf("%v json.Unmarshal error = %v, want nil", tt.time, err)
} else if !equalTimeAndZone(jsonTime, tt.time) {
diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go
index aca56e746af..3c57744043e 100644
--- a/libgo/go/time/zoneinfo.go
+++ b/libgo/go/time/zoneinfo.go
@@ -4,7 +4,10 @@
package time
-import "sync"
+import (
+ "sync"
+ "syscall"
+)
// A Location maps time instants to the zone in use at that time.
// Typically, the Location represents the collection of time offsets
@@ -168,10 +171,7 @@ func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
// 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.
+var zoneinfo, _ = syscall.Getenv("ZONEINFO")
// LoadLocation returns the Location with the given name.
//
@@ -180,6 +180,13 @@ func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
//
// 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".
+//
+// The time zone database needed by LoadLocation may not be
+// present on all systems, especially non-Unix systems.
+// LoadLocation looks in the directory or uncompressed zip file
+// named by the ZONEINFO environment variable, if any, then looks in
+// known installation locations on Unix systems,
+// and finally looks in $GOROOT/lib/time/zoneinfo.zip.
func LoadLocation(name string) (*Location, error) {
if name == "" || name == "UTC" {
return UTC, nil
@@ -187,5 +194,11 @@ func LoadLocation(name string) (*Location, error) {
if name == "Local" {
return Local, nil
}
+ if zoneinfo != "" {
+ if z, err := loadZoneFile(zoneinfo, name); err == nil {
+ z.name = name
+ return z, nil
+ }
+ }
return loadLocation(name)
}
diff --git a/libgo/go/time/zoneinfo_plan9.go b/libgo/go/time/zoneinfo_plan9.go
index 9c052d42cd3..6855238dc84 100644
--- a/libgo/go/time/zoneinfo_plan9.go
+++ b/libgo/go/time/zoneinfo_plan9.go
@@ -8,11 +8,10 @@ package time
import (
"errors"
+ "runtime"
"syscall"
)
-var badData = errors.New("malformed time zone information")
-
func isSpace(r rune) bool {
return r == ' ' || r == '\t' || r == '\n'
}
@@ -51,7 +50,7 @@ func fields(s string) []string {
return a
}
-func loadZoneData(s string) (l *Location, err error) {
+func loadZoneDataPlan9(s string) (l *Location, err error) {
f := fields(s)
if len(f) < 4 {
if len(f) == 2 && f[0] == "GMT" {
@@ -112,33 +111,32 @@ func loadZoneData(s string) (l *Location, err error) {
return l, nil
}
-func loadZoneFile(name string) (*Location, error) {
+func loadZoneFilePlan9(name string) (*Location, error) {
b, err := readFile(name)
if err != nil {
return nil, err
}
- return loadZoneData(string(b))
+ return loadZoneDataPlan9(string(b))
}
func initTestingZone() {
- if z, err := loadZoneFile("/adm/timezone/US_Pacific"); err == nil {
- localLoc = *z
- return
+ z, err := loadLocation("America/Los_Angeles")
+ if err != nil {
+ panic("cannot load America/Los_Angeles for testing: " + err.Error())
}
-
- // Fall back to UTC.
- localLoc.name = "UTC"
+ z.name = "Local"
+ localLoc = *z
}
func initLocal() {
t, ok := syscall.Getenv("timezone")
if ok {
- if z, err := loadZoneData(t); err == nil {
+ if z, err := loadZoneDataPlan9(t); err == nil {
localLoc = *z
return
}
} else {
- if z, err := loadZoneFile("/adm/timezone/local"); err == nil {
+ if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
localLoc = *z
localLoc.name = "Local"
return
@@ -150,7 +148,8 @@ func initLocal() {
}
func loadLocation(name string) (*Location, error) {
- if z, err := loadZoneFile("/adm/timezone/" + name); err == nil {
+ if z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil {
+ z.name = name
return z, nil
}
return nil, errors.New("unknown time zone " + name)
diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go
new file mode 100644
index 00000000000..ebb4205a98f
--- /dev/null
+++ b/libgo/go/time/zoneinfo_read.go
@@ -0,0 +1,341 @@
+// Copyright 2009 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.
+
+// Parse "zoneinfo" time zone file.
+// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
+// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
+// and ftp://munnari.oz.au/pub/oldtz/
+
+package time
+
+import "errors"
+
+const (
+ headerSize = 4 + 16 + 4*7
+)
+
+// Simple I/O interface to binary blob of data.
+type data struct {
+ p []byte
+ error bool
+}
+
+func (d *data) read(n int) []byte {
+ if len(d.p) < n {
+ d.p = nil
+ d.error = true
+ return nil
+ }
+ p := d.p[0:n]
+ d.p = d.p[n:]
+ return p
+}
+
+func (d *data) big4() (n uint32, ok bool) {
+ p := d.read(4)
+ if len(p) < 4 {
+ d.error = true
+ return 0, false
+ }
+ return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
+}
+
+func (d *data) byte() (n byte, ok bool) {
+ p := d.read(1)
+ if len(p) < 1 {
+ d.error = true
+ return 0, false
+ }
+ return p[0], true
+}
+
+// Make a string by stopping at the first NUL
+func byteString(p []byte) string {
+ for i := 0; i < len(p); i++ {
+ if p[i] == 0 {
+ return string(p[0:i])
+ }
+ }
+ return string(p)
+}
+
+var badData = errors.New("malformed time zone information")
+
+func loadZoneData(bytes []byte) (l *Location, err error) {
+ d := data{bytes, false}
+
+ // 4-byte magic "TZif"
+ if magic := d.read(4); string(magic) != "TZif" {
+ return nil, badData
+ }
+
+ // 1-byte version, then 15 bytes of padding
+ var p []byte
+ if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
+ return nil, badData
+ }
+
+ // six big-endian 32-bit integers:
+ // number of UTC/local indicators
+ // number of standard/wall indicators
+ // number of leap seconds
+ // number of transition times
+ // number of local time zones
+ // number of characters of time zone abbrev strings
+ const (
+ NUTCLocal = iota
+ NStdWall
+ NLeap
+ NTime
+ NZone
+ NChar
+ )
+ var n [6]int
+ for i := 0; i < 6; i++ {
+ nn, ok := d.big4()
+ if !ok {
+ return nil, badData
+ }
+ n[i] = int(nn)
+ }
+
+ // Transition times.
+ txtimes := data{d.read(n[NTime] * 4), false}
+
+ // Time zone indices for transition times.
+ txzones := d.read(n[NTime])
+
+ // Zone info structures
+ zonedata := data{d.read(n[NZone] * 6), false}
+
+ // Time zone abbreviations.
+ abbrev := d.read(n[NChar])
+
+ // Leap-second time pairs
+ d.read(n[NLeap] * 8)
+
+ // Whether tx times associated with local time types
+ // are specified as standard time or wall time.
+ isstd := d.read(n[NStdWall])
+
+ // Whether tx times associated with local time types
+ // are specified as UTC or local time.
+ isutc := d.read(n[NUTCLocal])
+
+ if d.error { // ran out of data
+ return nil, badData
+ }
+
+ // If version == 2, the entire file repeats, this time using
+ // 8-byte ints for txtimes and leap seconds.
+ // We won't need those until 2106.
+
+ // Now we can build up a useful data structure.
+ // First the zone information.
+ // utcoff[4] isdst[1] nameindex[1]
+ zone := make([]zone, n[NZone])
+ for i := range zone {
+ var ok bool
+ var n uint32
+ if n, ok = zonedata.big4(); !ok {
+ return nil, badData
+ }
+ zone[i].offset = int(n)
+ var b byte
+ if b, ok = zonedata.byte(); !ok {
+ return nil, badData
+ }
+ zone[i].isDST = b != 0
+ if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
+ return nil, badData
+ }
+ zone[i].name = byteString(abbrev[b:])
+ }
+
+ // Now the transition time info.
+ tx := make([]zoneTrans, n[NTime])
+ for i := range tx {
+ var ok bool
+ var n uint32
+ if n, ok = txtimes.big4(); !ok {
+ return nil, badData
+ }
+ tx[i].when = int64(int32(n))
+ if int(txzones[i]) >= len(zone) {
+ return nil, badData
+ }
+ tx[i].index = txzones[i]
+ if i < len(isstd) {
+ tx[i].isstd = isstd[i] != 0
+ }
+ if i < len(isutc) {
+ tx[i].isutc = isutc[i] != 0
+ }
+ }
+
+ // Commited to succeed.
+ l = &Location{zone: zone, tx: tx}
+
+ // Fill in the cache with information about right now,
+ // since that will be the most common lookup.
+ 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]
+ }
+ }
+
+ return l, nil
+}
+
+func loadZoneFile(dir, name string) (l *Location, err error) {
+ if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
+ return loadZoneZip(dir, name)
+ }
+ if dir != "" {
+ name = dir + "/" + name
+ }
+ buf, err := readFile(name)
+ if err != nil {
+ return
+ }
+ return loadZoneData(buf)
+}
+
+// There are 500+ zoneinfo files. Rather than distribute them all
+// individually, we ship them in an uncompressed zip file.
+// Used this way, the zip file format serves as a commonly readable
+// container for the individual small files. We choose zip over tar
+// because zip files have a contiguous table of contents, making
+// individual file lookups faster, and because the per-file overhead
+// in a zip file is considerably less than tar's 512 bytes.
+
+// get4 returns the little-endian 32-bit value in b.
+func get4(b []byte) int {
+ if len(b) < 4 {
+ return 0
+ }
+ return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
+}
+
+// get2 returns the little-endian 16-bit value in b.
+func get2(b []byte) int {
+ if len(b) < 2 {
+ return 0
+ }
+ return int(b[0]) | int(b[1])<<8
+}
+
+func loadZoneZip(zipfile, name string) (l *Location, err error) {
+ fd, err := open(zipfile)
+ if err != nil {
+ return nil, errors.New("open " + zipfile + ": " + err.Error())
+ }
+ defer closefd(fd)
+
+ const (
+ zecheader = 0x06054b50
+ zcheader = 0x02014b50
+ ztailsize = 22
+
+ zheadersize = 30
+ zheader = 0x04034b50
+ )
+
+ buf := make([]byte, ztailsize)
+ if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+ n := get2(buf[10:])
+ size := get4(buf[12:])
+ off := get4(buf[16:])
+
+ buf = make([]byte, size)
+ if err := preadn(fd, buf, off); err != nil {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+
+ for i := 0; i < n; i++ {
+ // zip entry layout:
+ // 0 magic[4]
+ // 4 madevers[1]
+ // 5 madeos[1]
+ // 6 extvers[1]
+ // 7 extos[1]
+ // 8 flags[2]
+ // 10 meth[2]
+ // 12 modtime[2]
+ // 14 moddate[2]
+ // 16 crc[4]
+ // 20 csize[4]
+ // 24 uncsize[4]
+ // 28 namelen[2]
+ // 30 xlen[2]
+ // 32 fclen[2]
+ // 34 disknum[2]
+ // 36 iattr[2]
+ // 38 eattr[4]
+ // 42 off[4]
+ // 46 name[namelen]
+ // 46+namelen+xlen+fclen - next header
+ //
+ if get4(buf) != zcheader {
+ break
+ }
+ meth := get2(buf[10:])
+ size := get4(buf[24:])
+ namelen := get2(buf[28:])
+ xlen := get2(buf[30:])
+ fclen := get2(buf[32:])
+ off := get4(buf[42:])
+ zname := buf[46 : 46+namelen]
+ buf = buf[46+namelen+xlen+fclen:]
+ if string(zname) != name {
+ continue
+ }
+ if meth != 0 {
+ return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
+ }
+
+ // zip per-file header layout:
+ // 0 magic[4]
+ // 4 extvers[1]
+ // 5 extos[1]
+ // 6 flags[2]
+ // 8 meth[2]
+ // 10 modtime[2]
+ // 12 moddate[2]
+ // 14 crc[4]
+ // 18 csize[4]
+ // 22 uncsize[4]
+ // 26 namelen[2]
+ // 28 xlen[2]
+ // 30 name[namelen]
+ // 30+namelen+xlen - file data
+ //
+ buf = make([]byte, zheadersize+namelen)
+ if err := preadn(fd, buf, off); err != nil ||
+ get4(buf) != zheader ||
+ get2(buf[8:]) != meth ||
+ get2(buf[26:]) != namelen ||
+ string(buf[30:30+namelen]) != name {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+ xlen = get2(buf[28:])
+
+ buf = make([]byte, size)
+ if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+
+ return loadZoneData(buf)
+ }
+
+ return nil, errors.New("cannot find " + name + " in zip file " + zipfile)
+}
diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go
index 540b653c57d..1bf1f11e24f 100644
--- a/libgo/go/time/zoneinfo_unix.go
+++ b/libgo/go/time/zoneinfo_unix.go
@@ -13,200 +13,10 @@ package time
import (
"errors"
+ "runtime"
"syscall"
)
-const (
- headerSize = 4 + 16 + 4*7
-)
-
-// Simple I/O interface to binary blob of data.
-type data struct {
- p []byte
- error bool
-}
-
-func (d *data) read(n int) []byte {
- if len(d.p) < n {
- d.p = nil
- d.error = true
- return nil
- }
- p := d.p[0:n]
- d.p = d.p[n:]
- return p
-}
-
-func (d *data) big4() (n uint32, ok bool) {
- p := d.read(4)
- if len(p) < 4 {
- d.error = true
- return 0, false
- }
- return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
-}
-
-func (d *data) byte() (n byte, ok bool) {
- p := d.read(1)
- if len(p) < 1 {
- d.error = true
- return 0, false
- }
- return p[0], true
-}
-
-// Make a string by stopping at the first NUL
-func byteString(p []byte) string {
- for i := 0; i < len(p); i++ {
- if p[i] == 0 {
- return string(p[0:i])
- }
- }
- return string(p)
-}
-
-var badData = errors.New("malformed time zone information")
-
-func loadZoneData(bytes []byte) (l *Location, err error) {
- d := data{bytes, false}
-
- // 4-byte magic "TZif"
- if magic := d.read(4); string(magic) != "TZif" {
- return nil, badData
- }
-
- // 1-byte version, then 15 bytes of padding
- var p []byte
- if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
- return nil, badData
- }
-
- // six big-endian 32-bit integers:
- // number of UTC/local indicators
- // number of standard/wall indicators
- // number of leap seconds
- // number of transition times
- // number of local time zones
- // number of characters of time zone abbrev strings
- const (
- NUTCLocal = iota
- NStdWall
- NLeap
- NTime
- NZone
- NChar
- )
- var n [6]int
- for i := 0; i < 6; i++ {
- nn, ok := d.big4()
- if !ok {
- return nil, badData
- }
- n[i] = int(nn)
- }
-
- // Transition times.
- txtimes := data{d.read(n[NTime] * 4), false}
-
- // Time zone indices for transition times.
- txzones := d.read(n[NTime])
-
- // Zone info structures
- zonedata := data{d.read(n[NZone] * 6), false}
-
- // Time zone abbreviations.
- abbrev := d.read(n[NChar])
-
- // Leap-second time pairs
- d.read(n[NLeap] * 8)
-
- // Whether tx times associated with local time types
- // are specified as standard time or wall time.
- isstd := d.read(n[NStdWall])
-
- // Whether tx times associated with local time types
- // are specified as UTC or local time.
- isutc := d.read(n[NUTCLocal])
-
- if d.error { // ran out of data
- return nil, badData
- }
-
- // If version == 2, the entire file repeats, this time using
- // 8-byte ints for txtimes and leap seconds.
- // We won't need those until 2106.
-
- // Now we can build up a useful data structure.
- // First the zone information.
- // utcoff[4] isdst[1] nameindex[1]
- zone := make([]zone, n[NZone])
- for i := range zone {
- var ok bool
- var n uint32
- if n, ok = zonedata.big4(); !ok {
- return nil, badData
- }
- zone[i].offset = int(n)
- var b byte
- if b, ok = zonedata.byte(); !ok {
- return nil, badData
- }
- zone[i].isDST = b != 0
- if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
- return nil, badData
- }
- zone[i].name = byteString(abbrev[b:])
- }
-
- // Now the transition time info.
- tx := make([]zoneTrans, n[NTime])
- for i := range tx {
- var ok bool
- var n uint32
- if n, ok = txtimes.big4(); !ok {
- return nil, badData
- }
- tx[i].when = int64(int32(n))
- if int(txzones[i]) >= len(zone) {
- return nil, badData
- }
- tx[i].index = txzones[i]
- if i < len(isstd) {
- tx[i].isstd = isstd[i] != 0
- }
- if i < len(isutc) {
- tx[i].isutc = isutc[i] != 0
- }
- }
-
- // Commited to succeed.
- l = &Location{zone: zone, tx: tx}
-
- // Fill in the cache with information about right now,
- // since that will be the most common lookup.
- 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]
- }
- }
-
- return l, nil
-}
-
-func loadZoneFile(name string) (l *Location, err error) {
- buf, err := readFile(name)
- if err != nil {
- return
- }
- return loadZoneData(buf)
-}
-
func initTestingZone() {
syscall.Setenv("TZ", "America/Los_Angeles")
initLocal()
@@ -218,6 +28,7 @@ var zoneDirs = []string{
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
+ runtime.GOROOT() + "/lib/time/zoneinfo/",
}
func initLocal() {
@@ -229,7 +40,7 @@ func initLocal() {
tz, ok := syscall.Getenv("TZ")
switch {
case !ok:
- z, err := loadZoneFile("/etc/localtime")
+ z, err := loadZoneFile("", "/etc/localtime")
if err == nil {
localLoc = *z
localLoc.name = "Local"
@@ -248,7 +59,7 @@ func initLocal() {
func loadLocation(name string) (*Location, error) {
for _, zoneDir := range zoneDirs {
- if z, err := loadZoneFile(zoneDir + name); err == nil {
+ if z, err := loadZoneFile(zoneDir, name); err == nil {
z.name = name
return z, nil
}
diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go
index beef4de92b0..754e392deca 100644
--- a/libgo/go/time/zoneinfo_windows.go
+++ b/libgo/go/time/zoneinfo_windows.go
@@ -6,6 +6,7 @@ package time
import (
"errors"
+ "runtime"
"syscall"
)
@@ -151,7 +152,10 @@ func initLocal() {
initLocalFromTZI(&i)
}
-// TODO(rsc): Implement.
func loadLocation(name string) (*Location, error) {
+ if z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name); err == nil {
+ z.name = name
+ return z, nil
+ }
return nil, errors.New("unknown time zone " + name)
}