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())() |
} |