OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/tags/gnu-dev/fsf-gcc-snapshot-1-mar-12/or1k-gcc/libgo/go/time
    from Rev 747 to Rev 783
    Reverse comparison

Rev 747 → Rev 783

/internal_test.go
0,0 → 1,13
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
package time
 
func init() {
// force US/Pacific for time zone tests
localOnce.Do(initTestingZone)
}
 
var Interrupt = interrupt
var DaysIn = daysIn
/sys_plan9.go
0,0 → 1,40
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
// +build plan9
 
package time
 
import "syscall"
 
// for testing: whatever interrupts a sleep
func interrupt() {
// cannot predict pid, don't want to kill group
}
 
// 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)
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
}
/sys_unix.go
0,0 → 1,40
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
// +build darwin freebsd linux netbsd openbsd
 
package time
 
import "syscall"
 
// for testing: whatever interrupts a sleep
func interrupt() {
syscall.Kill(syscall.Getpid(), syscall.SIGCHLD)
}
 
// 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
}
/zoneinfo.go
0,0 → 1,191
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
package time
 
import "sync"
 
// A Location maps time instants to the zone in use at that time.
// Typically, the Location represents the collection of time offsets
// in use in a geographical area, such as CEST and CET for central Europe.
type Location struct {
name string
zone []zone
tx []zoneTrans
 
// Most lookups will be for the current time.
// To avoid the binary search through tx, keep a
// static one-element cache that gives the correct
// zone for the time when the Location was created.
// if cacheStart <= t <= cacheEnd,
// lookup can return cacheZone.
// The units for cacheStart and cacheEnd are seconds
// since January 1, 1970 UTC, to match the argument
// to lookup.
cacheStart int64
cacheEnd int64
cacheZone *zone
}
 
// A zone represents a single time zone such as CEST or CET.
type zone struct {
name string // abbreviated name, "CET"
offset int // seconds east of UTC
isDST bool // is this zone Daylight Savings Time?
}
 
// A zoneTrans represents a single time zone transition.
type zoneTrans struct {
when int64 // transition time, in seconds since 1970 GMT
index uint8 // the index of the zone that goes into effect at that time
isstd, isutc bool // ignored - no idea what these mean
}
 
// UTC represents Universal Coordinated Time (UTC).
var UTC *Location = &utcLoc
 
// utcLoc is separate so that get can refer to &utcLoc
// and ensure that it never returns a nil *Location,
// even if a badly behaved client has changed UTC.
var utcLoc = Location{name: "UTC"}
 
// Local represents the system's local time zone.
var Local *Location = &localLoc
 
// localLoc is separate so that initLocal can initialize
// it even if a client has changed Local.
var localLoc Location
var localOnce sync.Once
 
func (l *Location) get() *Location {
if l == nil {
return &utcLoc
}
if l == &localLoc {
localOnce.Do(initLocal)
}
return l
}
 
// String returns a descriptive name for the time zone information,
// corresponding to the argument to LoadLocation.
func (l *Location) String() string {
return l.get().name
}
 
// FixedZone returns a Location that always uses
// the given zone name and offset (seconds east of UTC).
func FixedZone(name string, offset int) *Location {
l := &Location{
name: name,
zone: []zone{{name, offset, false}},
tx: []zoneTrans{{-1 << 63, 0, false, false}},
cacheStart: -1 << 63,
cacheEnd: 1<<63 - 1,
}
l.cacheZone = &l.zone[0]
return l
}
 
// lookup returns information about the time zone in use at an
// instant in time expressed as seconds since January 1, 1970 00:00:00 UTC.
//
// The returned information gives the name of the zone (such as "CET"),
// the start and end times bracketing sec when that zone is in effect,
// the offset in seconds east of UTC (such as -5*60*60), and whether
// the daylight savings is being observed at that time.
func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start, end int64) {
l = l.get()
 
if len(l.tx) == 0 {
name = "UTC"
offset = 0
isDST = false
start = -1 << 63
end = 1<<63 - 1
return
}
 
if zone := l.cacheZone; zone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = l.cacheStart
end = l.cacheEnd
return
}
 
// Binary search for entry with largest time <= sec.
// Not using sort.Search to avoid dependencies.
tx := l.tx
end = 1<<63 - 1
for len(tx) > 1 {
m := len(tx) / 2
lim := tx[m].when
if sec < lim {
end = lim
tx = tx[0:m]
} else {
tx = tx[m:]
}
}
zone := &l.zone[tx[0].index]
name = zone.name
offset = zone.offset
isDST = zone.isDST
start = tx[0].when
// end = maintained during the search
return
}
 
// lookupName returns information about the time zone with
// the given name (such as "EST").
func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) {
l = l.get()
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
return zone.offset, zone.isDST, true
}
}
return
}
 
// lookupOffset returns information about the time zone with
// the given offset (such as -5*60*60).
func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
l = l.get()
for i := range l.zone {
zone := &l.zone[i]
if zone.offset == offset {
return zone.name, zone.isDST, true
}
}
return
}
 
// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
// syntax too, but I don't feel like implementing it today.
 
// NOTE(rsc): Using the IANA names below means ensuring we have access
// to the database. Probably we will ship the files in $GOROOT/lib/zoneinfo/
// and only look there if there are no system files available (such as on Windows).
// The files total 200 kB.
 
// LoadLocation returns the Location with the given name.
//
// If the name is "" or "UTC", LoadLocation returns UTC.
// If the name is "Local", LoadLocation returns Local.
//
// Otherwise, the name is taken to be a location name corresponding to a file
// in the IANA Time Zone database, such as "America/New_York".
func LoadLocation(name string) (*Location, error) {
if name == "" || name == "UTC" {
return UTC, nil
}
if name == "Local" {
return Local, nil
}
return loadLocation(name)
}
/format.go
0,0 → 1,1009
// Copyright 2010 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 "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.
//
// 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
// 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.
//
// 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"
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"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)
 
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:]
}
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:]
}
}
}
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",
}
 
// match returns true if s1 and s2 match ignoring case.
// It is assumed s1 and s2 are the same length.
func match(s1, s2 string) bool {
for i := 0; i < len(s1); i++ {
c1 := s1[i]
c2 := s2[i]
if c1 != c2 {
// Switch to lower-case; 'a'-'A' is known to be a single bit.
c1 |= 'a' - 'A'
c2 |= 'a' - 'A'
if c1 != c2 || c1 < 'a' || c1 > 'z' {
return false
}
}
}
return true
}
 
func lookup(tab []string, val string) (int, string, error) {
for i, v := range tab {
if len(val) >= len(v) && match(val[0:len(v)], v) {
return i, val[len(v):], nil
}
}
return -1, val, errBad
}
 
// Duplicates functionality in strconv, but avoids dependency.
func itoa(x int) string {
var buf [32]byte
n := len(buf)
if x == 0 {
return "0"
}
u := uint(x)
if x < 0 {
u = -u
}
for u > 0 {
n--
buf[n] = byte(u%10 + '0')
u /= 10
}
if x < 0 {
n--
buf[n] = '-'
}
return string(buf[n:])
}
 
// Never printed, just needs to be non-nil for return by atoi.
var atoiError = errors.New("time: invalid number")
 
// Duplicates functionality in strconv, but avoids dependency.
func atoi(s string) (x int, err error) {
neg := false
if s != "" && s[0] == '-' {
neg = true
s = s[1:]
}
x, rem, err := leadingInt(s)
if err != nil || rem != "" {
return 0, atoiError
}
if neg {
x = -x
}
return x, nil
}
 
func pad(i int, padding string) string {
s := itoa(i)
if i < 10 {
s = padding + s
}
return s
}
 
func zeroPad(i int) string { return pad(i, "0") }
 
// formatNano formats a fractional second, as nanoseconds.
func formatNano(nanosec, n int) 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))
// Zero pad left without fmt.
if len(s) < 9 {
s = "000000000"[:9-len(s)] + s
}
if n > 9 {
n = 9
}
return "." + s[:n]
}
 
// String returns the time formatted using the format string
// "Mon Jan _2 15:04:05 -0700 MST 2006"
func (t Time) String() string {
return t.Format("Mon Jan _2 15:04:05 -0700 MST 2006")
}
 
type buffer []byte
 
func (b *buffer) WriteString(s string) {
*b = append(*b, s...)
}
 
func (b *buffer) String() string {
return string([]byte(*b))
}
 
// 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 {
var (
year int = -1
month Month
day int
hour int = -1
min int
sec int
b buffer
)
// Each iteration generates one std value.
for {
prefix, std, suffix := nextStdChunk(layout)
b.WriteString(prefix)
if std == "" {
break
}
 
// Compute year, month, day if needed.
if year < 0 {
// Jan 01 02 2006
if a, z := std[0], std[len(std)-1]; a == 'J' || a == 'j' || z == '1' || z == '2' || z == '6' {
year, month, day = t.Date()
}
}
 
// Compute hour, minute, second if needed.
if hour < 0 {
// 03 04 05 15 pm
if z := std[len(std)-1]; z == '3' || z == '4' || z == '5' || z == 'm' || z == 'M' {
hour, min, sec = t.Clock()
}
}
 
var p string
switch std {
case stdYear:
p = zeroPad(year % 100)
case stdLongYear:
p = itoa(year)
case stdMonth:
p = month.String()[:3]
case stdLongMonth:
p = month.String()
case stdNumMonth:
p = itoa(int(month))
case stdZeroMonth:
p = zeroPad(int(month))
case stdWeekDay:
p = t.Weekday().String()[:3]
case stdLongWeekDay:
p = t.Weekday().String()
case stdDay:
p = itoa(day)
case stdUnderDay:
p = pad(day, " ")
case stdZeroDay:
p = zeroPad(day)
case stdHour:
p = zeroPad(hour)
case stdHour12:
// Noon is 12PM, midnight is 12AM.
hr := hour % 12
if hr == 0 {
hr = 12
}
p = itoa(hr)
case stdZeroHour12:
// Noon is 12PM, midnight is 12AM.
hr := hour % 12
if hr == 0 {
hr = 12
}
p = zeroPad(hr)
case stdMinute:
p = itoa(min)
case stdZeroMinute:
p = zeroPad(min)
case stdSecond:
p = itoa(sec)
case stdZeroSecond:
p = zeroPad(sec)
case stdPM:
if hour >= 12 {
p = "PM"
} else {
p = "AM"
}
case stdpm:
if hour >= 12 {
p = "pm"
} else {
p = "am"
}
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".
_, offset := t.Zone()
if offset == 0 && std[0] == 'Z' {
p = "Z"
break
}
zone := offset / 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 stdTZ:
name, offset := t.Zone()
if name != "" {
p = name
} else {
// No time zone known for this time, but we must print one.
// Use the -0700 format.
zone := offset / 60 // convert to minutes
if zone < 0 {
p = "-"
zone = -zone
} else {
p = "+"
}
p += zeroPad(zone / 60)
p += zeroPad(zone % 60)
}
default:
if len(std) >= 2 && std[0:2] == ".0" {
p = formatNano(t.Nanosecond(), len(std)-1)
}
}
b.WriteString(p)
layout = suffix
}
return b.String()
}
 
var errBad = errors.New("bad value for field") // placeholder not passed to user
 
// ParseError describes a problem parsing a time string.
type ParseError struct {
Layout string
Value string
LayoutElem string
ValueElem string
Message string
}
 
func quote(s string) string {
return "\"" + s + "\""
}
 
// Error returns the string representation of a ParseError.
func (e *ParseError) Error() string {
if e.Message == "" {
return "parsing time " +
quote(e.Value) + " as " +
quote(e.Layout) + ": cannot parse " +
quote(e.ValueElem) + " as " +
quote(e.LayoutElem)
}
return "parsing time " +
quote(e.Value) + e.Message
}
 
// isDigit returns true if s[i] is a decimal digit, false if not or
// if s[i] is out of range.
func isDigit(s string, i int) bool {
if len(s) <= i {
return false
}
c := s[i]
return '0' <= c && c <= '9'
}
 
// 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, error) {
if !isDigit(s, 0) {
return 0, s, errBad
}
if !isDigit(s, 1) {
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, 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.
//
// 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
// for syntax but it is otherwise ignored.
func Parse(layout, value string) (Time, error) {
alayout, avalue := layout, value
rangeErrString := "" // set if a value is out of range
amSet := false // do we need to subtract 12 from the hour for midnight?
pmSet := false // do we need to add 12 to the hour?
 
// Time being constructed.
var (
year int
month int = 1 // January
day int = 1
hour int
min int
sec int
nsec int
z *Location
zoneOffset int = -1
zoneName string
)
 
// Each iteration processes one std value.
for {
var err error
prefix, std, suffix := nextStdChunk(layout)
value, err = skip(value, prefix)
if err != nil {
return Time{}, &ParseError{alayout, avalue, prefix, value, ""}
}
if len(std) == 0 {
if len(value) != 0 {
return Time{}, &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:]
year, err = atoi(p)
if year >= 69 { // Unix time starts Dec 31 1969 in some time zones
year += 1900
} else {
year += 2000
}
case stdLongYear:
if len(value) < 4 || !isDigit(value, 0) {
err = errBad
break
}
p, value = value[0:4], value[4:]
year, err = atoi(p)
case stdMonth:
month, value, err = lookup(shortMonthNames, value)
case stdLongMonth:
month, value, err = lookup(longMonthNames, value)
case stdNumMonth, stdZeroMonth:
month, value, err = getnum(value, std == stdZeroMonth)
if month <= 0 || 12 < month {
rangeErrString = "month"
}
case stdWeekDay:
// Ignore weekday except for error checking.
_, value, err = lookup(shortDayNames, value)
case stdLongWeekDay:
_, value, err = lookup(longDayNames, value)
case stdDay, stdUnderDay, stdZeroDay:
if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
value = value[1:]
}
day, value, err = getnum(value, std == stdZeroDay)
if day < 0 || 31 < day {
rangeErrString = "day"
}
case stdHour:
hour, value, err = getnum(value, false)
if hour < 0 || 24 <= hour {
rangeErrString = "hour"
}
case stdHour12, stdZeroHour12:
hour, value, err = getnum(value, std == stdZeroHour12)
if hour < 0 || 12 < hour {
rangeErrString = "hour"
}
case stdMinute, stdZeroMinute:
min, value, err = getnum(value, std == stdZeroMinute)
if min < 0 || 60 <= min {
rangeErrString = "minute"
}
case stdSecond, stdZeroSecond:
sec, value, err = getnum(value, std == stdZeroSecond)
if sec < 0 || 60 <= sec {
rangeErrString = "second"
}
// Special case: do we have a fractional second but no
// fractional second in the format?
if len(value) > 2 && value[0] == '.' && isDigit(value, 1) {
_, std, _ := nextStdChunk(layout)
if len(std) > 0 && std[0] == '.' && isDigit(std, 1) {
// Fractional second in the layout; proceed normally
break
}
// No fractional second in the layout but we have one in the input.
n := 2
for ; n < len(value) && isDigit(value, n); n++ {
}
nsec, rangeErrString, err = parseNanoseconds(value, n)
value = value[n:]
}
case stdPM:
if len(value) < 2 {
err = errBad
break
}
p, value = value[0:2], value[2:]
switch p {
case "PM":
pmSet = true
case "AM":
amSet = true
default:
err = errBad
}
case stdpm:
if len(value) < 2 {
err = errBad
break
}
p, value = value[0:2], value[2:]
switch p {
case "pm":
pmSet = true
case "am":
amSet = true
default:
err = errBad
}
case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ:
if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' {
value = value[1:]
z = UTC
break
}
var sign, hour, min string
if std == stdISO8601ColonTZ || std == stdNumColonTZ {
if len(value) < 6 {
err = errBad
break
}
if value[3] != ':' {
err = errBad
break
}
sign, hour, min, value = value[0:1], value[1:3], value[4:6], value[6:]
} else if std == stdNumShortTZ {
if len(value) < 3 {
err = errBad
break
}
sign, hour, min, value = value[0:1], value[1:3], "00", value[3:]
} else {
if len(value) < 5 {
err = errBad
break
}
sign, hour, min, value = value[0:1], value[1:3], value[3:5], value[5:]
}
var hr, mm int
hr, err = atoi(hour)
if err == nil {
mm, err = atoi(min)
}
zoneOffset = (hr*60 + mm) * 60 // offset is in seconds
switch sign[0] {
case '+':
case '-':
zoneOffset = -zoneOffset
default:
err = errBad
}
case stdTZ:
// Does it look like a time zone?
if len(value) >= 3 && value[0:3] == "UTC" {
z = UTC
value = 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.
zoneName = p
default:
if len(value) < len(std) {
err = errBad
break
}
if len(std) >= 2 && std[0:2] == ".0" {
nsec, rangeErrString, err = parseNanoseconds(value, len(std))
value = value[len(std):]
}
}
if rangeErrString != "" {
return Time{}, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"}
}
if err != nil {
return Time{}, &ParseError{alayout, avalue, std, value, ""}
}
}
if pmSet && hour < 12 {
hour += 12
} else if amSet && hour == 12 {
hour = 0
}
 
// TODO: be more aggressive checking day?
if z != nil {
return Date(year, Month(month), day, hour, min, sec, nsec, z), nil
}
 
t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
if zoneOffset != -1 {
t.sec -= int64(zoneOffset)
 
// Look for local zone with the given offset.
// If that zone was in effect at the given time, use it.
name, offset, _, _, _ := Local.lookup(t.sec + internalToUnix)
if offset == zoneOffset && (zoneName == "" || name == zoneName) {
t.loc = Local
return t, nil
}
 
// Otherwise create fake zone to record offset.
t.loc = FixedZone(zoneName, zoneOffset)
return t, nil
}
 
if zoneName != "" {
// Look for local zone with the given offset.
// If that zone was in effect at the given time, use it.
offset, _, ok := Local.lookupName(zoneName)
if ok {
name, off, _, _, _ := Local.lookup(t.sec + internalToUnix - int64(offset))
if name == zoneName && off == offset {
t.sec -= int64(offset)
t.loc = Local
return t, nil
}
}
 
// Otherwise, create fake zone with unknown offset.
t.loc = FixedZone(zoneName, 0)
return t, nil
}
 
// Otherwise, fall back to UTC.
return t, nil
}
 
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
if value[0] != '.' {
err = errBad
return
}
ns, err = atoi(value[1:nbytes])
if err != nil {
return
}
if ns < 0 || 1e9 <= ns {
rangeErrString = "fractional second"
return
}
// We need nanoseconds, which means scaling by the number
// of missing digits in the format, maximum length 10. If it's
// longer than 10, we won't scale.
scaleDigits := 10 - nbytes
for i := 0; i < scaleDigits; i++ {
ns *= 10
}
return
}
 
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
 
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x >= (1<<31-10)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int(c) - '0'
}
return x, s[i:], nil
}
 
var unitMap = map[string]float64{
"ns": float64(Nanosecond),
"us": float64(Microsecond),
"µs": float64(Microsecond), // U+00B5 = micro symbol
"μs": float64(Microsecond), // U+03BC = Greek letter mu
"ms": float64(Millisecond),
"s": float64(Second),
"m": float64(Minute),
"h": float64(Hour),
}
 
// ParseDuration parses a duration string.
// A duration string is a possibly signed sequence of
// decimal numbers, each with optional fraction and a unit suffix,
// such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
func ParseDuration(s string) (Duration, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
f := float64(0)
neg := false
 
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("time: invalid duration " + orig)
}
for s != "" {
g := float64(0) // this element of the sequence
 
var x int
var err error
 
// The next character must be [0-9.]
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
return 0, errors.New("time: invalid duration " + orig)
}
// Consume [0-9]*
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
g = float64(x)
pre := pl != len(s) // whether we consumed anything before a period
 
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
scale := 1
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
g += float64(x) / float64(scale)
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("time: invalid duration " + orig)
}
 
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || ('0' <= c && c <= '9') {
break
}
}
if i == 0 {
return 0, errors.New("time: missing unit in duration " + orig)
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
}
 
f += g * unit
}
 
if neg {
f = -f
}
return Duration(f), nil
}
/sys_windows.go
0,0 → 1,9
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
package time
 
// for testing: whatever interrupts a sleep
func interrupt() {
}
/tick_test.go
0,0 → 1,58
// 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 * Millisecond
Count = 10
)
ticker := NewTicker(Delta)
t0 := Now()
for i := 0; i < Count; i++ {
<-ticker.C
}
ticker.Stop()
t1 := Now()
dt := t1.Sub(t0)
target := Delta * Count
slop := target * 2 / 10
if dt < target-slop || dt > target+slop {
t.Fatalf("%d %s ticks took %s, expected [%s,%s]", Count, Delta, dt, target-slop, target+slop)
}
// Now test that the ticker stopped
Sleep(2 * Delta)
select {
case <-ticker.C:
t.Fatal("Ticker did not shut down")
default:
// ok
}
}
 
// 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()
}
}
 
func BenchmarkTicker(b *testing.B) {
ticker := NewTicker(1)
b.ResetTimer()
b.StartTimer()
for i := 0; i < b.N; i++ {
<-ticker.C
}
b.StopTimer()
ticker.Stop()
}
/zoneinfo_unix.go
0,0 → 1,257
// 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.
 
// +build darwin freebsd linux netbsd openbsd
 
// 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"
"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()
}
 
// Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
var zoneDirs = []string{
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
}
 
func initLocal() {
// consult $TZ to find the time zone to use.
// no $TZ means use the system default /etc/localtime.
// $TZ="" means use UTC.
// $TZ="foo" means use /usr/share/zoneinfo/foo.
 
tz, ok := syscall.Getenv("TZ")
switch {
case !ok:
z, err := loadZoneFile("/etc/localtime")
if err == nil {
localLoc = *z
localLoc.name = "Local"
return
}
case tz != "" && tz != "UTC":
if z, err := loadLocation(tz); err == nil {
localLoc = *z
return
}
}
 
// Fall back to UTC.
localLoc.name = "UTC"
}
 
func loadLocation(name string) (*Location, error) {
for _, zoneDir := range zoneDirs {
if z, err := loadZoneFile(zoneDir + name); err == nil {
z.name = name
return z, nil
}
}
return nil, errors.New("unknown time zone " + name)
}
/example_test.go
0,0 → 1,58
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
package time_test
 
import (
"fmt"
"time"
)
 
func expensiveCall() {}
 
func ExampleDuration() {
t0 := time.Now()
expensiveCall()
t1 := time.Now()
fmt.Printf("The call took %v to run.\n", t1.Sub(t0))
}
 
var c chan int
 
func handle(int) {}
 
func ExampleAfter() {
select {
case m := <-c:
handle(m)
case <-time.After(5 * time.Minute):
fmt.Println("timed out")
}
}
 
func ExampleSleep() {
time.Sleep(100 * time.Millisecond)
}
 
func statusUpdate() string { return "" }
 
func ExampleTick() {
c := time.Tick(1 * time.Minute)
for now := range c {
fmt.Printf("%v %s\n", now, statusUpdate())
}
}
 
func ExampleMonth() {
_, month, day := time.Now().Date()
if month == time.November && day == 10 {
fmt.Println("Happy Go day!")
}
}
 
// Go launched at Tue Nov 10 15:00:00 -0800 PST 2009
func ExampleDate() {
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
fmt.Printf("Go launched at %s\n", t.Local())
}
/tick.go
0,0 → 1,53
// 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 "errors"
 
// A Ticker holds a synchronous channel that delivers `ticks' of a clock
// at intervals.
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
 
// NewTicker returns a new Ticker containing a channel that will send the
// time with a period specified by the duration argument.
// It adjusts the intervals or drops ticks to make up for slow receivers.
// The duration d must be greater than zero; if not, NewTicker will panic.
func NewTicker(d Duration) *Ticker {
if d <= 0 {
panic(errors.New("non-positive interval for NewTicker"))
}
// Give the channel a 1-element time buffer.
// If the client falls behind while reading, we drop ticks
// on the floor until the client catches up.
c := make(chan Time, 1)
t := &Ticker{
C: c,
r: runtimeTimer{
when: nano() + int64(d),
period: int64(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
 
// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
stopTimer(&t.r)
}
 
// 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(d Duration) <-chan Time {
if d <= 0 {
return nil
}
return NewTicker(d).C
}
/zoneinfo_plan9.go
0,0 → 1,157
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
 
// Parse Plan 9 timezone(2) files.
 
package time
 
import (
"errors"
"syscall"
)
 
var badData = errors.New("malformed time zone information")
 
func isSpace(r rune) bool {
return r == ' ' || r == '\t' || r == '\n'
}
 
// Copied from strings to avoid a dependency.
func fields(s string) []string {
// First count the fields.
n := 0
inField := false
for _, rune := range s {
wasInField := inField
inField = !isSpace(rune)
if inField && !wasInField {
n++
}
}
 
// Now create them.
a := make([]string, n)
na := 0
fieldStart := -1 // Set to -1 when looking for start of field.
for i, rune := range s {
if isSpace(rune) {
if fieldStart >= 0 {
a[na] = s[fieldStart:i]
na++
fieldStart = -1
}
} else if fieldStart == -1 {
fieldStart = i
}
}
if fieldStart >= 0 { // Last field might end at EOF.
a[na] = s[fieldStart:]
}
return a
}
 
func loadZoneData(s string) (l *Location, err error) {
f := fields(s)
if len(f) < 4 {
if len(f) == 2 && f[0] == "GMT" {
return UTC, nil
}
return nil, badData
}
 
var zones [2]zone
 
// standard timezone offset
o, err := atoi(f[1])
if err != nil {
return nil, badData
}
zones[0] = zone{name: f[0], offset: o, isDST: false}
 
// alternate timezone offset
o, err = atoi(f[3])
if err != nil {
return nil, badData
}
zones[1] = zone{name: f[2], offset: o, isDST: true}
 
// transition time pairs
var tx []zoneTrans
f = f[4:]
for i := 0; i < len(f); i++ {
zi := 0
if i%2 == 0 {
zi = 1
}
t, err := atoi(f[i])
if err != nil {
return nil, badData
}
t -= zones[0].offset
tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
}
 
// Committed to succeed.
l = &Location{zone: zones[:], 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) (*Location, error) {
b, err := readFile(name)
if err != nil {
return nil, err
}
return loadZoneData(string(b))
}
 
func initTestingZone() {
if z, err := loadZoneFile("/adm/timezone/US_Pacific"); err == nil {
localLoc = *z
return
}
 
// Fall back to UTC.
localLoc.name = "UTC"
}
 
func initLocal() {
t, ok := syscall.Getenv("timezone")
if ok {
if z, err := loadZoneData(t); err == nil {
localLoc = *z
return
}
} else {
if z, err := loadZoneFile("/adm/timezone/local"); err == nil {
localLoc = *z
localLoc.name = "Local"
return
}
}
 
// Fall back to UTC.
localLoc.name = "UTC"
}
 
func loadLocation(name string) (*Location, error) {
if z, err := loadZoneFile("/adm/timezone/" + name); err == nil {
return z, nil
}
return nil, errors.New("unknown time zone " + name)
}
/time_test.go
0,0 → 1,941
// 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 (
"bytes"
"encoding/gob"
"encoding/json"
"math/rand"
"strconv"
"strings"
"testing"
"testing/quick"
. "time"
)
 
// We should be in PST/PDT, but if the time zone files are missing we
// won't be. The purpose of this test is to at least explain why some of
// the subsequent tests fail.
func TestZoneData(t *testing.T) {
lt := Now()
// PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique.
if name, off := lt.Zone(); off != -8*60*60 && off != -7*60*60 {
t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", name, off)
t.Error("Likely problem: the time zone files have not been installed.")
}
}
 
// parsedTime is the struct representing a parsed time value.
type parsedTime struct {
Year int
Month Month
Day int
Hour, Minute, Second int // 15:04:05 is 15, 4, 5.
Nanosecond int // Fractional second.
Weekday Weekday
ZoneOffset int // seconds east of UTC, e.g. -7*60*60 for -0700
Zone string // e.g., "MST"
}
 
type TimeTest struct {
seconds int64
golden parsedTime
}
 
var utctests = []TimeTest{
{0, parsedTime{1970, January, 1, 0, 0, 0, 0, Thursday, 0, "UTC"}},
{1221681866, parsedTime{2008, September, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}},
{-1221681866, parsedTime{1931, April, 16, 3, 55, 34, 0, Thursday, 0, "UTC"}},
{-11644473600, parsedTime{1601, January, 1, 0, 0, 0, 0, Monday, 0, "UTC"}},
{599529660, parsedTime{1988, December, 31, 0, 1, 0, 0, Saturday, 0, "UTC"}},
{978220860, parsedTime{2000, December, 31, 0, 1, 0, 0, Sunday, 0, "UTC"}},
}
 
var nanoutctests = []TimeTest{
{0, parsedTime{1970, January, 1, 0, 0, 0, 1e8, Thursday, 0, "UTC"}},
{1221681866, parsedTime{2008, September, 17, 20, 4, 26, 2e8, Wednesday, 0, "UTC"}},
}
 
var localtests = []TimeTest{
{0, parsedTime{1969, December, 31, 16, 0, 0, 0, Wednesday, -8 * 60 * 60, "PST"}},
{1221681866, parsedTime{2008, September, 17, 13, 4, 26, 0, Wednesday, -7 * 60 * 60, "PDT"}},
}
 
var nanolocaltests = []TimeTest{
{0, parsedTime{1969, December, 31, 16, 0, 0, 1e8, Wednesday, -8 * 60 * 60, "PST"}},
{1221681866, parsedTime{2008, September, 17, 13, 4, 26, 3e8, Wednesday, -7 * 60 * 60, "PDT"}},
}
 
func same(t Time, u *parsedTime) bool {
// Check aggregates.
year, month, day := t.Date()
hour, min, sec := t.Clock()
name, offset := t.Zone()
if year != u.Year || month != u.Month || day != u.Day ||
hour != u.Hour || min != u.Minute || sec != u.Second ||
name != u.Zone || offset != u.ZoneOffset {
return false
}
// Check individual entries.
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.Nanosecond() == u.Nanosecond &&
t.Weekday() == u.Weekday
}
 
func TestSecondsToUTC(t *testing.T) {
for _, test := range utctests {
sec := test.seconds
golden := &test.golden
tm := Unix(sec, 0).UTC()
newsec := tm.Unix()
if newsec != sec {
t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec)
}
if !same(tm, golden) {
t.Errorf("SecondsToUTC(%d): // %#v", sec, tm)
t.Errorf(" want=%+v", *golden)
t.Errorf(" have=%v", tm.Format(RFC3339+" MST"))
}
}
}
 
func TestNanosecondsToUTC(t *testing.T) {
for _, test := range nanoutctests {
golden := &test.golden
nsec := test.seconds*1e9 + int64(golden.Nanosecond)
tm := Unix(0, nsec).UTC()
newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond())
if newnsec != nsec {
t.Errorf("NanosecondsToUTC(%d).Nanoseconds() = %d", nsec, newnsec)
}
if !same(tm, golden) {
t.Errorf("NanosecondsToUTC(%d):", nsec)
t.Errorf(" want=%+v", *golden)
t.Errorf(" have=%+v", tm.Format(RFC3339+" MST"))
}
}
}
 
func TestSecondsToLocalTime(t *testing.T) {
for _, test := range localtests {
sec := test.seconds
golden := &test.golden
tm := Unix(sec, 0)
newsec := tm.Unix()
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.Format(RFC3339+" MST"))
}
}
}
 
func TestNanosecondsToLocalTime(t *testing.T) {
for _, test := range nanolocaltests {
golden := &test.golden
nsec := test.seconds*1e9 + int64(golden.Nanosecond)
tm := Unix(0, nsec)
newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond())
if newnsec != nsec {
t.Errorf("NanosecondsToLocalTime(%d).Seconds() = %d", nsec, newnsec)
}
if !same(tm, golden) {
t.Errorf("NanosecondsToLocalTime(%d):", nsec)
t.Errorf(" want=%+v", *golden)
t.Errorf(" have=%+v", tm.Format(RFC3339+" MST"))
}
}
}
 
func TestSecondsToUTCAndBack(t *testing.T) {
f := func(sec int64) bool { return Unix(sec, 0).UTC().Unix() == 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)
}
}
 
func TestNanosecondsToUTCAndBack(t *testing.T) {
f := func(nsec int64) bool {
t := Unix(0, nsec).UTC()
ns := t.Unix()*1e9 + int64(t.Nanosecond())
return ns == nsec
}
f32 := func(nsec int32) bool { return f(int64(nsec)) }
cfg := &quick.Config{MaxCount: 10000}
 
// Try a small date first, then the large ones. (The span is only a few hundred years
// for nanoseconds in an int64.)
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{
{Date(2008, 9, 17, 20, 4, 26, 0, UTC), "2008-09-17T20:04:26Z"},
{Date(1994, 9, 17, 20, 4, 26, 0, FixedZone("EST", -18000)), "1994-09-17T20:04:26-05:00"},
{Date(2000, 12, 26, 1, 15, 6, 0, FixedZone("OTO", 15600)), "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, "Wed Feb 4 21:00:57 2009"},
{"UnixDate", UnixDate, "Wed Feb 4 21:00:57 PST 2009"},
{"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"},
{"RFC822", RFC822, "04 Feb 09 2100 PST"},
{"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"},
{"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"},
{"Kitchen", Kitchen, "9:00PM"},
{"am/pm", "3pm", "9pm"},
{"AM/PM", "3PM", "9PM"},
{"two-digit year", "06 01 02", "09 02 04"},
// Time stamps, Fractional seconds.
{"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"},
}
 
func TestFormat(t *testing.T) {
// The numeric time represents Thu Feb 4 21:00:57.012345678 PST 2010
time := Unix(0, 1233810057012345678)
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 int // sign of year
fracDigits int // number of digits of fractional second
}
 
var parseTests = []ParseTest{
{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
{"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010", true, true, 1, 0},
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1, 0},
{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0},
{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57 -0800", true, true, 1, 0},
{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0},
{"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, 0},
// Optional fractional seconds.
{"ANSIC", ANSIC, "Thu Feb 4 21:00:57.0 2010", false, true, 1, 1},
{"UnixDate", UnixDate, "Thu Feb 4 21:00:57.01 PST 2010", true, true, 1, 2},
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57.012 -0800 2010", true, true, 1, 3},
{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57.0123 PST", true, true, 1, 4},
{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57.01234 PST", true, true, 1, 5},
{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57.01234 -0800", true, true, 1, 5},
{"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9},
// Amount of white space should not matter.
{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
// Case should not matter
{"ANSIC", ANSIC, "THU FEB 4 21:00:57 2010", false, true, 1, 0},
{"ANSIC", ANSIC, "thu feb 4 21:00:57 2010", false, true, 1, 0},
// Fractional seconds.
{"millisecond", "Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3},
{"microsecond", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6},
{"nanosecond", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9},
// Leading zeros in other places should not be taken as fractional seconds.
{"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
{"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2},
}
 
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, 0},
// 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, 0},
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +0000 2010", false, true, 1, 0},
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +1130 2010", false, true, 1, 0},
}
 
// 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() != February {
t.Errorf("%s: bad month: %s not %s", test.name, time.Month(), February)
}
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)
}
// Nanoseconds must be checked against the precision of the input.
nanosec, err := strconv.ParseUint("012345678"[:test.fracDigits]+"000000000"[:9-test.fracDigits], 10, 0)
if err != nil {
panic(err)
}
if time.Nanosecond() != int(nanosec) {
t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond(), nanosec)
}
name, offset := time.Zone()
if test.hasTZ && offset != -28800 {
t.Errorf("%s: bad tz offset: %s %d not %d", test.name, name, offset, -28800)
}
if test.hasWD && time.Weekday() != Thursday {
t.Errorf("%s: bad weekday: %s not %s", test.name, time.Weekday(), Thursday)
}
}
 
func TestFormatAndParse(t *testing.T) {
const fmt = "Mon MST " + RFC3339 // all fields
f := func(sec int64) bool {
t1 := Unix(sec, 0)
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 t1.Unix() != t2.Unix() || t1.Nanosecond() != t2.Nanosecond() {
t.Errorf("FormatAndParse %d: %q(%d) %q(%d)", sec, t1, t1.Unix(), t2, t2.Unix())
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", "cannot parse"}, // cannot parse Feb as Mon
{ANSIC, "Thu Feb 4 21:00:57 @2010", "cannot 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"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59x01 2010", "cannot parse"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.xxx 2010", "cannot parse"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second 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.Error(), test.expect) < 0 {
t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err)
}
}
}
 
func TestNoonIs12PM(t *testing.T) {
noon := Date(0, January, 1, 12, 0, 0, 0, UTC)
const expect = "12:00PM"
got := noon.Format("3:04PM")
if got != expect {
t.Errorf("got %q; expect %q", got, expect)
}
got = noon.Format("03:04PM")
if got != expect {
t.Errorf("got %q; expect %q", got, expect)
}
}
 
func TestMidnightIs12AM(t *testing.T) {
midnight := Date(0, January, 1, 0, 0, 0, 0, UTC)
expect := "12:00AM"
got := midnight.Format("3:04PM")
if got != expect {
t.Errorf("got %q; expect %q", got, expect)
}
got = midnight.Format("03:04PM")
if got != expect {
t.Errorf("got %q; expect %q", got, expect)
}
}
 
func Test12PMIsNoon(t *testing.T) {
noon, err := Parse("3:04PM", "12:00PM")
if err != nil {
t.Fatal("error parsing date:", err)
}
if noon.Hour() != 12 {
t.Errorf("got %d; expect 12", noon.Hour())
}
noon, err = Parse("03:04PM", "12:00PM")
if err != nil {
t.Fatal("error parsing date:", err)
}
if noon.Hour() != 12 {
t.Errorf("got %d; expect 12", noon.Hour())
}
}
 
func Test12AMIsMidnight(t *testing.T) {
midnight, err := Parse("3:04PM", "12:00AM")
if err != nil {
t.Fatal("error parsing date:", err)
}
if midnight.Hour() != 0 {
t.Errorf("got %d; expect 0", midnight.Hour())
}
midnight, err = Parse("03:04PM", "12:00AM")
if err != nil {
t.Fatal("error parsing date:", err)
}
if midnight.Hour() != 0 {
t.Errorf("got %d; expect 0", midnight.Hour())
}
}
 
// 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, "Thu Feb 02 16:10:03 -0500 2006")
if err != nil {
t.Fatal("error parsing date:", err)
}
expect := "Thu 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("got %s; expect %s", str, expect)
}
}
 
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
_, offset := time.Zone()
if offset != expected {
t.Errorf("ZoneOffset = %d, want %d", offset, expected)
}
}
 
type ISOWeekTest struct {
year int // year
month, day int // month and day
yex int // expected year
wex int // expected week
}
 
var isoWeekTests = []ISOWeekTest{
{1981, 1, 1, 1981, 1}, {1982, 1, 1, 1981, 53}, {1983, 1, 1, 1982, 52},
{1984, 1, 1, 1983, 52}, {1985, 1, 1, 1985, 1}, {1986, 1, 1, 1986, 1},
{1987, 1, 1, 1987, 1}, {1988, 1, 1, 1987, 53}, {1989, 1, 1, 1988, 52},
{1990, 1, 1, 1990, 1}, {1991, 1, 1, 1991, 1}, {1992, 1, 1, 1992, 1},
{1993, 1, 1, 1992, 53}, {1994, 1, 1, 1993, 52}, {1995, 1, 2, 1995, 1},
{1996, 1, 1, 1996, 1}, {1996, 1, 7, 1996, 1}, {1996, 1, 8, 1996, 2},
{1997, 1, 1, 1997, 1}, {1998, 1, 1, 1998, 1}, {1999, 1, 1, 1998, 53},
{2000, 1, 1, 1999, 52}, {2001, 1, 1, 2001, 1}, {2002, 1, 1, 2002, 1},
{2003, 1, 1, 2003, 1}, {2004, 1, 1, 2004, 1}, {2005, 1, 1, 2004, 53},
{2006, 1, 1, 2005, 52}, {2007, 1, 1, 2007, 1}, {2008, 1, 1, 2008, 1},
{2009, 1, 1, 2009, 1}, {2010, 1, 1, 2009, 53}, {2010, 1, 1, 2009, 53},
{2011, 1, 1, 2010, 52}, {2011, 1, 2, 2010, 52}, {2011, 1, 3, 2011, 1},
{2011, 1, 4, 2011, 1}, {2011, 1, 5, 2011, 1}, {2011, 1, 6, 2011, 1},
{2011, 1, 7, 2011, 1}, {2011, 1, 8, 2011, 1}, {2011, 1, 9, 2011, 1},
{2011, 1, 10, 2011, 2}, {2011, 1, 11, 2011, 2}, {2011, 6, 12, 2011, 23},
{2011, 6, 13, 2011, 24}, {2011, 12, 25, 2011, 51}, {2011, 12, 26, 2011, 52},
{2011, 12, 27, 2011, 52}, {2011, 12, 28, 2011, 52}, {2011, 12, 29, 2011, 52},
{2011, 12, 30, 2011, 52}, {2011, 12, 31, 2011, 52}, {1995, 1, 1, 1994, 52},
{2012, 1, 1, 2011, 52}, {2012, 1, 2, 2012, 1}, {2012, 1, 8, 2012, 1},
{2012, 1, 9, 2012, 2}, {2012, 12, 23, 2012, 51}, {2012, 12, 24, 2012, 52},
{2012, 12, 30, 2012, 52}, {2012, 12, 31, 2013, 1}, {2013, 1, 1, 2013, 1},
{2013, 1, 6, 2013, 1}, {2013, 1, 7, 2013, 2}, {2013, 12, 22, 2013, 51},
{2013, 12, 23, 2013, 52}, {2013, 12, 29, 2013, 52}, {2013, 12, 30, 2014, 1},
{2014, 1, 1, 2014, 1}, {2014, 1, 5, 2014, 1}, {2014, 1, 6, 2014, 2},
{2015, 1, 1, 2015, 1}, {2016, 1, 1, 2015, 53}, {2017, 1, 1, 2016, 52},
{2018, 1, 1, 2018, 1}, {2019, 1, 1, 2019, 1}, {2020, 1, 1, 2020, 1},
{2021, 1, 1, 2020, 53}, {2022, 1, 1, 2021, 52}, {2023, 1, 1, 2022, 52},
{2024, 1, 1, 2024, 1}, {2025, 1, 1, 2025, 1}, {2026, 1, 1, 2026, 1},
{2027, 1, 1, 2026, 53}, {2028, 1, 1, 2027, 52}, {2029, 1, 1, 2029, 1},
{2030, 1, 1, 2030, 1}, {2031, 1, 1, 2031, 1}, {2032, 1, 1, 2032, 1},
{2033, 1, 1, 2032, 53}, {2034, 1, 1, 2033, 52}, {2035, 1, 1, 2035, 1},
{2036, 1, 1, 2036, 1}, {2037, 1, 1, 2037, 1}, {2038, 1, 1, 2037, 53},
{2039, 1, 1, 2038, 52}, {2040, 1, 1, 2039, 52},
}
 
func TestISOWeek(t *testing.T) {
// Selected dates and corner cases
for _, wt := range isoWeekTests {
dt := Date(wt.year, Month(wt.month), wt.day, 0, 0, 0, 0, UTC)
y, w := dt.ISOWeek()
if w != wt.wex || y != wt.yex {
t.Errorf("got %d/%d; expected %d/%d for %d-%02d-%02d",
y, w, wt.yex, wt.wex, wt.year, wt.month, wt.day)
}
}
 
// The only real invariant: Jan 04 is in week 1
for year := 1950; year < 2100; year++ {
if y, w := Date(year, January, 4, 0, 0, 0, 0, UTC).ISOWeek(); y != year || w != 1 {
t.Errorf("got %d/%d; expected %d/1 for Jan 04", y, w, year)
}
}
}
 
var durationTests = []struct {
str string
d Duration
}{
{"0", 0},
{"1ns", 1 * Nanosecond},
{"1.1us", 1100 * Nanosecond},
{"2.2ms", 2200 * Microsecond},
{"3.3s", 3300 * Millisecond},
{"4m5s", 4*Minute + 5*Second},
{"4m5.001s", 4*Minute + 5001*Millisecond},
{"5h6m7.001s", 5*Hour + 6*Minute + 7001*Millisecond},
{"8m0.000000001s", 8*Minute + 1*Nanosecond},
{"2562047h47m16.854775807s", 1<<63 - 1},
{"-2562047h47m16.854775808s", -1 << 63},
}
 
func TestDurationString(t *testing.T) {
for _, tt := range durationTests {
if str := tt.d.String(); str != tt.str {
t.Errorf("Duration(%d).String() = %s, want %s", int64(tt.d), str, tt.str)
}
if tt.d > 0 {
if str := (-tt.d).String(); str != "-"+tt.str {
t.Errorf("Duration(%d).String() = %s, want %s", int64(-tt.d), str, "-"+tt.str)
}
}
}
}
 
var dateTests = []struct {
year, month, day, hour, min, sec, nsec int
z *Location
unix int64
}{
{2011, 11, 6, 1, 0, 0, 0, Local, 1320566400}, // 1:00:00 PDT
{2011, 11, 6, 1, 59, 59, 0, Local, 1320569999}, // 1:59:59 PDT
{2011, 11, 6, 2, 0, 0, 0, Local, 1320573600}, // 2:00:00 PST
 
{2011, 3, 13, 1, 0, 0, 0, Local, 1300006800}, // 1:00:00 PST
{2011, 3, 13, 1, 59, 59, 0, Local, 1300010399}, // 1:59:59 PST
{2011, 3, 13, 3, 0, 0, 0, Local, 1300010400}, // 3:00:00 PDT
{2011, 3, 13, 2, 30, 0, 0, Local, 1300008600}, // 2:30:00 PDT ≡ 1:30 PST
 
// Many names for Fri Nov 18 7:56:35 PST 2011
{2011, 11, 18, 7, 56, 35, 0, Local, 1321631795}, // Nov 18 7:56:35
{2011, 11, 19, -17, 56, 35, 0, Local, 1321631795}, // Nov 19 -17:56:35
{2011, 11, 17, 31, 56, 35, 0, Local, 1321631795}, // Nov 17 31:56:35
{2011, 11, 18, 6, 116, 35, 0, Local, 1321631795}, // Nov 18 6:116:35
{2011, 10, 49, 7, 56, 35, 0, Local, 1321631795}, // Oct 49 7:56:35
{2011, 11, 18, 7, 55, 95, 0, Local, 1321631795}, // Nov 18 7:55:95
{2011, 11, 18, 7, 56, 34, 1e9, Local, 1321631795}, // Nov 18 7:56:34 + 10⁹ns
{2011, 12, -12, 7, 56, 35, 0, Local, 1321631795}, // Dec -21 7:56:35
{2012, 1, -43, 7, 56, 35, 0, Local, 1321631795}, // Jan -52 7:56:35 2012
{2012, int(January - 2), 18, 7, 56, 35, 0, Local, 1321631795}, // (Jan-2) 18 7:56:35 2012
{2010, int(December + 11), 18, 7, 56, 35, 0, Local, 1321631795}, // (Dec+11) 18 7:56:35 2010
}
 
func TestDate(t *testing.T) {
for _, tt := range dateTests {
time := Date(tt.year, Month(tt.month), tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z)
want := Unix(tt.unix, 0)
if !time.Equal(want) {
t.Errorf("Date(%d, %d, %d, %d, %d, %d, %d, %s) = %v, want %v",
tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z,
time, want)
}
}
}
 
// Several ways of getting from
// Fri Nov 18 7:56:35 PST 2011
// to
// Thu Mar 19 7:56:35 PST 2016
var addDateTests = []struct {
years, months, days int
}{
{4, 4, 1},
{3, 16, 1},
{3, 15, 30},
{5, -6, -18 - 30 - 12},
}
 
func TestAddDate(t *testing.T) {
t0 := Date(2011, 11, 18, 7, 56, 35, 0, UTC)
t1 := Date(2016, 3, 19, 7, 56, 35, 0, UTC)
for _, at := range addDateTests {
time := t0.AddDate(at.years, at.months, at.days)
if !time.Equal(t1) {
t.Errorf("AddDate(%d, %d, %d) = %v, want %v",
at.years, at.months, at.days,
time, t1)
}
}
}
 
var daysInTests = []struct {
year, month, di int
}{
{2011, 1, 31}, // January, first month, 31 days
{2011, 2, 28}, // February, non-leap year, 28 days
{2012, 2, 29}, // February, leap year, 29 days
{2011, 6, 30}, // June, 30 days
{2011, 12, 31}, // December, last month, 31 days
}
 
func TestDaysIn(t *testing.T) {
// The daysIn function is not exported.
// Test the daysIn function via the `var DaysIn = daysIn`
// statement in the internal_test.go file.
for _, tt := range daysInTests {
di := DaysIn(Month(tt.month), tt.year)
if di != tt.di {
t.Errorf("got %d; expected %d for %d-%02d",
di, tt.di, tt.year, tt.month)
}
}
}
 
func TestAddToExactSecond(t *testing.T) {
// Add an amount to the current time to round it up to the next exact second.
// This test checks that the nsec field still lies within the range [0, 999999999].
t1 := Now()
t2 := t1.Add(Second - Duration(t1.Nanosecond()))
sec := (t1.Second() + 1) % 60
if t2.Second() != sec || t2.Nanosecond() != 0 {
t.Errorf("sec = %d, nsec = %d, want sec = %d, nsec = 0", t2.Second(), t2.Nanosecond(), sec)
}
}
 
func equalTimeAndZone(a, b Time) bool {
aname, aoffset := a.Zone()
bname, boffset := b.Zone()
return a.Equal(b) && aoffset == boffset && aname == bname
}
 
var gobTests = []Time{
Date(0, 1, 2, 3, 4, 5, 6, UTC),
Date(7, 8, 9, 10, 11, 12, 13, FixedZone("", 0)),
Unix(81985467080890095, 0x76543210), // Time.sec: 0x0123456789ABCDEF
Time{}, // nil location
Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", 32767*60)),
Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", -32768*60)),
}
 
func TestTimeGob(t *testing.T) {
var b bytes.Buffer
enc := gob.NewEncoder(&b)
dec := gob.NewDecoder(&b)
for _, tt := range gobTests {
var gobtt Time
if err := enc.Encode(&tt); err != nil {
t.Errorf("%v gob Encode error = %q, want nil", tt, err)
} else if err := dec.Decode(&gobtt); err != nil {
t.Errorf("%v gob Decode error = %q, want nil", tt, err)
} else if !equalTimeAndZone(gobtt, tt) {
t.Errorf("Decoded time = %v, want %v", gobtt, tt)
}
b.Reset()
}
}
 
var invalidEncodingTests = []struct {
bytes []byte
want string
}{
{[]byte{}, "Time.GobDecode: no data"},
{[]byte{0, 2, 3}, "Time.GobDecode: unsupported version"},
{[]byte{1, 2, 3}, "Time.GobDecode: invalid length"},
}
 
func TestInvalidTimeGob(t *testing.T) {
for _, tt := range invalidEncodingTests {
var ignored Time
err := ignored.GobDecode(tt.bytes)
if err == nil || err.Error() != tt.want {
t.Errorf("time.GobDecode(%#v) error = %v, want %v", tt.bytes, err, tt.want)
}
}
}
 
var notEncodableTimes = []struct {
time Time
want string
}{
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.GobEncode: zone offset has fractional minute"},
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.GobEncode: unexpected zone offset"},
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.GobEncode: unexpected zone offset"},
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.GobEncode: unexpected zone offset"},
}
 
func TestNotGobEncodableTime(t *testing.T) {
for _, tt := range notEncodableTimes {
_, err := tt.time.GobEncode()
if err == nil || err.Error() != tt.want {
t.Errorf("%v GobEncode error = %v, want %v", tt.time, err, tt.want)
}
}
}
 
var jsonTests = []struct {
time Time
json string
}{
{Date(9999, 4, 12, 23, 20, 50, .52*1e9, UTC), `"9999-04-12T23:20:50.52Z"`},
{Date(1996, 12, 19, 16, 39, 57, 0, Local), `"1996-12-19T16:39:57-08:00"`},
{Date(0, 1, 1, 0, 0, 0, 1, FixedZone("", 1*60)), `"0000-01-01T00:00:00.000000001+00:01"`},
}
 
func TestTimeJSON(t *testing.T) {
for _, tt := range jsonTests {
var jsonTime Time
 
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)
} 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) {
t.Errorf("Unmarshaled time = %v, want %v", jsonTime, tt.time)
}
}
}
 
func TestInvalidTimeJSON(t *testing.T) {
var tt Time
err := json.Unmarshal([]byte(`{"now is the time":"buddy"}`), &tt)
_, isParseErr := err.(*ParseError)
if !isParseErr {
t.Errorf("expected *time.ParseError unmarshaling JSON, got %v", err)
}
}
 
var notJSONEncodableTimes = []struct {
time Time
want string
}{
{Date(10000, 1, 1, 0, 0, 0, 0, UTC), "Time.MarshalJSON: year outside of range [0,9999]"},
{Date(-1, 1, 1, 0, 0, 0, 0, UTC), "Time.MarshalJSON: year outside of range [0,9999]"},
}
 
func TestNotJSONEncodableTime(t *testing.T) {
for _, tt := range notJSONEncodableTimes {
_, err := tt.time.MarshalJSON()
if err == nil || err.Error() != tt.want {
t.Errorf("%v MarshalJSON error = %v, want %v", tt.time, err, tt.want)
}
}
}
 
var parseDurationTests = []struct {
in string
ok bool
want Duration
}{
// simple
{"0", true, 0},
{"5s", true, 5 * Second},
{"30s", true, 30 * Second},
{"1478s", true, 1478 * Second},
// sign
{"-5s", true, -5 * Second},
{"+5s", true, 5 * Second},
{"-0", true, 0},
{"+0", true, 0},
// decimal
{"5.0s", true, 5 * Second},
{"5.6s", true, 5*Second + 600*Millisecond},
{"5.s", true, 5 * Second},
{".5s", true, 500 * Millisecond},
{"1.0s", true, 1 * Second},
{"1.00s", true, 1 * Second},
{"1.004s", true, 1*Second + 4*Millisecond},
{"1.0040s", true, 1*Second + 4*Millisecond},
{"100.00100s", true, 100*Second + 1*Millisecond},
// different units
{"10ns", true, 10 * Nanosecond},
{"11us", true, 11 * Microsecond},
{"12µs", true, 12 * Microsecond}, // U+00B5
{"12μs", true, 12 * Microsecond}, // U+03BC
{"13ms", true, 13 * Millisecond},
{"14s", true, 14 * Second},
{"15m", true, 15 * Minute},
{"16h", true, 16 * Hour},
// composite durations
{"3h30m", true, 3*Hour + 30*Minute},
{"10.5s4m", true, 4*Minute + 10*Second + 500*Millisecond},
{"-2m3.4s", true, -(2*Minute + 3*Second + 400*Millisecond)},
{"1h2m3s4ms5us6ns", true, 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond},
{"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond},
 
// errors
{"", false, 0},
{"3", false, 0},
{"-", false, 0},
{"s", false, 0},
{".", false, 0},
{"-.", false, 0},
{".s", false, 0},
{"+.s", false, 0},
}
 
func TestParseDuration(t *testing.T) {
for _, tc := range parseDurationTests {
d, err := ParseDuration(tc.in)
if tc.ok && (err != nil || d != tc.want) {
t.Errorf("ParseDuration(%q) = %v, %v, want %v, nil", tc.in, d, err, tc.want)
} else if !tc.ok && err == nil {
t.Errorf("ParseDuration(%q) = _, nil, want _, non-nil", tc.in)
}
}
}
 
func TestParseDurationRoundTrip(t *testing.T) {
for i := 0; i < 100; i++ {
// Resolutions finer than milliseconds will result in
// imprecise round-trips.
d0 := Duration(rand.Int31()) * Millisecond
s := d0.String()
d1, err := ParseDuration(s)
if err != nil || d0 != d1 {
t.Errorf("round-trip failed: %d => %q => %d, %v", d0, s, d1, err)
}
}
}
 
func BenchmarkNow(b *testing.B) {
for i := 0; i < b.N; i++ {
Now()
}
}
 
func BenchmarkFormat(b *testing.B) {
time := Unix(1265346057, 0)
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")
}
}
 
func BenchmarkHour(b *testing.B) {
t := Now()
for i := 0; i < b.N; i++ {
_ = t.Hour()
}
}
 
func BenchmarkSecond(b *testing.B) {
t := Now()
for i := 0; i < b.N; i++ {
_ = t.Second()
}
}
 
func BenchmarkYear(b *testing.B) {
t := Now()
for i := 0; i < b.N; i++ {
_ = t.Year()
}
}
 
func BenchmarkDay(b *testing.B) {
t := Now()
for i := 0; i < b.N; i++ {
_ = t.Day()
}
}
/time.go
0,0 → 1,1010
// 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 provides functionality for measuring and displaying time.
//
// The calendrical calculations always assume a Gregorian calendar.
package time
 
import "errors"
 
// A Time represents an instant in time with nanosecond precision.
//
// Programs using times should typically store and pass them as values,
// not pointers. That is, time variables and struct fields should be of
// type time.Time, not *time.Time.
//
// Time instants can be compared using the Before, After, and Equal methods.
// The Sub method subtracts two instants, producing a Duration.
// The Add method adds a Time and a Duration, producing a Time.
//
// The zero value of type Time is January 1, year 1, 00:00:00.000000000 UTC.
// As this time is unlikely to come up in practice, the IsZero method gives
// a simple way of detecting a time that has not been initialized explicitly.
//
// Each Time has associated with it a Location, consulted when computing the
// presentation form of the time, such as in the Format, Hour, and Year methods.
// The methods Local, UTC, and In return a Time with a specific location.
// Changing the location in this way changes only the presentation; it does not
// change the instant in time being denoted and therefore does not affect the
// computations described in earlier paragraphs.
//
type Time struct {
// sec gives the number of seconds elapsed since
// January 1, year 1 00:00:00 UTC.
sec int64
 
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
nsec int32
 
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// Only the zero Time has a nil Location.
// In that case it is interpreted to mean UTC.
loc *Location
}
 
// After reports whether the time instant t is after u.
func (t Time) After(u Time) bool {
return t.sec > u.sec || t.sec == u.sec && t.nsec > u.nsec
}
 
// Before reports whether the time instant t is before u.
func (t Time) Before(u Time) bool {
return t.sec < u.sec || t.sec == u.sec && t.nsec < u.nsec
}
 
// Equal reports whether t and u represent the same time instant.
// Two times can be equal even if they are in different locations.
// For example, 6:00 +0200 CEST and 4:00 UTC are Equal.
// This comparison is different from using t == u, which also compares
// the locations.
func (t Time) Equal(u Time) bool {
return t.sec == u.sec && t.nsec == u.nsec
}
 
// A Month specifies a month of the year (January = 1, ...).
type Month int
 
const (
January Month = 1 + iota
February
March
April
May
June
July
August
September
October
November
December
)
 
var months = [...]string{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
}
 
// String returns the English name of the month ("January", "February", ...).
func (m Month) String() string { return months[m-1] }
 
// A Weekday specifies a day of the week (Sunday = 0, ...).
type Weekday int
 
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
 
var days = [...]string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
 
// String returns the English name of the day ("Sunday", "Monday", ...).
func (d Weekday) String() string { return days[d] }
 
// Computations on time.
//
// The zero value for a Time is defined to be
// January 1, year 1, 00:00:00.000000000 UTC
// which (1) looks like a zero, or as close as you can get in a date
// (1-1-1 00:00:00 UTC), (2) is unlikely enough to arise in practice to
// be a suitable "not set" sentinel, unlike Jan 1 1970, and (3) has a
// non-negative year even in time zones west of UTC, unlike 1-1-0
// 00:00:00 UTC, which would be 12-31-(-1) 19:00:00 in New York.
//
// The zero Time value does not force a specific epoch for the time
// representation. For example, to use the Unix epoch internally, we
// could define that to distinguish a zero value from Jan 1 1970, that
// time would be represented by sec=-1, nsec=1e9. However, it does
// suggest a representation, namely using 1-1-1 00:00:00 UTC as the
// epoch, and that's what we do.
//
// The Add and Sub computations are oblivious to the choice of epoch.
//
// The presentation computations - year, month, minute, and so on - all
// 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
// 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
// ago that all the times we care about will be positive, and then round
// to zero and round down coincide. These presentation routines already
// have to add the zone offset, so adding the translation to the
// alternate epoch is cheap. For example, having a non-negative time t
// means that we can write
//
// sec = t % 60
//
// instead of
//
// sec = t % 60
// if sec < 0 {
// sec += 60
// }
//
// everywhere.
//
// The calendar runs on an exact 400 year cycle: a 400-year calendar
// printed for 1970-2469 will apply as well to 2470-2869. Even the days
// of the week match up. It simplifies the computations to choose the
// cycle boundaries so that the exceptional years are always delayed as
// long as possible. That means choosing a year equal to 1 mod 400, so
// that the first leap year is the 4th year, the first missed leap year
// is the 100th year, and the missed missed leap year is the 400th year.
// So we'd prefer instead to print a calendar for 2001-2400 and reuse it
// for 2401-2800.
//
// Finally, it's convenient if the delta between the Unix epoch and
// long-ago epoch is representable by an int64 constant.
//
// These three considerations—choose an epoch as early as possible, that
// uses a year equal to 1 mod 400, and that is no more than 2⁶³ seconds
// earlier than 1970—bring us to the year -292277022399. We refer to
// this year as the absolute zero year, and to times measured as a uint64
// seconds since this year as absolute times.
//
// Times measured as an int64 seconds since the year 1—the representation
// used for Time's sec field—are called internal times.
//
// Times measured as an int64 seconds since the year 1970 are called Unix
// times.
//
// It is tempting to just use the year 1 as the absolute epoch, defining
// that the routines are only valid for years >= 1. However, the
// routines would then be invalid when displaying the epoch in time zones
// west of UTC, since it is year 0. It doesn't seem tenable to say that
// printing the zero time correctly isn't supported in half the time
// zones. By comparison, it's reasonable to mishandle some times in
// the year -292277022399.
//
// All this is opaque to clients of the API and can be changed if a
// better implementation presents itself.
 
const (
// The unsigned zero year for internal calculations.
// Must be 1 mod 400, and times before it will not compute correctly,
// but otherwise can be changed at will.
absoluteZeroYear = -292277022399
 
// The year of the zero Time.
// Assumed by the unixToInternal computation below.
internalYear = 1
 
// The year of the zero Unix time.
unixYear = 1970
 
// Offsets to convert between internal and absolute or Unix times.
absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay
internalToAbsolute = -absoluteToInternal
 
unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay
internalToUnix int64 = -unixToInternal
)
 
// IsZero reports whether t represents the zero time instant,
// January 1, year 1, 00:00:00 UTC.
func (t Time) IsZero() bool {
return t.sec == 0 && t.nsec == 0
}
 
// abs returns the time t as an absolute time, adjusted by the zone offset.
// It is called when computing a presentation property like Month or Hour.
func (t Time) abs() uint64 {
l := t.loc
if l == nil {
l = &utcLoc
}
// Avoid function call if we hit the local time cache.
sec := t.sec + internalToUnix
if l != &utcLoc {
if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
sec += int64(l.cacheZone.offset)
} else {
_, offset, _, _, _ := l.lookup(sec)
sec += int64(offset)
}
}
return uint64(sec + (unixToInternal + internalToAbsolute))
}
 
// Date returns the year, month, and day in which t occurs.
func (t Time) Date() (year int, month Month, day int) {
year, month, day, _ = t.date(true)
return
}
 
// Year returns the year in which t occurs.
func (t Time) Year() int {
year, _, _, _ := t.date(false)
return year
}
 
// Month returns the month of the year specified by t.
func (t Time) Month() Month {
_, month, _, _ := t.date(true)
return month
}
 
// Day returns the day of the month specified by t.
func (t Time) Day() int {
_, _, day, _ := t.date(true)
return day
}
 
// Weekday returns the day of the week specified by t.
func (t Time) Weekday() Weekday {
// January 1 of the absolute year, like January 1 of 2001, was a Monday.
sec := (t.abs() + uint64(Monday)*secondsPerDay) % secondsPerWeek
return Weekday(int(sec) / secondsPerDay)
}
 
// ISOWeek returns the ISO 8601 year and week number in which t occurs.
// Week ranges from 1 to 53. Jan 01 to Jan 03 of year n might belong to
// week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1
// of year n+1.
func (t Time) ISOWeek() (year, week int) {
year, month, day, yday := t.date(true)
wday := int(t.Weekday()+6) % 7 // weekday but Monday = 0.
const (
Mon int = iota
Tue
Wed
Thu
Fri
Sat
Sun
)
 
// Calculate week as number of Mondays in year up to
// and including today, plus 1 because the first week is week 0.
// Putting the + 1 inside the numerator as a + 7 keeps the
// numerator from being negative, which would cause it to
// round incorrectly.
week = (yday - wday + 7) / 7
 
// The week number is now correct under the assumption
// that the first Monday of the year is in week 1.
// If Jan 1 is a Tuesday, Wednesday, or Thursday, the first Monday
// is actually in week 2.
jan1wday := (wday - yday + 7*53) % 7
if Tue <= jan1wday && jan1wday <= Thu {
week++
}
 
// If the week number is still 0, we're in early January but in
// the last week of last year.
if week == 0 {
year--
week = 52
// A year has 53 weeks when Jan 1 or Dec 31 is a Thursday,
// meaning Jan 1 of the next year is a Friday
// or it was a leap year and Jan 1 of the next year is a Saturday.
if jan1wday == Fri || (jan1wday == Sat && isLeap(year)) {
week++
}
}
 
// December 29 to 31 are in week 1 of next year if
// they are after the last Thursday of the year and
// December 31 is a Monday, Tuesday, or Wednesday.
if month == December && day >= 29 && wday < Thu {
if dec31wday := (wday + 31 - day) % 7; Mon <= dec31wday && dec31wday <= Wed {
year++
week = 1
}
}
 
return
}
 
// Clock returns the hour, minute, and second within the day specified by t.
func (t Time) Clock() (hour, min, sec int) {
sec = int(t.abs() % secondsPerDay)
hour = sec / secondsPerHour
sec -= hour * secondsPerHour
min = sec / secondsPerMinute
sec -= min * secondsPerMinute
return
}
 
// Hour returns the hour within the day specified by t, in the range [0, 23].
func (t Time) Hour() int {
return int(t.abs()%secondsPerDay) / secondsPerHour
}
 
// Minute returns the minute offset within the hour specified by t, in the range [0, 59].
func (t Time) Minute() int {
return int(t.abs()%secondsPerHour) / secondsPerMinute
}
 
// Second returns the second offset within the minute specified by t, in the range [0, 59].
func (t Time) Second() int {
return int(t.abs() % secondsPerMinute)
}
 
// Nanosecond returns the nanosecond offset within the second specified by t,
// in the range [0, 999999999].
func (t Time) Nanosecond() int {
return int(t.nsec)
}
 
// A Duration represents the elapsed time between two instants
// as an int64 nanosecond count. The representation limits the
// largest representable duration to approximately 290 years.
type Duration int64
 
// Common durations. There is no definition for units of Day or larger
// to avoid confusion across daylight savings time zone transitions.
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
 
// Duration returns a string representing the duration in the form "72h3m0.5s".
// Leading zero units are omitted. As a special case, durations less than one
// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure
// that the leading digit is non-zero. The zero duration formats as 0,
// with no unit.
func (d Duration) String() string {
// Largest time is 2540400h10m10.000000000s
var buf [32]byte
w := len(buf)
 
u := uint64(d)
neg := d < 0
if neg {
u = -u
}
 
if u < uint64(Second) {
// Special case: if duration is smaller than a second,
// use smaller units, like 1.2ms
var (
prec int
unit byte
)
switch {
case u == 0:
return "0"
case u < uint64(Microsecond):
// print nanoseconds
prec = 0
unit = 'n'
case u < uint64(Millisecond):
// print microseconds
prec = 3
unit = 'u'
default:
// print milliseconds
prec = 6
unit = 'm'
}
w -= 2
buf[w] = unit
buf[w+1] = 's'
w, u = fmtFrac(buf[:w], u, prec)
w = fmtInt(buf[:w], u)
} else {
w--
buf[w] = 's'
 
w, u = fmtFrac(buf[:w], u, 9)
 
// u is now integer seconds
w = fmtInt(buf[:w], u%60)
u /= 60
 
// u is now integer minutes
if u > 0 {
w--
buf[w] = 'm'
w = fmtInt(buf[:w], u%60)
u /= 60
 
// u is now integer hours
// Stop at hours because days can be different lengths.
if u > 0 {
w--
buf[w] = 'h'
w = fmtInt(buf[:w], u)
}
}
}
 
if neg {
w--
buf[w] = '-'
}
 
return string(buf[w:])
}
 
// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
// tail of buf, omitting trailing zeros. it omits the decimal
// point too when the fraction is 0. It returns the index where the
// output bytes begin and the value v/10**prec.
func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
// Omit trailing zeros up to and including decimal point.
w := len(buf)
print := false
for i := 0; i < prec; i++ {
digit := v % 10
print = print || digit != 0
if print {
w--
buf[w] = byte(digit) + '0'
}
v /= 10
}
if print {
w--
buf[w] = '.'
}
return w, v
}
 
// fmtInt formats v into the tail of buf.
// It returns the index where the output begins.
func fmtInt(buf []byte, v uint64) int {
w := len(buf)
if v == 0 {
w--
buf[w] = '0'
} else {
for v > 0 {
w--
buf[w] = byte(v%10) + '0'
v /= 10
}
}
return w
}
 
// Nanoseconds returns the duration as an integer nanosecond count.
func (d Duration) Nanoseconds() int64 { return int64(d) }
 
// These methods return float64 because the dominant
// use case is for printing a floating point number like 1.5s, and
// a truncation to integer would make them not useful in those cases.
// Splitting the integer and fraction ourselves guarantees that
// converting the returned float64 to an integer rounds the same
// way that a pure integer conversion would have, even in cases
// where, say, float64(d.Nanoseconds())/1e9 would have rounded
// differently.
 
// Seconds returns the duration as a floating point number of seconds.
func (d Duration) Seconds() float64 {
sec := d / Second
nsec := d % Second
return float64(sec) + float64(nsec)*1e-9
}
 
// Minutes returns the duration as a floating point number of minutes.
func (d Duration) Minutes() float64 {
min := d / Minute
nsec := d % Minute
return float64(min) + float64(nsec)*(1e-9/60)
}
 
// Hours returns the duration as a floating point number of hours.
func (d Duration) Hours() float64 {
hour := d / Hour
nsec := d % Hour
return float64(hour) + float64(nsec)*(1e-9/60/60)
}
 
// Add returns the time t+d.
func (t Time) Add(d Duration) Time {
t.sec += int64(d / 1e9)
t.nsec += int32(d % 1e9)
if t.nsec >= 1e9 {
t.sec++
t.nsec -= 1e9
} else if t.nsec < 0 {
t.sec--
t.nsec += 1e9
}
return t
}
 
// Sub returns the duration t-u.
// To compute t-d for a duration d, use t.Add(-d).
func (t Time) Sub(u Time) Duration {
return Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec)
}
 
// Since returns the time elapsed since t.
// It is shorthand for time.Now().Sub(t).
func Since(t Time) Duration {
return Now().Sub(t)
}
 
// AddDate returns the time corresponding to adding the
// given number of years, months, and days to t.
// For example, AddDate(-1, 2, 3) applied to January 1, 2011
// returns March 4, 2010.
//
// AddDate normalizes its result in the same way that Date does,
// so, for example, adding one month to October 31 yields
// December 1, the normalized form for November 31.
func (t Time) AddDate(years int, months int, days int) Time {
year, month, day := t.Date()
hour, min, sec := t.Clock()
return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec), t.loc)
}
 
const (
secondsPerMinute = 60
secondsPerHour = 60 * 60
secondsPerDay = 24 * secondsPerHour
secondsPerWeek = 7 * secondsPerDay
daysPer400Years = 365*400 + 97
daysPer100Years = 365*100 + 24
daysPer4Years = 365*4 + 1
days1970To2001 = 31*365 + 8
)
 
// date computes the year and, only when full=true,
// the month and day in which t occurs.
func (t Time) date(full bool) (year int, month Month, day int, yday int) {
// Split into time and day.
d := t.abs() / secondsPerDay
 
// Account for 400 year cycles.
n := d / daysPer400Years
y := 400 * n
d -= daysPer400Years * n
 
// Cut off 100-year cycles.
// The last cycle has one extra leap year, so on the last day
// of that year, day / daysPer100Years will be 4 instead of 3.
// Cut it back down to 3 by subtracting n>>2.
n = d / daysPer100Years
n -= n >> 2
y += 100 * n
d -= daysPer100Years * n
 
// Cut off 4-year cycles.
// The last cycle has a missing leap year, which does not
// affect the computation.
n = d / daysPer4Years
y += 4 * n
d -= daysPer4Years * n
 
// Cut off years within a 4-year cycle.
// The last year is a leap year, so on the last day of that year,
// day / 365 will be 4 instead of 3. Cut it back down to 3
// by subtracting n>>2.
n = d / 365
n -= n >> 2
y += n
d -= 365 * n
 
year = int(int64(y) + absoluteZeroYear)
yday = int(d)
 
if !full {
return
}
 
day = yday
if isLeap(year) {
// Leap year
switch {
case day > 31+29-1:
// After leap day; pretend it wasn't there.
day--
case day == 31+29-1:
// Leap day.
month = February
day = 29
return
}
}
 
// Estimate month on assumption that every month has 31 days.
// The estimate may be too low by at most one month, so adjust.
month = Month(day / 31)
end := int(daysBefore[month+1])
var begin int
if day >= end {
month++
begin = end
} else {
begin = int(daysBefore[month])
}
 
month++ // because January is 1
day = day - begin + 1
return
}
 
// daysBefore[m] counts the number of days in a non-leap year
// before month m begins. There is an entry for m=12, counting
// the number of days before January of next year (365).
var daysBefore = [...]int32{
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
}
 
func daysIn(m Month, year int) int {
if m == February && isLeap(year) {
return 29
}
return int(daysBefore[m] - daysBefore[m-1])
}
 
// Provided by package runtime.
func now() (sec int64, nsec int32)
 
// Now returns the current local time.
func Now() Time {
sec, nsec := now()
return Time{sec + unixToInternal, nsec, Local}
}
 
// UTC returns t with the location set to UTC.
func (t Time) UTC() Time {
t.loc = UTC
return t
}
 
// Local returns t with the location set to local time.
func (t Time) Local() Time {
t.loc = Local
return t
}
 
// In returns t with the location information set to loc.
//
// In panics if loc is nil.
func (t Time) In(loc *Location) Time {
if loc == nil {
panic("time: missing Location in call to Time.In")
}
t.loc = loc
return t
}
 
// Location returns the time zone information associated with t.
func (t Time) Location() *Location {
l := t.loc
if l == nil {
l = UTC
}
return l
}
 
// Zone computes the time zone in effect at time t, returning the abbreviated
// name of the zone (such as "CET") and its offset in seconds east of UTC.
func (t Time) Zone() (name string, offset int) {
name, offset, _, _, _ = t.loc.lookup(t.sec + internalToUnix)
return
}
 
// Unix returns the Unix time, the number of seconds elapsed
// since January 1, 1970 UTC.
func (t Time) Unix() int64 {
return t.sec + internalToUnix
}
 
// UnixNano returns the Unix time, the number of nanoseconds elapsed
// since January 1, 1970 UTC.
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.
func (t Time) GobEncode() ([]byte, error) {
var offsetMin int16 // minutes east of UTC. -1 is UTC.
 
if t.Location() == &utcLoc {
offsetMin = -1
} else {
_, offset := t.Zone()
if offset%60 != 0 {
return nil, errors.New("Time.GobEncode: zone offset has fractional minute")
}
offset /= 60
if offset < -32768 || offset == -1 || offset > 32767 {
return nil, errors.New("Time.GobEncode: unexpected zone offset")
}
offsetMin = int16(offset)
}
 
enc := []byte{
timeGobVersion, // byte 0 : version
byte(t.sec >> 56), // bytes 1-8: seconds
byte(t.sec >> 48),
byte(t.sec >> 40),
byte(t.sec >> 32),
byte(t.sec >> 24),
byte(t.sec >> 16),
byte(t.sec >> 8),
byte(t.sec),
byte(t.nsec >> 24), // bytes 9-12: nanoseconds
byte(t.nsec >> 16),
byte(t.nsec >> 8),
byte(t.nsec),
byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
byte(offsetMin),
}
 
return enc, nil
}
 
// GobDecode implements the gob.GobDecoder interface.
func (t *Time) GobDecode(buf []byte) error {
if len(buf) == 0 {
return errors.New("Time.GobDecode: no data")
}
 
if buf[0] != timeGobVersion {
return errors.New("Time.GobDecode: unsupported version")
}
 
if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 {
return errors.New("Time.GobDecode: invalid length")
}
 
buf = buf[1:]
t.sec = int64(buf[7]) | int64(buf[6])<<8 | int64(buf[5])<<16 | int64(buf[4])<<24 |
int64(buf[3])<<32 | int64(buf[2])<<40 | int64(buf[1])<<48 | int64(buf[0])<<56
 
buf = buf[8:]
t.nsec = int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24
 
buf = buf[4:]
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
 
if offset == -1*60 {
t.loc = &utcLoc
} else if _, localoff, _, _, _ := Local.lookup(t.sec + internalToUnix); offset == localoff {
t.loc = Local
} else {
t.loc = FixedZone("", offset)
}
 
return nil
}
 
// 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 {
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
}
 
// 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.
return
}
 
// Unix returns the local Time corresponding to the given Unix time,
// sec seconds and nsec nanoseconds since January 1, 1970 UTC.
// It is valid to pass nsec outside the range [0, 999999999].
func Unix(sec int64, nsec int64) Time {
if nsec < 0 || nsec >= 1e9 {
n := nsec / 1e9
sec += n
nsec -= n * 1e9
if nsec < 0 {
nsec += 1e9
sec--
}
}
return Time{sec + unixToInternal, int32(nsec), Local}
}
 
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
 
// norm returns nhi, nlo such that
// hi * base + lo == nhi * base + nlo
// 0 <= nlo < base
func norm(hi, lo, base int) (nhi, nlo int) {
if lo < 0 {
n := (-lo-1)/base + 1
hi -= n
lo += n * base
}
if lo >= base {
n := lo / base
hi += n
lo -= n * base
}
return hi, lo
}
 
// Date returns the Time corresponding to
// yyyy-mm-dd hh:mm:ss + nsec nanoseconds
// in the appropriate zone for that time in the given location.
//
// The month, day, hour, min, sec, and nsec values may be outside
// their usual ranges and will be normalized during the conversion.
// For example, October 32 converts to November 1.
//
// A daylight savings time transition skips or repeats times.
// For example, in the United States, March 13, 2011 2:15am never occurred,
// while November 6, 2011 1:15am occurred twice. In such cases, the
// choice of time zone, and therefore the time, is not well-defined.
// Date returns a time that is correct in one of the two zones involved
// in the transition, but it does not guarantee which.
//
// Date panics if loc is nil.
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time {
if loc == nil {
panic("time: missing Location in call to Date")
}
 
// Normalize month, overflowing into year.
m := int(month) - 1
year, m = norm(year, m, 12)
month = Month(m) + 1
 
// Normalize nsec, sec, min, hour, overflowing into day.
sec, nsec = norm(sec, nsec, 1e9)
min, sec = norm(min, sec, 60)
hour, min = norm(hour, min, 60)
day, hour = norm(day, hour, 24)
 
y := uint64(int64(year) - absoluteZeroYear)
 
// Compute days since the absolute epoch.
 
// Add in days from 400-year cycles.
n := y / 400
y -= 400 * n
d := daysPer400Years * n
 
// Add in 100-year cycles.
n = y / 100
y -= 100 * n
d += daysPer100Years * n
 
// Add in 4-year cycles.
n = y / 4
y -= 4 * n
d += daysPer4Years * n
 
// Add in non-leap years.
n = y
d += 365 * n
 
// Add in days before this month.
d += uint64(daysBefore[month-1])
if isLeap(year) && month >= March {
d++ // February 29
}
 
// Add in days before today.
d += uint64(day - 1)
 
// Add in time elapsed today.
abs := d * secondsPerDay
abs += uint64(hour*secondsPerHour + min*secondsPerMinute + sec)
 
unix := int64(abs) + (absoluteToInternal + internalToUnix)
 
// Look for zone offset for t, so we can adjust to UTC.
// The lookup function expects UTC, so we pass t in the
// hope that it will not be too close to a zone transition,
// and then adjust if it is.
_, offset, _, start, end := loc.lookup(unix)
if offset != 0 {
switch utc := unix - int64(offset); {
case utc < start:
_, offset, _, _, _ = loc.lookup(start - 1)
case utc >= end:
_, offset, _, _, _ = loc.lookup(end)
}
unix -= int64(offset)
}
 
return Time{unix + unixToInternal, int32(nsec), loc}
}
/zoneinfo_windows.go
0,0 → 1,157
// 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 (
"errors"
"syscall"
)
 
// TODO(rsc): Fall back to copy of zoneinfo files.
 
// BUG(brainman,rsc): On Windows, the operating system does not provide complete
// time zone information.
// The implementation assumes that this year's rules for daylight savings
// time apply to all previous and future years as well.
// Also, time zone abbreviations are unavailable. The implementation constructs
// them using the capital letters from a longer time zone description.
 
// abbrev returns the abbreviation to use for the given zone name.
func abbrev(name []uint16) string {
// name is 'Pacific Standard Time' but we want 'PST'.
// Extract just capital letters. It's not perfect but the
// information we need is not available from the kernel.
// Because time zone abbreviations are not unique,
// Windows refuses to expose them.
//
// http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb
// http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net
var short []rune
for _, c := range name {
if 'A' <= c && c <= 'Z' {
short = append(short, rune(c))
}
}
return string(short)
}
 
// pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*)
// denoted by the system date+time d in the given year.
// It is up to the caller to convert this local time into a UTC-based time.
func pseudoUnix(year int, d *syscall.Systemtime) int64 {
// Windows specifies daylight savings information in "day in month" format:
// d.Month is month number (1-12)
// d.DayOfWeek is appropriate weekday (Sunday=0 to Saturday=6)
// d.Day is week within the month (1 to 5, where 5 is last week of the month)
// d.Hour, d.Minute and d.Second are absolute time
day := 1
t := Date(year, Month(d.Month), day, int(d.Hour), int(d.Minute), int(d.Second), 0, UTC)
i := int(d.DayOfWeek) - int(t.Weekday())
if i < 0 {
i += 7
}
day += i
if week := int(d.Day) - 1; week < 4 {
day += week * 7
} else {
// "Last" instance of the day.
day += 4 * 7
if day > daysIn(Month(d.Month), year) {
day -= 7
}
}
return t.sec + int64(day-1)*secondsPerDay + internalToUnix
}
 
func initLocalFromTZI(i *syscall.Timezoneinformation) {
l := &localLoc
 
nzone := 1
if i.StandardDate.Month > 0 {
nzone++
}
l.zone = make([]zone, nzone)
 
std := &l.zone[0]
std.name = abbrev(i.StandardName[0:])
if nzone == 1 {
// No daylight savings.
std.offset = -int(i.Bias) * 60
l.cacheStart = -1 << 63
l.cacheEnd = 1<<63 - 1
l.cacheZone = std
return
}
 
// StandardBias must be ignored if StandardDate is not set,
// so this computation is delayed until after the nzone==1
// return above.
std.offset = -int(i.Bias+i.StandardBias) * 60
 
dst := &l.zone[1]
dst.name = abbrev(i.DaylightName[0:])
dst.offset = -int(i.Bias+i.DaylightBias) * 60
dst.isDST = true
 
// Arrange so that d0 is first transition date, d1 second,
// i0 is index of zone after first transition, i1 second.
d0 := &i.StandardDate
d1 := &i.DaylightDate
i0 := 0
i1 := 1
if d0.Month > d1.Month {
d0, d1 = d1, d0
i0, i1 = i1, i0
}
 
// 2 tx per year, 100 years on each side of this year
l.tx = make([]zoneTrans, 400)
 
t := Now().UTC()
year := t.Year()
txi := 0
for y := year - 100; y < year+100; y++ {
tx := &l.tx[txi]
tx.when = pseudoUnix(y, d0) - int64(l.zone[i1].offset)
tx.index = uint8(i0)
txi++
 
tx = &l.tx[txi]
tx.when = pseudoUnix(y, d1) - int64(l.zone[i0].offset)
tx.index = uint8(i1)
txi++
}
}
 
var usPacific = syscall.Timezoneinformation{
Bias: 8 * 60,
StandardName: [32]uint16{
'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e',
},
StandardDate: syscall.Systemtime{Month: 11, Day: 1, Hour: 2},
DaylightName: [32]uint16{
'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e',
},
DaylightDate: syscall.Systemtime{Month: 3, Day: 2, Hour: 2},
DaylightBias: -60,
}
 
func initTestingZone() {
initLocalFromTZI(&usPacific)
}
 
func initLocal() {
var i syscall.Timezoneinformation
if _, err := syscall.GetTimeZoneInformation(&i); err != nil {
localLoc.name = "UTC"
return
}
initLocalFromTZI(&i)
}
 
// TODO(rsc): Implement.
func loadLocation(name string) (*Location, error) {
return nil, errors.New("unknown time zone " + name)
}
/sleep_test.go
0,0 → 1,221
// 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 (
"errors"
"fmt"
"runtime"
"sort"
"sync/atomic"
"testing"
. "time"
)
 
func TestSleep(t *testing.T) {
const delay = 100 * Millisecond
go func() {
Sleep(delay / 2)
Interrupt()
}()
start := Now()
Sleep(delay)
duration := Now().Sub(start)
if duration < delay {
t.Fatalf("Sleep(%s) slept for only %s", 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(1 * Second)
} else {
c <- true
}
}
 
AfterFunc(0, f)
<-c
}
 
func TestAfterStress(t *testing.T) {
stop := uint32(0)
go func() {
for atomic.LoadUint32(&stop) == 0 {
runtime.GC()
// Need to yield, because otherwise
// the main goroutine will never set the stop flag.
runtime.Gosched()
}
}()
c := Tick(1)
for i := 0; i < 100; i++ {
<-c
}
atomic.StoreUint32(&stop, 1)
}
 
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 BenchmarkAfter(b *testing.B) {
for i := 0; i < b.N; i++ {
<-After(1)
}
}
 
func BenchmarkStop(b *testing.B) {
for i := 0; i < b.N; i++ {
NewTimer(1 * Second).Stop()
}
}
 
func TestAfter(t *testing.T) {
const delay = 100 * Millisecond
start := Now()
end := <-After(delay)
if duration := Now().Sub(start); duration < delay {
t.Fatalf("After(%s) slept for only %d ns", delay, duration)
}
if min := start.Add(delay); end.Before(min) {
t.Fatalf("After(%s) expect >= %s, got %s", delay, min, end)
}
}
 
func TestAfterTick(t *testing.T) {
const (
Delta = 100 * Millisecond
Count = 10
)
t0 := Now()
for i := 0; i < Count; i++ {
<-After(Delta)
}
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)
}
}
 
func TestAfterStop(t *testing.T) {
AfterFunc(100*Millisecond, func() {})
t0 := NewTimer(50 * Millisecond)
c1 := make(chan bool, 1)
t1 := AfterFunc(150*Millisecond, func() { c1 <- true })
c2 := After(200 * Millisecond)
if !t0.Stop() {
t.Fatalf("failed to stop event 0")
}
if !t1.Stop() {
t.Fatalf("failed to stop event 1")
}
<-c2
select {
case <-t0.C:
t.Fatalf("event 0 was not stopped")
case <-c1:
t.Fatalf("event 1 was not stopped")
default:
}
if t1.Stop() {
t.Fatalf("Stop returned true twice")
}
}
 
func TestAfterQueuing(t *testing.T) {
// This test flakes out on some systems,
// so we'll try it a few times before declaring it a failure.
const attempts = 3
err := errors.New("!=nil")
for i := 0; i < attempts && err != nil; i++ {
if err = testAfterQueuing(t); err != nil {
t.Logf("attempt %v failed: %v", i, err)
}
}
if err != nil {
t.Fatal(err)
}
}
 
// 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*/ }
 
type afterResult struct {
slot int
t Time
}
 
func await(slot int, result chan<- afterResult, ac <-chan Time) {
result <- afterResult{slot, <-ac}
}
 
func testAfterQueuing(t *testing.T) error {
const (
Delta = 100 * Millisecond
)
// 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 := Now()
for _, slot := range slots {
go await(slot, result, After(Duration(slot)*Delta))
}
sort.Ints(slots)
for _, slot := range slots {
r := <-result
if r.slot != slot {
return fmt.Errorf("after slot %d, expected %d", r.slot, slot)
}
dt := r.t.Sub(t0)
target := Duration(slot) * Delta
if dt < target-Delta/2 || dt > target+Delta*10 {
return fmt.Errorf("After(%s) arrived at %s, expected [%s,%s]", target, dt, target-Delta/2, target+Delta*10)
}
}
return nil
}
 
func TestTimerStopStress(t *testing.T) {
if testing.Short() {
return
}
for i := 0; i < 100; i++ {
go func(i int) {
timer := AfterFunc(2*Second, func() {
t.Fatalf("timer %d was not stopped", i)
})
Sleep(1 * Second)
timer.Stop()
}(i)
}
Sleep(3 * Second)
}
/sleep.go
0,0 → 1,95
// 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
 
// Sleep pauses the current goroutine for the duration d.
func Sleep(d Duration)
 
func nano() int64 {
sec, nsec := now()
return sec*1e9 + int64(nsec)
}
 
// Interface to timers implemented in package runtime.
// Must be in sync with ../runtime/runtime.h:/^struct.Timer$
type runtimeTimer struct {
i int32
when int64
period int64
f func(int64, interface{})
arg interface{}
}
 
func startTimer(*runtimeTimer)
func stopTimer(*runtimeTimer) bool
 
// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
type Timer struct {
C <-chan Time
r runtimeTimer
}
 
// Stop prevents the Timer from firing.
// It returns true if the call stops the timer, false if the timer has already
// expired or stopped.
func (t *Timer) Stop() (ok bool) {
return stopTimer(&t.r)
}
 
// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer {
c := make(chan Time, 1)
t := &Timer{
C: c,
r: runtimeTimer{
when: nano() + int64(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
 
func sendTime(now int64, c interface{}) {
// Non-blocking send of time on c.
// Used in NewTimer, it cannot block anyway (buffer).
// Used in NewTicker, dropping sends on the floor is
// the desired behavior when the reader gets behind,
// because the sends are periodic.
select {
case c.(chan Time) <- Unix(0, now):
default:
}
}
 
// After waits for the duration to elapse and then sends the current time
// on the returned channel.
// It is equivalent to NewTimer(d).C.
func After(d Duration) <-chan Time {
return NewTimer(d).C
}
 
// AfterFunc waits for the duration to elapse and then calls f
// in its own goroutine. It returns a Timer that can
// be used to cancel the call using its Stop method.
func AfterFunc(d Duration, f func()) *Timer {
t := &Timer{
r: runtimeTimer{
when: nano() + int64(d),
f: goFunc,
arg: f,
},
}
startTimer(&t.r)
return t
}
 
func goFunc(now int64, arg interface{}) {
go arg.(func())()
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.