summaryrefslogtreecommitdiff
path: root/libgo/go/time
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/time')
-rw-r--r--libgo/go/time/format.go618
-rw-r--r--libgo/go/time/sleep.go151
-rw-r--r--libgo/go/time/sleep_test.go134
-rw-r--r--libgo/go/time/tick.go171
-rw-r--r--libgo/go/time/tick_test.go45
-rw-r--r--libgo/go/time/time.go229
-rw-r--r--libgo/go/time/time_test.go341
-rw-r--r--libgo/go/time/zoneinfo_unix.go267
-rw-r--r--libgo/go/time/zoneinfo_windows.go192
9 files changed, 2148 insertions, 0 deletions
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go
new file mode 100644
index 000000000..7b5a8f3b6
--- /dev/null
+++ b/libgo/go/time/format.go
@@ -0,0 +1,618 @@
+package time
+
+import (
+ "bytes"
+ "os"
+ "strconv"
+)
+
+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.
+//
+// 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.
+//
+// Numeric time zone offsets format as follows:
+// -0700 ±hhmm
+// -07:00 ±hh:mm
+// Replacing the sign in the format with a Z triggers
+// the ISO 8601 behavior of printing Z instead of an
+// offset for the UTC zone. Thus:
+// 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"
+ // RFC822 with Zulu time.
+ RFC822Z = "02 Jan 06 1504 -0700"
+ RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
+ RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
+ RFC3339 = "2006-01-02T15:04:05Z07:00"
+ Kitchen = "3:04PM"
+)
+
+const (
+ stdLongMonth = "January"
+ stdMonth = "Jan"
+ stdNumMonth = "1"
+ stdZeroMonth = "01"
+ stdLongWeekDay = "Monday"
+ stdWeekDay = "Mon"
+ stdDay = "2"
+ stdUnderDay = "_2"
+ stdZeroDay = "02"
+ stdHour = "15"
+ stdHour12 = "3"
+ stdZeroHour12 = "03"
+ stdMinute = "4"
+ stdZeroMinute = "04"
+ stdSecond = "5"
+ stdZeroSecond = "05"
+ stdLongYear = "2006"
+ stdYear = "06"
+ stdPM = "PM"
+ stdpm = "pm"
+ stdTZ = "MST"
+ stdISO8601TZ = "Z0700" // prints Z for UTC
+ stdISO8601ColonTZ = "Z07:00" // prints Z for UTC
+ stdNumTZ = "-0700" // always numeric
+ stdNumShortTZ = "-07" // always numeric
+ stdNumColonTZ = "-07:00" // always numeric
+)
+
+// nextStdChunk finds the first occurrence of a std string in
+// layout and returns the text before, the std string, and the text after.
+func nextStdChunk(layout string) (prefix, std, suffix string) {
+ for i := 0; i < len(layout); i++ {
+ switch layout[i] {
+ case 'J': // January, Jan
+ if len(layout) >= i+7 && layout[i:i+7] == stdLongMonth {
+ return layout[0:i], stdLongMonth, layout[i+7:]
+ }
+ if len(layout) >= i+3 && layout[i:i+3] == stdMonth {
+ return layout[0:i], stdMonth, layout[i+3:]
+ }
+
+ case 'M': // Monday, Mon, MST
+ if len(layout) >= i+6 && layout[i:i+6] == stdLongWeekDay {
+ return layout[0:i], stdLongWeekDay, layout[i+6:]
+ }
+ if len(layout) >= i+3 {
+ if layout[i:i+3] == stdWeekDay {
+ return layout[0:i], stdWeekDay, layout[i+3:]
+ }
+ if layout[i:i+3] == stdTZ {
+ return layout[0:i], stdTZ, layout[i+3:]
+ }
+ }
+
+ case '0': // 01, 02, 03, 04, 05, 06
+ if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
+ return layout[0:i], layout[i : i+2], layout[i+2:]
+ }
+
+ case '1': // 15, 1
+ if len(layout) >= i+2 && layout[i+1] == '5' {
+ return layout[0:i], stdHour, layout[i+2:]
+ }
+ return layout[0:i], stdNumMonth, layout[i+1:]
+
+ case '2': // 2006, 2
+ if len(layout) >= i+4 && layout[i:i+4] == stdLongYear {
+ return layout[0:i], stdLongYear, layout[i+4:]
+ }
+ return layout[0:i], stdDay, layout[i+1:]
+
+ case '_': // _2
+ if len(layout) >= i+2 && layout[i+1] == '2' {
+ return layout[0:i], stdUnderDay, layout[i+2:]
+ }
+
+ case '3', '4', '5': // 3, 4, 5
+ return layout[0:i], layout[i : i+1], layout[i+1:]
+
+ case 'P': // PM
+ if len(layout) >= i+2 && layout[i+1] == 'M' {
+ return layout[0:i], layout[i : i+2], layout[i+2:]
+ }
+
+ case 'p': // pm
+ if len(layout) >= i+2 && layout[i+1] == 'm' {
+ return layout[0:i], layout[i : i+2], layout[i+2:]
+ }
+
+ case '-': // -0700, -07:00, -07
+ if len(layout) >= i+5 && layout[i:i+5] == stdNumTZ {
+ return layout[0:i], layout[i : i+5], layout[i+5:]
+ }
+ if len(layout) >= i+6 && layout[i:i+6] == stdNumColonTZ {
+ return layout[0:i], layout[i : i+6], layout[i+6:]
+ }
+ if len(layout) >= i+3 && layout[i:i+3] == stdNumShortTZ {
+ return layout[0:i], layout[i : i+3], layout[i+3:]
+ }
+ case 'Z': // Z0700, Z07:00
+ if len(layout) >= i+5 && layout[i:i+5] == stdISO8601TZ {
+ return layout[0:i], layout[i : i+5], layout[i+5:]
+ }
+ if len(layout) >= i+6 && layout[i:i+6] == stdISO8601ColonTZ {
+ return layout[0:i], layout[i : i+6], layout[i+6:]
+ }
+ }
+ }
+ return layout, "", ""
+}
+
+var longDayNames = []string{
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+}
+
+var shortDayNames = []string{
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+}
+
+var shortMonthNames = []string{
+ "---",
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+}
+
+var longMonthNames = []string{
+ "---",
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+}
+
+func lookup(tab []string, val string) (int, string, os.Error) {
+ for i, v := range tab {
+ if len(val) >= len(v) && val[0:len(v)] == v {
+ return i, val[len(v):], nil
+ }
+ }
+ return -1, val, errBad
+}
+
+func pad(i int, padding string) string {
+ s := strconv.Itoa(i)
+ if i < 10 {
+ s = padding + s
+ }
+ return s
+}
+
+func zeroPad(i int) string { return pad(i, "0") }
+
+// 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.
+func (t *Time) Format(layout string) string {
+ b := new(bytes.Buffer)
+ // Each iteration generates one std value.
+ for {
+ prefix, std, suffix := nextStdChunk(layout)
+ b.WriteString(prefix)
+ if std == "" {
+ break
+ }
+ var p string
+ switch std {
+ case stdYear:
+ p = strconv.Itoa64(t.Year % 100)
+ case stdLongYear:
+ p = strconv.Itoa64(t.Year)
+ case stdMonth:
+ p = shortMonthNames[t.Month]
+ case stdLongMonth:
+ p = longMonthNames[t.Month]
+ case stdNumMonth:
+ p = strconv.Itoa(t.Month)
+ case stdZeroMonth:
+ p = zeroPad(t.Month)
+ case stdWeekDay:
+ p = shortDayNames[t.Weekday]
+ case stdLongWeekDay:
+ p = longDayNames[t.Weekday]
+ case stdDay:
+ p = strconv.Itoa(t.Day)
+ case stdUnderDay:
+ p = pad(t.Day, " ")
+ case stdZeroDay:
+ p = zeroPad(t.Day)
+ case stdHour:
+ p = zeroPad(t.Hour)
+ case stdHour12:
+ p = strconv.Itoa(t.Hour % 12)
+ case stdZeroHour12:
+ p = zeroPad(t.Hour % 12)
+ case stdMinute:
+ p = strconv.Itoa(t.Minute)
+ case stdZeroMinute:
+ p = zeroPad(t.Minute)
+ case stdSecond:
+ p = strconv.Itoa(t.Second)
+ case stdZeroSecond:
+ p = zeroPad(t.Second)
+ case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ:
+ // Ugly special case. We cheat and take the "Z" variants
+ // to mean "the time zone as formatted for ISO 8601".
+ if t.ZoneOffset == 0 && std[0] == 'Z' {
+ p = "Z"
+ break
+ }
+ zone := t.ZoneOffset / 60 // convert to minutes
+ if zone < 0 {
+ p = "-"
+ zone = -zone
+ } else {
+ p = "+"
+ }
+ p += zeroPad(zone / 60)
+ if std == stdISO8601ColonTZ || std == stdNumColonTZ {
+ p += ":"
+ }
+ p += zeroPad(zone % 60)
+ case stdPM:
+ if t.Hour >= 12 {
+ p = "PM"
+ } else {
+ p = "AM"
+ }
+ case stdpm:
+ if t.Hour >= 12 {
+ p = "pm"
+ } else {
+ p = "am"
+ }
+ case stdTZ:
+ if t.Zone != "" {
+ p = t.Zone
+ } else {
+ // No time zone known for this time, but we must print one.
+ // Use the -0700 format.
+ zone := t.ZoneOffset / 60 // convert to minutes
+ if zone < 0 {
+ p = "-"
+ zone = -zone
+ } else {
+ p = "+"
+ }
+ p += zeroPad(zone / 60)
+ p += zeroPad(zone % 60)
+ }
+ }
+ b.WriteString(p)
+ layout = suffix
+ }
+ return b.String()
+}
+
+// String returns a Unix-style representation of the time value.
+func (t *Time) String() string {
+ if t == nil {
+ return "<nil>"
+ }
+ return t.Format(UnixDate)
+}
+
+var errBad = os.ErrorString("bad") // just a marker; not returned to user
+
+// ParseError describes a problem parsing a time string.
+type ParseError struct {
+ Layout string
+ Value string
+ LayoutElem string
+ ValueElem string
+ Message string
+}
+
+// String is the string representation of a ParseError.
+func (e *ParseError) String() string {
+ if e.Message == "" {
+ return "parsing time " +
+ strconv.Quote(e.Value) + " as " +
+ strconv.Quote(e.Layout) + ": cannot parse " +
+ strconv.Quote(e.ValueElem) + " as " +
+ strconv.Quote(e.LayoutElem)
+ }
+ return "parsing time " +
+ strconv.Quote(e.Value) + e.Message
+}
+
+// getnum parses s[0:1] or s[0:2] (fixed forces the latter)
+// as a decimal integer and returns the integer and the
+// remainder of the string.
+func getnum(s string, fixed bool) (int, string, os.Error) {
+ if len(s) == 0 || s[0] < '0' || s[0] > '9' {
+ return 0, s, errBad
+ }
+ if len(s) == 1 || s[1] < '0' || s[1] > '9' {
+ if fixed {
+ return 0, s, errBad
+ }
+ return int(s[0] - '0'), s[1:], nil
+ }
+ return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil
+}
+
+func cutspace(s string) string {
+ for len(s) > 0 && s[0] == ' ' {
+ s = s[1:]
+ }
+ return s
+}
+
+// skip removes the given prefix from value,
+// treating runs of space characters as equivalent.
+func skip(value, prefix string) (string, os.Error) {
+ for len(prefix) > 0 {
+ if prefix[0] == ' ' {
+ if len(value) > 0 && value[0] != ' ' {
+ return "", errBad
+ }
+ prefix = cutspace(prefix)
+ value = cutspace(value)
+ continue
+ }
+ if len(value) == 0 || value[0] != prefix[0] {
+ return "", errBad
+ }
+ prefix = prefix[1:]
+ value = value[1:]
+ }
+ return value, nil
+}
+
+// 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.
+//
+// Only those elements present in the value will be set in the returned time
+// structure. Also, if the input string represents an inconsistent time
+// (such as having the wrong day of the week), the returned value will also
+// be inconsistent. In any case, the elements of the returned time will be
+// sane: hours in 0..23, minutes in 0..59, day of month in 0..31, etc.
+// Years must be in the range 0000..9999.
+func Parse(alayout, avalue string) (*Time, os.Error) {
+ var t Time
+ rangeErrString := "" // set if a value is out of range
+ pmSet := false // do we need to add 12 to the hour?
+ layout, value := alayout, avalue
+ // Each iteration processes one std value.
+ for {
+ var err os.Error
+ prefix, std, suffix := nextStdChunk(layout)
+ value, err = skip(value, prefix)
+ if err != nil {
+ return nil, &ParseError{alayout, avalue, prefix, value, ""}
+ }
+ if len(std) == 0 {
+ if len(value) != 0 {
+ return nil, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
+ }
+ break
+ }
+ layout = suffix
+ var p string
+ switch std {
+ case stdYear:
+ if len(value) < 2 {
+ err = errBad
+ break
+ }
+ p, value = value[0:2], value[2:]
+ t.Year, err = strconv.Atoi64(p)
+ if t.Year >= 69 { // Unix time starts Dec 31 1969 in some time zones
+ t.Year += 1900
+ } else {
+ t.Year += 2000
+ }
+ case stdLongYear:
+ if len(value) < 4 || value[0] < '0' || value[0] > '9' {
+ err = errBad
+ break
+ }
+ p, value = value[0:4], value[4:]
+ t.Year, err = strconv.Atoi64(p)
+ case stdMonth:
+ t.Month, value, err = lookup(shortMonthNames, value)
+ case stdLongMonth:
+ t.Month, value, err = lookup(longMonthNames, value)
+ case stdNumMonth, stdZeroMonth:
+ t.Month, value, err = getnum(value, std == stdZeroMonth)
+ if t.Month <= 0 || 12 < t.Month {
+ rangeErrString = "month"
+ }
+ case stdWeekDay:
+ t.Weekday, value, err = lookup(shortDayNames, value)
+ case stdLongWeekDay:
+ t.Weekday, value, err = lookup(longDayNames, value)
+ case stdDay, stdUnderDay, stdZeroDay:
+ if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
+ value = value[1:]
+ }
+ t.Day, value, err = getnum(value, std == stdZeroDay)
+ if t.Day < 0 || 31 < t.Day {
+ // TODO: be more thorough in date check?
+ rangeErrString = "day"
+ }
+ case stdHour:
+ t.Hour, value, err = getnum(value, false)
+ if t.Hour < 0 || 24 <= t.Hour {
+ rangeErrString = "hour"
+ }
+ case stdHour12, stdZeroHour12:
+ t.Hour, value, err = getnum(value, std == stdZeroHour12)
+ if t.Hour < 0 || 12 < t.Hour {
+ rangeErrString = "hour"
+ }
+ case stdMinute, stdZeroMinute:
+ t.Minute, value, err = getnum(value, std == stdZeroMinute)
+ if t.Minute < 0 || 60 <= t.Minute {
+ rangeErrString = "minute"
+ }
+ case stdSecond, stdZeroSecond:
+ t.Second, value, err = getnum(value, std == stdZeroSecond)
+ if t.Second < 0 || 60 <= t.Second {
+ rangeErrString = "second"
+ }
+ case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ:
+ if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' {
+ value = value[1:]
+ t.Zone = "UTC"
+ break
+ }
+ var sign, hh, mm string
+ if std == stdISO8601ColonTZ || std == stdNumColonTZ {
+ if len(value) < 6 {
+ err = errBad
+ break
+ }
+ if value[3] != ':' {
+ err = errBad
+ break
+ }
+ sign, hh, mm, value = value[0:1], value[1:3], value[4:6], value[6:]
+ } else if std == stdNumShortTZ {
+ if len(value) < 3 {
+ err = errBad
+ break
+ }
+ sign, hh, mm, value = value[0:1], value[1:3], "00", value[3:]
+ } else {
+ if len(value) < 5 {
+ err = errBad
+ break
+ }
+ sign, hh, mm, value = value[0:1], value[1:3], value[3:5], value[5:]
+ }
+ var hr, min int
+ hr, err = strconv.Atoi(hh)
+ if err == nil {
+ min, err = strconv.Atoi(mm)
+ }
+ t.ZoneOffset = (hr*60 + min) * 60 // offset is in seconds
+ switch sign[0] {
+ case '+':
+ case '-':
+ t.ZoneOffset = -t.ZoneOffset
+ default:
+ err = errBad
+ }
+ case stdPM:
+ if len(value) < 2 {
+ err = errBad
+ break
+ }
+ p, value = value[0:2], value[2:]
+ if p == "PM" {
+ pmSet = true
+ } else if p != "AM" {
+ err = errBad
+ }
+ case stdpm:
+ if len(value) < 2 {
+ err = errBad
+ break
+ }
+ p, value = value[0:2], value[2:]
+ if p == "pm" {
+ pmSet = true
+ } else if p != "am" {
+ err = errBad
+ }
+ case stdTZ:
+ // Does it look like a time zone?
+ if len(value) >= 3 && value[0:3] == "UTC" {
+ t.Zone, value = value[0:3], value[3:]
+ break
+ }
+
+ if len(value) >= 3 && value[2] == 'T' {
+ p, value = value[0:3], value[3:]
+ } else if len(value) >= 4 && value[3] == 'T' {
+ p, value = value[0:4], value[4:]
+ } else {
+ err = errBad
+ break
+ }
+ for i := 0; i < len(p); i++ {
+ if p[i] < 'A' || 'Z' < p[i] {
+ err = errBad
+ }
+ }
+ if err != nil {
+ break
+ }
+ // It's a valid format.
+ t.Zone = p
+ // Can we find its offset?
+ if offset, found := lookupByName(p); found {
+ t.ZoneOffset = offset
+ }
+ }
+ if rangeErrString != "" {
+ return nil, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"}
+ }
+ if err != nil {
+ return nil, &ParseError{alayout, avalue, std, value, ""}
+ }
+ }
+ if pmSet && t.Hour < 12 {
+ t.Hour += 12
+ }
+ return &t, nil
+}
diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go
new file mode 100644
index 000000000..3538775ad
--- /dev/null
+++ b/libgo/go/time/sleep.go
@@ -0,0 +1,151 @@
+// 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.
+
+package time
+
+import (
+ "os"
+ "syscall"
+ "sync"
+ "container/heap"
+)
+
+// The event type represents a single After or AfterFunc event.
+type event struct {
+ t int64 // The absolute time that the event should fire.
+ f func(int64) // The function to call when the event fires.
+ sleeping bool // A sleeper is sleeping for this event.
+}
+
+type eventHeap []*event
+
+var events eventHeap
+var eventMutex sync.Mutex
+
+func init() {
+ events.Push(&event{1 << 62, nil, true}) // sentinel
+}
+
+// Sleep pauses the current goroutine for at least ns nanoseconds.
+// Higher resolution sleeping may be provided by syscall.Nanosleep
+// on some operating systems.
+func Sleep(ns int64) os.Error {
+ _, err := sleep(Nanoseconds(), ns)
+ return err
+}
+
+// sleep takes the current time and a duration,
+// pauses for at least ns nanoseconds, and
+// returns the current time and an error.
+func sleep(t, ns int64) (int64, os.Error) {
+ // TODO(cw): use monotonic-time once it's available
+ end := t + ns
+ for t < end {
+ errno := syscall.Sleep(end - t)
+ if errno != 0 && errno != syscall.EINTR {
+ return 0, os.NewSyscallError("sleep", errno)
+ }
+ t = Nanoseconds()
+ }
+ return t, nil
+}
+
+// After waits at least ns nanoseconds before sending the current time
+// on the returned channel.
+func After(ns int64) <-chan int64 {
+ c := make(chan int64, 1)
+ after(ns, func(t int64) { c <- t })
+ return c
+}
+
+// AfterFunc waits at least ns nanoseconds before calling f
+// in its own goroutine.
+func AfterFunc(ns int64, f func()) {
+ after(ns, func(_ int64) {
+ go f()
+ })
+}
+
+// after is the implementation of After and AfterFunc.
+// When the current time is after ns, it calls f with the current time.
+// It assumes that f will not block.
+func after(ns int64, f func(int64)) {
+ t := Nanoseconds() + ns
+ eventMutex.Lock()
+ t0 := events[0].t
+ heap.Push(events, &event{t, f, false})
+ if t < t0 {
+ go sleeper()
+ }
+ eventMutex.Unlock()
+}
+
+// sleeper continually looks at the earliest event in the queue, marks it
+// as sleeping, waits until it happens, then removes any events
+// in the queue that are due. It stops when it finds an event that is
+// already marked as sleeping. When an event is inserted before the first item,
+// a new sleeper is started.
+//
+// Scheduling vagaries mean that sleepers may not wake up in
+// exactly the order of the events that they are waiting for,
+// but this does not matter as long as there are at least as
+// many sleepers as events marked sleeping (invariant). This ensures that
+// there is always a sleeper to service the remaining events.
+//
+// A sleeper will remove at least the event it has been waiting for
+// unless the event has already been removed by another sleeper. Both
+// cases preserve the invariant described above.
+func sleeper() {
+ eventMutex.Lock()
+ e := events[0]
+ for !e.sleeping {
+ t := Nanoseconds()
+ if dt := e.t - t; dt > 0 {
+ e.sleeping = true
+ eventMutex.Unlock()
+ if nt, err := sleep(t, dt); err != nil {
+ // If sleep has encountered an error,
+ // there's not much we can do. We pretend
+ // that time really has advanced by the required
+ // amount and lie to the rest of the system.
+ t = e.t
+ } else {
+ t = nt
+ }
+ eventMutex.Lock()
+ e = events[0]
+ }
+ for t >= e.t {
+ e.f(t)
+ heap.Pop(events)
+ e = events[0]
+ }
+ }
+ eventMutex.Unlock()
+}
+
+func (eventHeap) Len() int {
+ return len(events)
+}
+
+func (eventHeap) Less(i, j int) bool {
+ return events[i].t < events[j].t
+}
+
+func (eventHeap) Swap(i, j int) {
+ events[i], events[j] = events[j], events[i]
+}
+
+func (eventHeap) Push(x interface{}) {
+ events = append(events, x.(*event))
+}
+
+func (eventHeap) Pop() interface{} {
+ // TODO: possibly shrink array.
+ n := len(events) - 1
+ e := events[n]
+ events[n] = nil
+ events = events[0:n]
+ return e
+}
diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go
new file mode 100644
index 000000000..9e36288f8
--- /dev/null
+++ b/libgo/go/time/sleep_test.go
@@ -0,0 +1,134 @@
+// 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.
+
+package time_test
+
+import (
+ "os"
+ "syscall"
+ "testing"
+ "sort"
+ . "time"
+)
+
+func TestSleep(t *testing.T) {
+ const delay = int64(100e6)
+ go func() {
+ Sleep(delay / 2)
+ syscall.Kill(os.Getpid(), syscall.SIGCHLD)
+ }()
+ start := Nanoseconds()
+ Sleep(delay)
+ duration := Nanoseconds() - start
+ if duration < delay {
+ t.Fatalf("Sleep(%d) slept for only %d ns", delay, duration)
+ }
+}
+
+// Test the basic function calling behavior. Correct queueing
+// behavior is tested elsewhere, since After and AfterFunc share
+// the same code.
+func TestAfterFunc(t *testing.T) {
+ i := 10
+ c := make(chan bool)
+ var f func()
+ f = func() {
+ i--
+ if i >= 0 {
+ AfterFunc(0, f)
+ Sleep(1e9)
+ } else {
+ c <- true
+ }
+ }
+
+ AfterFunc(0, f)
+ <-c
+}
+
+func BenchmarkAfterFunc(b *testing.B) {
+ i := b.N
+ c := make(chan bool)
+ var f func()
+ f = func() {
+ i--
+ if i >= 0 {
+ AfterFunc(0, f)
+ } else {
+ c <- true
+ }
+ }
+
+ AfterFunc(0, f)
+ <-c
+}
+
+func TestAfter(t *testing.T) {
+ const delay = int64(100e6)
+ start := Nanoseconds()
+ end := <-After(delay)
+ if duration := Nanoseconds() - start; duration < delay {
+ t.Fatalf("After(%d) slept for only %d ns", delay, duration)
+ }
+ if min := start + delay; end < min {
+ t.Fatalf("After(%d) expect >= %d, got %d", delay, min, end)
+ }
+}
+
+func TestAfterTick(t *testing.T) {
+ const (
+ Delta = 100 * 1e6
+ Count = 10
+ )
+ t0 := Nanoseconds()
+ for i := 0; i < Count; i++ {
+ <-After(Delta)
+ }
+ t1 := Nanoseconds()
+ ns := t1 - t0
+ target := int64(Delta * Count)
+ slop := target * 2 / 10
+ if ns < target-slop || ns > target+slop {
+ t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target))
+ }
+}
+
+var slots = []int{5, 3, 6, 6, 6, 1, 1, 2, 7, 9, 4, 8, 0}
+
+type afterResult struct {
+ slot int
+ t int64
+}
+
+func await(slot int, result chan<- afterResult, ac <-chan int64) {
+ result <- afterResult{slot, <-ac}
+}
+
+func TestAfterQueuing(t *testing.T) {
+ const (
+ Delta = 100 * 1e6
+ )
+ // make the result channel buffered because we don't want
+ // to depend on channel queueing semantics that might
+ // possibly change in the future.
+ result := make(chan afterResult, len(slots))
+
+ t0 := Nanoseconds()
+ for _, slot := range slots {
+ go await(slot, result, After(int64(slot)*Delta))
+ }
+ sort.SortInts(slots)
+ for _, slot := range slots {
+ r := <-result
+ if r.slot != slot {
+ t.Fatalf("after queue got slot %d, expected %d", r.slot, slot)
+ }
+ ns := r.t - t0
+ target := int64(slot * Delta)
+ slop := int64(Delta) / 4
+ if ns < target-slop || ns > target+slop {
+ t.Fatalf("after queue slot %d arrived at %g, expected [%g,%g]", slot, float64(ns), float64(target-slop), float64(target+slop))
+ }
+ }
+}
diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go
new file mode 100644
index 000000000..ddd727270
--- /dev/null
+++ b/libgo/go/time/tick.go
@@ -0,0 +1,171 @@
+// 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.
+
+package time
+
+import (
+ "os"
+ "sync"
+)
+
+// A Ticker holds a synchronous channel that delivers `ticks' of a clock
+// at intervals.
+type Ticker struct {
+ C <-chan int64 // The channel on which the ticks are delivered.
+ c chan<- int64 // The same channel, but the end we use.
+ ns int64
+ shutdown chan bool // Buffered channel used to signal shutdown.
+ nextTick int64
+ next *Ticker
+}
+
+// Stop turns off a ticker. After Stop, no more ticks will be sent.
+func (t *Ticker) Stop() {
+ // Make it non-blocking so multiple Stops don't block.
+ _ = t.shutdown <- true
+}
+
+// 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.
+func Tick(ns int64) <-chan int64 {
+ if ns <= 0 {
+ return nil
+ }
+ return NewTicker(ns).C
+}
+
+type alarmer struct {
+ wakeUp chan bool // wakeup signals sent/received here
+ wakeMeAt chan int64
+ wakeTime int64
+}
+
+// Set alarm to go off at time ns, if not already set earlier.
+func (a *alarmer) set(ns int64) {
+ switch {
+ case a.wakeTime > ns:
+ // Next tick we expect is too late; shut down the late runner
+ // and (after fallthrough) start a new wakeLoop.
+ close(a.wakeMeAt)
+ fallthrough
+ case a.wakeMeAt == nil:
+ // There's no wakeLoop, start one.
+ a.wakeMeAt = make(chan int64)
+ a.wakeUp = make(chan bool, 1)
+ go wakeLoop(a.wakeMeAt, a.wakeUp)
+ fallthrough
+ case a.wakeTime == 0:
+ // Nobody else is waiting; it's just us.
+ a.wakeTime = ns
+ a.wakeMeAt <- ns
+ default:
+ // There's already someone scheduled.
+ }
+}
+
+// Channel to notify tickerLoop of new Tickers being created.
+var newTicker chan *Ticker
+
+func startTickerLoop() {
+ newTicker = make(chan *Ticker)
+ go tickerLoop()
+}
+
+// wakeLoop delivers ticks at scheduled times, sleeping until the right moment.
+// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new
+// wakeLoop and signal that this one is done by closing the wakeMeAt channel.
+func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) {
+ for wakeAt := range wakeMeAt {
+ Sleep(wakeAt - Nanoseconds())
+ wakeUp <- true
+ }
+}
+
+// A single tickerLoop serves all ticks to Tickers. It waits for two events:
+// either the creation of a new Ticker or a tick from the alarm,
+// signalling a time to wake up one or more Tickers.
+func tickerLoop() {
+ // Represents the next alarm to be delivered.
+ var alarm alarmer
+ var now, wakeTime int64
+ var tickers *Ticker
+ for {
+ select {
+ case t := <-newTicker:
+ // Add Ticker to list
+ t.next = tickers
+ tickers = t
+ // Arrange for a new alarm if this one precedes the existing one.
+ alarm.set(t.nextTick)
+ case <-alarm.wakeUp:
+ now = Nanoseconds()
+ wakeTime = now + 1e15 // very long in the future
+ var prev *Ticker = nil
+ // Scan list of tickers, delivering updates to those
+ // that need it and determining the next wake time.
+ // TODO(r): list should be sorted in time order.
+ for t := tickers; t != nil; t = t.next {
+ if _, ok := <-t.shutdown; ok {
+ // Ticker is done; remove it from list.
+ if prev == nil {
+ tickers = t.next
+ } else {
+ prev.next = t.next
+ }
+ continue
+ }
+ if t.nextTick <= now {
+ if len(t.c) == 0 {
+ // Only send if there's room. We must not block.
+ // The channel is allocated with a one-element
+ // buffer, which is sufficient: if he hasn't picked
+ // up the last tick, no point in sending more.
+ t.c <- now
+ }
+ t.nextTick += t.ns
+ if t.nextTick <= now {
+ // Still behind; advance in one big step.
+ t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns
+ }
+ }
+ if t.nextTick < wakeTime {
+ wakeTime = t.nextTick
+ }
+ prev = t
+ }
+ if tickers != nil {
+ // Please send wakeup at earliest required time.
+ // If there are no tickers, don't bother.
+ alarm.wakeTime = wakeTime
+ alarm.wakeMeAt <- wakeTime
+ } else {
+ alarm.wakeTime = 0
+ }
+ }
+ }
+}
+
+var onceStartTickerLoop sync.Once
+
+// NewTicker returns a new Ticker containing a channel that will
+// send the time, in nanoseconds, every ns nanoseconds. It adjusts the
+// intervals to make up for pauses in delivery of the ticks. The value of
+// ns must be greater than zero; if not, NewTicker will panic.
+func NewTicker(ns int64) *Ticker {
+ if ns <= 0 {
+ panic(os.ErrorString("non-positive interval for NewTicker"))
+ }
+ c := make(chan int64, 1) // See comment on send in tickerLoop
+ t := &Ticker{
+ C: c,
+ c: c,
+ ns: ns,
+ shutdown: make(chan bool, 1),
+ nextTick: Nanoseconds() + ns,
+ }
+ onceStartTickerLoop.Do(startTickerLoop)
+ // must be run in background so global Tickers can be created
+ go func() { newTicker <- t }()
+ return t
+}
diff --git a/libgo/go/time/tick_test.go b/libgo/go/time/tick_test.go
new file mode 100644
index 000000000..2a63a0f2b
--- /dev/null
+++ b/libgo/go/time/tick_test.go
@@ -0,0 +1,45 @@
+// 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.
+
+package time_test
+
+import (
+ "testing"
+ . "time"
+)
+
+func TestTicker(t *testing.T) {
+ const (
+ Delta = 100 * 1e6
+ Count = 10
+ )
+ ticker := NewTicker(Delta)
+ t0 := Nanoseconds()
+ for i := 0; i < Count; i++ {
+ <-ticker.C
+ }
+ ticker.Stop()
+ t1 := Nanoseconds()
+ ns := t1 - t0
+ target := int64(Delta * Count)
+ slop := target * 2 / 10
+ if ns < target-slop || ns > target+slop {
+ t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target))
+ }
+ // Now test that the ticker stopped
+ Sleep(2 * Delta)
+ _, received := <-ticker.C
+ if received {
+ t.Fatal("Ticker did not shut down")
+ }
+}
+
+// Test that a bug tearing down a ticker has been fixed. This routine should not deadlock.
+func TestTeardown(t *testing.T) {
+ for i := 0; i < 3; i++ {
+ ticker := NewTicker(1e8)
+ <-ticker.C
+ ticker.Stop()
+ }
+}
diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go
new file mode 100644
index 000000000..4abd11230
--- /dev/null
+++ b/libgo/go/time/time.go
@@ -0,0 +1,229 @@
+// 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.
+
+// The time package provides functionality for measuring and
+// displaying time.
+package time
+
+import (
+ "os"
+)
+
+// Seconds reports the number of seconds since the Unix epoch,
+// January 1, 1970 00:00:00 UTC.
+func Seconds() int64 {
+ sec, _, err := os.Time()
+ if err != nil {
+ panic(err)
+ }
+ return sec
+}
+
+// Nanoseconds reports the number of nanoseconds since the Unix epoch,
+// January 1, 1970 00:00:00 UTC.
+func Nanoseconds() int64 {
+ sec, nsec, err := os.Time()
+ if err != nil {
+ panic(err)
+ }
+ return sec*1e9 + nsec
+}
+
+// Days of the week.
+const (
+ Sunday = iota
+ Monday
+ Tuesday
+ Wednesday
+ Thursday
+ Friday
+ Saturday
+)
+
+// Time is the struct representing a parsed time value.
+type Time struct {
+ Year int64 // 2006 is 2006
+ Month, Day int // Jan-2 is 1, 2
+ Hour, Minute, Second int // 15:04:05 is 15, 4, 5.
+ Weekday int // Sunday, Monday, ...
+ ZoneOffset int // seconds east of UTC, e.g. -7*60 for -0700
+ Zone string // e.g., "MST"
+}
+
+var nonleapyear = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+var leapyear = []int{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+
+func months(year int64) []int {
+ if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
+ return leapyear
+ }
+ return nonleapyear
+}
+
+const (
+ secondsPerDay = 24 * 60 * 60
+ daysPer400Years = 365*400 + 97
+ daysPer100Years = 365*100 + 24
+ daysPer4Years = 365*4 + 1
+ days1970To2001 = 31*365 + 8
+)
+
+// SecondsToUTC converts sec, in number of seconds since the Unix epoch,
+// into a parsed Time value in the UTC time zone.
+func SecondsToUTC(sec int64) *Time {
+ t := new(Time)
+
+ // Split into time and day.
+ day := sec / secondsPerDay
+ sec -= day * secondsPerDay
+ if sec < 0 {
+ day--
+ sec += secondsPerDay
+ }
+
+ // Time
+ t.Hour = int(sec / 3600)
+ t.Minute = int((sec / 60) % 60)
+ t.Second = int(sec % 60)
+
+ // Day 0 = January 1, 1970 was a Thursday
+ t.Weekday = int((day + Thursday) % 7)
+ if t.Weekday < 0 {
+ t.Weekday += 7
+ }
+
+ // Change day from 0 = 1970 to 0 = 2001,
+ // to make leap year calculations easier
+ // (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
+ day -= days1970To2001
+
+ year := int64(2001)
+ if day < 0 {
+ // Go back enough 400 year cycles to make day positive.
+ n := -day/daysPer400Years + 1
+ year -= 400 * n
+ day += daysPer400Years * n
+ }
+
+ // Cut off 400 year cycles.
+ n := day / daysPer400Years
+ year += 400 * n
+ day -= daysPer400Years * n
+
+ // Cut off 100-year cycles
+ n = day / daysPer100Years
+ if n > 3 { // happens on last day of 400th year
+ n = 3
+ }
+ year += 100 * n
+ day -= daysPer100Years * n
+
+ // Cut off 4-year cycles
+ n = day / daysPer4Years
+ if n > 24 { // happens on last day of 100th year
+ n = 24
+ }
+ year += 4 * n
+ day -= daysPer4Years * n
+
+ // Cut off non-leap years.
+ n = day / 365
+ if n > 3 { // happens on last day of 4th year
+ n = 3
+ }
+ year += n
+ day -= 365 * n
+
+ t.Year = year
+
+ // If someone ever needs yearday,
+ // tyearday = day (+1?)
+
+ months := months(year)
+ var m int
+ yday := int(day)
+ for m = 0; m < 12 && yday >= months[m]; m++ {
+ yday -= months[m]
+ }
+ t.Month = m + 1
+ t.Day = yday + 1
+ t.Zone = "UTC"
+
+ return t
+}
+
+// UTC returns the current time as a parsed Time value in the UTC time zone.
+func UTC() *Time { return SecondsToUTC(Seconds()) }
+
+// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
+// into a parsed Time value in the local time zone.
+func SecondsToLocalTime(sec int64) *Time {
+ z, offset := lookupTimezone(sec)
+ t := SecondsToUTC(sec + int64(offset))
+ t.Zone = z
+ t.ZoneOffset = offset
+ return t
+}
+
+// LocalTime returns the current time as a parsed Time value in the local time zone.
+func LocalTime() *Time { return SecondsToLocalTime(Seconds()) }
+
+// Seconds returns the number of seconds since January 1, 1970 represented by the
+// parsed Time value.
+func (t *Time) Seconds() int64 {
+ // First, accumulate days since January 1, 2001.
+ // Using 2001 instead of 1970 makes the leap-year
+ // handling easier (see SecondsToUTC), because
+ // it is at the beginning of the 4-, 100-, and 400-year cycles.
+ day := int64(0)
+
+ // Rewrite year to be >= 2001.
+ year := t.Year
+ if year < 2001 {
+ n := (2001-year)/400 + 1
+ year += 400 * n
+ day -= daysPer400Years * n
+ }
+
+ // Add in days from 400-year cycles.
+ n := (year - 2001) / 400
+ year -= 400 * n
+ day += daysPer400Years * n
+
+ // Add in 100-year cycles.
+ n = (year - 2001) / 100
+ year -= 100 * n
+ day += daysPer100Years * n
+
+ // Add in 4-year cycles.
+ n = (year - 2001) / 4
+ year -= 4 * n
+ day += daysPer4Years * n
+
+ // Add in non-leap years.
+ n = year - 2001
+ day += 365 * n
+
+ // Add in days this year.
+ months := months(t.Year)
+ for m := 0; m < t.Month-1; m++ {
+ day += int64(months[m])
+ }
+ day += int64(t.Day - 1)
+
+ // Convert days to seconds since January 1, 2001.
+ sec := day * secondsPerDay
+
+ // Add in time elapsed today.
+ sec += int64(t.Hour) * 3600
+ sec += int64(t.Minute) * 60
+ sec += int64(t.Second)
+
+ // Convert from seconds since 2001 to seconds since 1970.
+ sec += days1970To2001 * secondsPerDay
+
+ // Account for local time zone.
+ sec -= int64(t.ZoneOffset)
+ return sec
+}
diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go
new file mode 100644
index 000000000..c86bca1b4
--- /dev/null
+++ b/libgo/go/time/time_test.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.
+
+package time_test
+
+import (
+ "os"
+ "strings"
+ "testing"
+ "testing/quick"
+ . "time"
+)
+
+func init() {
+ // Force US Pacific time for daylight-savings
+ // tests below (localtests). Needs to be set
+ // before the first call into the time library.
+ os.Setenv("TZ", "America/Los_Angeles")
+}
+
+type TimeTest struct {
+ seconds int64
+ golden Time
+}
+
+var utctests = []TimeTest{
+ {0, Time{1970, 1, 1, 0, 0, 0, Thursday, 0, "UTC"}},
+ {1221681866, Time{2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC"}},
+ {-1221681866, Time{1931, 4, 16, 3, 55, 34, Thursday, 0, "UTC"}},
+ {-11644473600, Time{1601, 1, 1, 0, 0, 0, Monday, 0, "UTC"}},
+ {599529660, Time{1988, 12, 31, 0, 1, 0, Saturday, 0, "UTC"}},
+ {978220860, Time{2000, 12, 31, 0, 1, 0, Sunday, 0, "UTC"}},
+ {1e18, Time{31688740476, 10, 23, 1, 46, 40, Friday, 0, "UTC"}},
+ {-1e18, Time{-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "UTC"}},
+ {0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, Sunday, 0, "UTC"}},
+ {-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "UTC"}},
+}
+
+var localtests = []TimeTest{
+ {0, Time{1969, 12, 31, 16, 0, 0, Wednesday, -8 * 60 * 60, "PST"}},
+ {1221681866, Time{2008, 9, 17, 13, 4, 26, Wednesday, -7 * 60 * 60, "PDT"}},
+}
+
+func same(t, u *Time) bool {
+ return t.Year == u.Year &&
+ t.Month == u.Month &&
+ t.Day == u.Day &&
+ t.Hour == u.Hour &&
+ t.Minute == u.Minute &&
+ t.Second == u.Second &&
+ t.Weekday == u.Weekday &&
+ t.ZoneOffset == u.ZoneOffset &&
+ t.Zone == u.Zone
+}
+
+func TestSecondsToUTC(t *testing.T) {
+ for i := 0; i < len(utctests); i++ {
+ sec := utctests[i].seconds
+ golden := &utctests[i].golden
+ tm := SecondsToUTC(sec)
+ newsec := tm.Seconds()
+ if newsec != sec {
+ t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec)
+ }
+ if !same(tm, golden) {
+ t.Errorf("SecondsToUTC(%d):", sec)
+ t.Errorf(" want=%+v", *golden)
+ t.Errorf(" have=%+v", *tm)
+ }
+ }
+}
+
+func TestSecondsToLocalTime(t *testing.T) {
+ for i := 0; i < len(localtests); i++ {
+ sec := localtests[i].seconds
+ golden := &localtests[i].golden
+ tm := SecondsToLocalTime(sec)
+ newsec := tm.Seconds()
+ if newsec != sec {
+ t.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec)
+ }
+ if !same(tm, golden) {
+ t.Errorf("SecondsToLocalTime(%d):", sec)
+ t.Errorf(" want=%+v", *golden)
+ t.Errorf(" have=%+v", *tm)
+ }
+ }
+}
+
+func TestSecondsToUTCAndBack(t *testing.T) {
+ f := func(sec int64) bool { return SecondsToUTC(sec).Seconds() == sec }
+ f32 := func(sec int32) bool { return f(int64(sec)) }
+ cfg := &quick.Config{MaxCount: 10000}
+
+ // Try a reasonable date first, then the huge ones.
+ if err := quick.Check(f32, cfg); err != nil {
+ t.Fatal(err)
+ }
+ if err := quick.Check(f, cfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+type TimeFormatTest struct {
+ time Time
+ formattedValue string
+}
+
+var rfc3339Formats = []TimeFormatTest{
+ {Time{2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC"}, "2008-09-17T20:04:26Z"},
+ {Time{1994, 9, 17, 20, 4, 26, Wednesday, -18000, "EST"}, "1994-09-17T20:04:26-05:00"},
+ {Time{2000, 12, 26, 1, 15, 6, Wednesday, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"},
+}
+
+func TestRFC3339Conversion(t *testing.T) {
+ for _, f := range rfc3339Formats {
+ if f.time.Format(RFC3339) != f.formattedValue {
+ t.Error("RFC3339:")
+ t.Errorf(" want=%+v", f.formattedValue)
+ t.Errorf(" have=%+v", f.time.Format(RFC3339))
+ }
+ }
+}
+
+type FormatTest struct {
+ name string
+ format string
+ result string
+}
+
+var formatTests = []FormatTest{
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010"},
+ {"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010"},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010"},
+ {"RFC822", RFC822, "04 Feb 10 2100 PST"},
+ {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST"},
+ {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST"},
+ {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00"},
+ {"Kitchen", Kitchen, "9:00PM"},
+ {"am/pm", "3pm", "9pm"},
+ {"AM/PM", "3PM", "9PM"},
+}
+
+func TestFormat(t *testing.T) {
+ // The numeric time represents Thu Feb 4 21:00:57 PST 2010
+ time := SecondsToLocalTime(1265346057)
+ for _, test := range formatTests {
+ result := time.Format(test.format)
+ if result != test.result {
+ t.Errorf("%s expected %q got %q", test.name, test.result, result)
+ }
+ }
+}
+
+type ParseTest struct {
+ name string
+ format string
+ value string
+ hasTZ bool // contains a time zone
+ hasWD bool // contains a weekday
+ yearSign int64 // sign of year
+}
+
+var parseTests = []ParseTest{
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1},
+ {"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010", true, true, 1},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1},
+ {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1},
+ {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1},
+ {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1},
+ {"custom: \"2006-01-02 15:04:05-07\"", "2006-01-02 15:04:05-07", "2010-02-04 21:00:57-08", true, false, 1},
+ // Amount of white space should not matter.
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1},
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1},
+}
+
+func TestParse(t *testing.T) {
+ for _, test := range parseTests {
+ time, err := Parse(test.format, test.value)
+ if err != nil {
+ t.Errorf("%s error: %v", test.name, err)
+ } else {
+ checkTime(time, &test, t)
+ }
+ }
+}
+
+var rubyTests = []ParseTest{
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1},
+ // Ignore the time zone in the test. If it parses, it'll be OK.
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0000 2010", false, true, 1},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +0000 2010", false, true, 1},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +1130 2010", false, true, 1},
+}
+
+// Problematic time zone format needs special tests.
+func TestRubyParse(t *testing.T) {
+ for _, test := range rubyTests {
+ time, err := Parse(test.format, test.value)
+ if err != nil {
+ t.Errorf("%s error: %v", test.name, err)
+ } else {
+ checkTime(time, &test, t)
+ }
+ }
+}
+
+func checkTime(time *Time, test *ParseTest, t *testing.T) {
+ // The time should be Thu Feb 4 21:00:57 PST 2010
+ if test.yearSign*time.Year != 2010 {
+ t.Errorf("%s: bad year: %d not %d", test.name, time.Year, 2010)
+ }
+ if time.Month != 2 {
+ t.Errorf("%s: bad month: %d not %d", test.name, time.Month, 2)
+ }
+ if time.Day != 4 {
+ t.Errorf("%s: bad day: %d not %d", test.name, time.Day, 4)
+ }
+ if time.Hour != 21 {
+ t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour, 21)
+ }
+ if time.Minute != 0 {
+ t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute, 0)
+ }
+ if time.Second != 57 {
+ t.Errorf("%s: bad second: %d not %d", test.name, time.Second, 57)
+ }
+ if test.hasTZ && time.ZoneOffset != -28800 {
+ t.Errorf("%s: bad tz offset: %d not %d", test.name, time.ZoneOffset, -28800)
+ }
+ if test.hasWD && time.Weekday != 4 {
+ t.Errorf("%s: bad weekday: %d not %d", test.name, time.Weekday, 4)
+ }
+}
+
+func TestFormatAndParse(t *testing.T) {
+ const fmt = "Mon MST " + RFC3339 // all fields
+ f := func(sec int64) bool {
+ t1 := SecondsToLocalTime(sec)
+ if t1.Year < 1000 || t1.Year > 9999 {
+ // not required to work
+ return true
+ }
+ t2, err := Parse(fmt, t1.Format(fmt))
+ if err != nil {
+ t.Errorf("error: %s", err)
+ return false
+ }
+ if !same(t1, t2) {
+ t.Errorf("different: %q %q", t1, t2)
+ return false
+ }
+ return true
+ }
+ f32 := func(sec int32) bool { return f(int64(sec)) }
+ cfg := &quick.Config{MaxCount: 10000}
+
+ // Try a reasonable date first, then the huge ones.
+ if err := quick.Check(f32, cfg); err != nil {
+ t.Fatal(err)
+ }
+ if err := quick.Check(f, cfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+type ParseErrorTest struct {
+ format string
+ value string
+ expect string // must appear within the error
+}
+
+var parseErrorTests = []ParseErrorTest{
+ {ANSIC, "Feb 4 21:00:60 2010", "parse"}, // cannot parse Feb as Mon
+ {ANSIC, "Thu Feb 4 21:00:57 @2010", "parse"},
+ {ANSIC, "Thu Feb 4 21:00:60 2010", "second out of range"},
+ {ANSIC, "Thu Feb 4 21:61:57 2010", "minute out of range"},
+ {ANSIC, "Thu Feb 4 24:00:60 2010", "hour out of range"},
+}
+
+func TestParseErrors(t *testing.T) {
+ for _, test := range parseErrorTests {
+ _, err := Parse(test.format, test.value)
+ if err == nil {
+ t.Errorf("expected error for %q %q", test.format, test.value)
+ } else if strings.Index(err.String(), test.expect) < 0 {
+ t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err)
+ }
+ }
+}
+
+// Check that a time without a Zone still produces a (numeric) time zone
+// when formatted with MST as a requested zone.
+func TestMissingZone(t *testing.T) {
+ time, err := Parse(RubyDate, "Tue Feb 02 16:10:03 -0500 2006")
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ expect := "Tue Feb 2 16:10:03 -0500 2006" // -0500 not EST
+ str := time.Format(UnixDate) // uses MST as its time zone
+ if str != expect {
+ t.Errorf("expected %q got %q", expect, str)
+ }
+}
+
+func TestMinutesInTimeZone(t *testing.T) {
+ time, err := Parse(RubyDate, "Mon Jan 02 15:04:05 +0123 2006")
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ expected := (1*60 + 23) * 60
+ if time.ZoneOffset != expected {
+ t.Errorf("ZoneOffset incorrect, expected %d got %d", expected, time.ZoneOffset)
+ }
+}
+
+func BenchmarkSeconds(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Seconds()
+ }
+}
+
+func BenchmarkNanoseconds(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Nanoseconds()
+ }
+}
+
+func BenchmarkFormat(b *testing.B) {
+ time := SecondsToLocalTime(1265346057)
+ for i := 0; i < b.N; i++ {
+ time.Format("Mon Jan 2 15:04:05 2006")
+ }
+}
+
+func BenchmarkParse(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Parse(ANSIC, "Mon Jan 2 15:04:05 2006")
+ }
+}
diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go
new file mode 100644
index 000000000..6685da747
--- /dev/null
+++ b/libgo/go/time/zoneinfo_unix.go
@@ -0,0 +1,267 @@
+// 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 (
+ "io/ioutil"
+ "os"
+ "sync"
+)
+
+const (
+ headerSize = 4 + 16 + 4*7
+ zoneDir = "/usr/share/zoneinfo/"
+ zoneDir2 = "/usr/share/lib/zoneinfo/"
+)
+
+// 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)
+}
+
+// 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
+}
+
+func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
+ d := data{bytes, false}
+
+ // 4-byte magic "TZif"
+ if magic := d.read(4); string(magic) != "TZif" {
+ return nil, false
+ }
+
+ // 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, false
+ }
+
+ // 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, false
+ }
+ 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, false
+ }
+
+ // 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]
+ z := make([]zone, n[NZone])
+ for i := 0; i < len(z); i++ {
+ var ok bool
+ var n uint32
+ if n, ok = zonedata.big4(); !ok {
+ return nil, false
+ }
+ z[i].utcoff = int(n)
+ var b byte
+ if b, ok = zonedata.byte(); !ok {
+ return nil, false
+ }
+ z[i].isdst = b != 0
+ if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
+ return nil, false
+ }
+ z[i].name = byteString(abbrev[b:])
+ }
+
+ // Now the transition time info.
+ zt = make([]zonetime, n[NTime])
+ for i := 0; i < len(zt); i++ {
+ var ok bool
+ var n uint32
+ if n, ok = txtimes.big4(); !ok {
+ return nil, false
+ }
+ zt[i].time = int32(n)
+ if int(txzones[i]) >= len(z) {
+ return nil, false
+ }
+ zt[i].zone = &z[txzones[i]]
+ if i < len(isstd) {
+ zt[i].isstd = isstd[i] != 0
+ }
+ if i < len(isutc) {
+ zt[i].isutc = isutc[i] != 0
+ }
+ }
+ return zt, true
+}
+
+func readinfofile(name string) ([]zonetime, bool) {
+ buf, err := ioutil.ReadFile(name)
+ if err != nil {
+ return nil, false
+ }
+ return parseinfo(buf)
+}
+
+var zones []zonetime
+var onceSetupZone sync.Once
+
+func setupZone() {
+ // consult $TZ to find the time zone to use.
+ // no $TZ means use the system default /etc/localtime.
+ // $TZ="" means use UTC.
+ // $TZ="foo" means use /usr/share/zoneinfo/foo.
+
+ tz, err := os.Getenverror("TZ")
+ switch {
+ case err == os.ENOENV:
+ zones, _ = readinfofile("/etc/localtime")
+ case len(tz) > 0:
+ var ok bool
+ zones, ok = readinfofile(zoneDir + tz)
+ if !ok {
+ zones, _ = readinfofile(zoneDir2 + tz)
+ }
+ case len(tz) == 0:
+ // do nothing: use UTC
+ }
+}
+
+// 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
+}
diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go
new file mode 100644
index 000000000..c357eec62
--- /dev/null
+++ b/libgo/go/time/zoneinfo_windows.go
@@ -0,0 +1,192 @@
+// 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.
+
+package time
+
+import (
+ "syscall"
+ "sync"
+ "os"
+)
+
+// BUG(brainman): The Windows implementation assumes that
+// this year's rules for daylight savings time apply to all previous
+// and future years as well.
+
+// TODO(brainman): use GetDynamicTimeZoneInformation, whenever posible (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
+}
+
+// Populate zone struct with Windows supplied information. Returns true, if data is valid.
+func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uint16) (dateisgood bool) {
+ z.name = syscall.UTF16ToString(name)
+ 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-calculte cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format.
+func (z *zone) preCalculateAbsSec() {
+ if z.year != 0 {
+ z.abssec = (&Time{z.year, int(z.month), int(z.day), int(z.hour), int(z.minute), int(z.second), 0, 0, ""}).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 particualar year.
+func (z *zone) cutoffSeconds(year int64) int64 {
+ // Windows specifies daylight savings information in "day in month" format:
+ // z.month is month number (1-12)
+ // z.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)
+ // z.hour, z.minute and z.second are absolute time
+ t := &Time{year, int(z.month), 1, int(z.hour), int(z.minute), int(z.second), 0, 0, ""}
+ t = SecondsToUTC(t.Seconds())
+ i := int(z.dayofweek) - t.Weekday
+ if i < 0 {
+ i += 7
+ }
+ t.Day += i
+ if week := int(z.day) - 1; week < 4 {
+ t.Day += week * 7
+ } else {
+ // "Last" instance of the day.
+ t.Day += 4 * 7
+ if t.Day > months(year)[t.Month] {
+ t.Day -= 7
+ }
+ }
+ // Result is in "local" time. Adjust it for "utc".
+ return t.Seconds() - int64(z.prev.offset)
+}
+
+// Is t before the cutoff for switching to z?
+func (z *zone) isBeforeCutoff(t *Time) bool {
+ 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 {
+ disabled bool // daylight saving time is not used localy
+ offsetIfDisabled int
+ januaryIsStd bool // is january 1 standard time?
+ std, dst zone
+}
+
+// Pick zone (std or dst) t time belongs to.
+func (zi *zoneinfo) pickZone(t *Time) *zone {
+ z := &zi.std
+ if tz.januaryIsStd {
+ if !zi.dst.isBeforeCutoff(t) && zi.std.isBeforeCutoff(t) {
+ // after switch to daylight time and before the switch back to standard
+ z = &zi.dst
+ }
+ } else {
+ 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
+var initError os.Error
+var onceSetupZone sync.Once
+
+func setupZone() {
+ var i syscall.Timezoneinformation
+ if _, e := syscall.GetTimeZoneInformation(&i); e != 0 {
+ initError = os.NewSyscallError("GetTimeZoneInformation", e)
+ return
+ }
+ if !tz.std.populate(i.Bias, i.StandardBias, &i.StandardDate, i.StandardName[0:]) {
+ tz.disabled = true
+ tz.offsetIfDisabled = tz.std.offset
+ return
+ }
+ 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)
+}
+
+// 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 initError != nil {
+ return "", 0
+ }
+ 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
+}
+
+// lookupByName returns the time offset for the
+// time zone with the given abbreviation. It only considers
+// time zones that apply to the current system.
+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
+}