diff --git a/examples/gno.land/r/demo/boards/z_4_filetest.gno b/examples/gno.land/r/demo/boards/z_4_filetest.gno index fa0b9e20be5..475d5edaeec 100644 --- a/examples/gno.land/r/demo/boards/z_4_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_4_filetest.gno @@ -308,14 +308,7 @@ func main() { // c[f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131]={ // "Fields": [ // { -// "N": "AAAAgJSeXbo=", -// "T": { -// "@type": "/gno.PrimitiveType", -// "value": "65536" -// } -// }, -// { -// "N": "AbSNdvQQIhE=", +// "N": "0vknwQ4AAAA=", // "T": { // "@type": "/gno.PrimitiveType", // "value": "1024" @@ -323,21 +316,8 @@ func main() { // }, // { // "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "time.Location" -// } -// }, -// "V": { -// "@type": "/gno.PointerValue", -// "Base": { -// "@type": "/gno.RefValue", -// "Escaped": true, -// "ObjectID": "336074805fc853987abe6f7fe3ad97a6a6f3077a:2" -// }, -// "Index": "182", -// "TV": null +// "@type": "/gno.PrimitiveType", +// "value": "512" // } // } // ], @@ -353,22 +333,13 @@ func main() { // { // "T": { // "@type": "/gno.PrimitiveType", -// "value": "65536" -// } -// }, -// { -// "T": { -// "@type": "/gno.PrimitiveType", // "value": "1024" // } // }, // { // "T": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "time.Location" -// } +// "@type": "/gno.PrimitiveType", +// "value": "512" // } // } // ], @@ -497,7 +468,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "0fd3352422af0a56a77ef2c9e88f479054e3d51f", +// "Hash": "55cc9b7009f66b35f15d34ceed3fa25c9e36d54d", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:131" // } // }, @@ -508,7 +479,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "bed4afa8ffdbbf775451c947fc68b27a345ce32a", +// "Hash": "d8d66a5855b0427c262cfce9891c88d469ab2351", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:132" // } // } @@ -534,7 +505,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "c45bbd47a46681a63af973db0ec2180922e4a8ae", +// "Hash": "6b2cd34f216499f59540555be3fe11f40a67baa1", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:127" // } // } @@ -785,7 +756,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "a416a751c3a45a1e5cba11e737c51340b081e372", +// "Hash": "ed9d8105a90a05bc8d953538dd8810201e26ec6d", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:86" // } // }, @@ -803,7 +774,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "36299fccbc13f2a84c4629fad4cb940f0bd4b1c6", +// "Hash": "ef78d14dccddfaa802727058e543d3ecc87157f0", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:87" // } // }, diff --git a/examples/gno.land/r/demo/groups/z_1_a_filetest.gno b/examples/gno.land/r/demo/groups/z_1_a_filetest.gno index aeff9ab7774..c752441e38a 100644 --- a/examples/gno.land/r/demo/groups/z_1_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_1_a_filetest.gno @@ -69,10 +69,10 @@ func main() { // // Group Creator: gnouser0 // -// Group createdAt: 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 +// Group createdAt: 2009-02-13 23:31:30 +0000 UTC // // Group Last MemberID: 0000000001 // // Group Members: // -// [0000000000, g1vahx7atnv4erxh6lta047h6lta047h6ll85gpy, 32, i am from UAE, 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001], +// [0000000000, g1vahx7atnv4erxh6lta047h6lta047h6ll85gpy, 32, i am from UAE, 2009-02-13 23:31:30 +0000 UTC], diff --git a/examples/gno.land/r/demo/groups/z_2_a_filetest.gno b/examples/gno.land/r/demo/groups/z_2_a_filetest.gno index d1cc53d612f..7cece2d3712 100644 --- a/examples/gno.land/r/demo/groups/z_2_a_filetest.gno +++ b/examples/gno.land/r/demo/groups/z_2_a_filetest.gno @@ -71,7 +71,7 @@ func main() { // // Group Creator: gnouser0 // -// Group createdAt: 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 +// Group createdAt: 2009-02-13 23:31:30 +0000 UTC // // Group Last MemberID: 0000000001 // diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index baf9ba3d4af..ea7eb0c19f2 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -186,11 +186,5 @@ func parseTimes(startTime, endTime string) (time.Time, time.Time, error) { return time.Time{}, time.Time{}, ErrEndBeforeStart } - _, stOffset := st.Zone() - _, etOffset := et.Zone() - if stOffset != etOffset { - return time.Time{}, time.Time{}, ErrStartEndTimezonemMismatch - } - return st, et, nil } diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno index 1d79b754ee4..26e196fc76f 100644 --- a/examples/gno.land/r/gnoland/events/events_test.gno +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -159,9 +159,6 @@ func TestParseTimes(t *testing.T) { _, _, err = parseTimes("2009-02-13T23:30:30Z", "2009-02-13T21:30:30Z") uassert.ErrorContains(t, err, ErrEndBeforeStart.Error()) - - _, _, err = parseTimes("2009-02-10T23:30:30+02:00", "2009-02-13T21:30:33+05:00") - uassert.ErrorContains(t, err, ErrStartEndTimezonemMismatch.Error()) } func TestRenderEventWidget(t *testing.T) { diff --git a/examples/gno.land/r/gnoland/monit/monit_test.gno b/examples/gno.land/r/gnoland/monit/monit_test.gno index fc9b394b8ed..f09b14e7b73 100644 --- a/examples/gno.land/r/gnoland/monit/monit_test.gno +++ b/examples/gno.land/r/gnoland/monit/monit_test.gno @@ -23,7 +23,7 @@ status=KO` Incr() { expected := `counter=3 -last update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 +last update=2009-02-13 23:31:30 +0000 UTC last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm status=OK` got := Render("") @@ -35,7 +35,7 @@ status=OK` use std.TestSkipTime(time.Hour) { expected := `counter=3 - last update=2009-02-13 22:31:30 +0000 UTC m=+1234564290.000000001 + last update=2009-02-13 22:31:30 +0000 UTC last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm status=KO` got := Render("") @@ -46,7 +46,7 @@ status=OK` Incr() { expected := `counter=4 - last update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 + last update=2009-02-13 23:31:30 +0000 UTC last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm status=OK` got := Render("") diff --git a/gnovm/stdlibs/time/format.gno b/gnovm/stdlibs/time/format.gno index 61a9eb3301b..a0e32626dc2 100644 --- a/gnovm/stdlibs/time/format.gno +++ b/gnovm/stdlibs/time/format.gno @@ -4,7 +4,9 @@ package time -import "errors" +import ( + "errors" +) // These are predefined layouts for use in Time.Format and time.Parse. // The reference time used in these layouts is the specific time stamp: @@ -510,30 +512,6 @@ func formatNano(b []byte, nanosec uint, std int) []byte { // with an explicit format string. func (t Time) String() string { s := t.Format("2006-01-02 15:04:05.999999999 -0700 MST") - - // Format monotonic clock reading as m=±ddd.nnnnnnnnn. - if t.wall&hasMonotonic != 0 { - m2 := uint64(t.ext) - sign := byte('+') - if t.ext < 0 { - sign = '-' - m2 = -m2 - } - m1, m2 := m2/1e9, m2%1e9 - m0, m1 := m1/1e9, m1%1e9 - buf := make([]byte, 0, 24) - buf = append(buf, " m="...) - buf = append(buf, sign) - wid := 0 - if m0 != 0 { - buf = appendInt(buf, int(m0), 0) - wid = 9 - } - buf = appendInt(buf, int(m1), wid) - buf = append(buf, '.') - buf = appendInt(buf, int(m2), 9) - s += string(buf) - } return s } @@ -563,31 +541,7 @@ func (t Time) GoString() string { buf = append(buf, ", "...) buf = appendInt(buf, t.Nanosecond(), 0) buf = append(buf, ", "...) - switch loc := t.Location(); loc { - case UTC, nil: - buf = append(buf, "time.UTC"...) - case Local: - buf = append(buf, "time.Local"...) - default: - // there are several options for how we could display this, none of - // which are great: - // - // - use Location(loc.name), which is not technically valid syntax - // - use LoadLocation(loc.name), which will cause a syntax error when - // embedded and also would require us to escape the string without - // importing fmt or strconv - // - try to use FixedZone, which would also require escaping the name - // and would represent e.g. "America/Los_Angeles" daylight saving time - // shifts inaccurately - // - use the pointer format, which is no worse than you'd get with the - // old fmt.Sprintf("%#v", t) format. - // - // Of these, Location(loc.name) is the least disruptive. This is an edge - // case we hope not to hit too often. - buf = append(buf, `time.Location(`...) - buf = append(buf, []byte(quote(loc.name))...) - buf = append(buf, `)`...) - } + buf = append(buf, "time.UTC"...) buf = append(buf, ')') return string(buf) } @@ -616,8 +570,7 @@ func (t Time) Format(layout string) string { // representation to b and returns the extended buffer. func (t Time) AppendFormat(b []byte, layout string) []byte { var ( - name, offset, abs = t.locabs() - + it = t.internal() year int = -1 month Month day int @@ -639,13 +592,13 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { // Compute year, month, day if needed. if year < 0 && std&stdNeedDate != 0 { - year, month, day, yday = absDate(abs, true) + year, month, day, yday = internalDate(it, true) yday++ } // Compute hour, minute, second if needed. if hour < 0 && std&stdNeedClock != 0 { - hour, min, sec = absClock(abs) + hour, min, sec = internalClock(it) } switch std & stdMask { @@ -667,9 +620,9 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { case stdZeroMonth: b = appendInt(b, int(month), 2) case stdWeekDay: - b = append(b, absWeekday(abs).String()[:3]...) + b = append(b, internalWeekday(it).String()[:3]...) case stdLongWeekDay: - s := absWeekday(abs).String() + s := internalWeekday(it).String() b = append(b, s...) case stdDay: b = appendInt(b, day, 0) @@ -729,25 +682,17 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ: // Ugly special case. We cheat and take the "Z" variants // to mean "the time zone as formatted for ISO 8601". - if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ) { + if std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ { b = append(b, 'Z') break } - zone := offset / 60 // convert to minutes - absoffset := offset - if zone < 0 { - b = append(b, '-') - zone = -zone - absoffset = -absoffset - } else { - b = append(b, '+') - } - b = appendInt(b, zone/60, 2) + b = append(b, '+') + b = appendInt(b, 0, 2) if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ { b = append(b, ':') } if std != stdNumShortTZ && std != stdISO8601ShortTZ { - b = appendInt(b, zone%60, 2) + b = appendInt(b, 0, 2) } // append seconds if appropriate @@ -755,25 +700,11 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { b = append(b, ':') } - b = appendInt(b, absoffset%60, 2) + b = appendInt(b, 0, 2) } case stdTZ: - if name != "" { - b = append(b, name...) - break - } - // 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 { - b = append(b, '-') - zone = -zone - } else { - b = append(b, '+') - } - b = appendInt(b, zone/60, 2) - b = appendInt(b, zone%60, 2) + b = append(b, "UTC"...) case stdFracSecond0, stdFracSecond9: b = formatNano(b, uint(t.Nanosecond()), std) } @@ -987,7 +918,6 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) min int sec int nsec int - z *Location zoneOffset int = -1 zoneName string ) @@ -1132,7 +1062,6 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' { value = value[1:] - z = UTC break } var sign, hour, min, seconds string @@ -1194,7 +1123,6 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) case stdTZ: // Does it look like a time zone? if len(value) >= 3 && value[0:3] == "UTC" { - z = UTC value = value[3:] break } @@ -1289,24 +1217,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) return Time{}, &ParseError{alayout, avalue, "", value, ": day out of range"} } - if z != nil { - return Date(year, Month(month), day, hour, min, sec, nsec, z), nil - } - if zoneOffset != -1 { t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) t.addSec(-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.unixSec()) - if offset == zoneOffset && (zoneName == "" || name == zoneName) { - t.setLoc(local) - return t, nil - } - - // Otherwise create fake zone to record offset. - t.setLoc(FixedZone(zoneName, zoneOffset)) return t, nil } @@ -1317,21 +1233,14 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) offset, ok := local.lookupName(zoneName, t.unixSec()) if ok { t.addSec(-int64(offset)) - t.setLoc(local) return t, nil } - // Otherwise, create fake zone with unknown offset. - if len(zoneName) > 3 && zoneName[:3] == "GMT" { - offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT. - offset *= 3600 - } - t.setLoc(FixedZone(zoneName, offset)) return t, nil } // Otherwise, fall back to default. - return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil + return Date(year, Month(month), day, hour, min, sec, nsec, UTC), nil } // parseTimeZone parses a time zone string and returns its length. Time zones diff --git a/gnovm/stdlibs/time/time.gno b/gnovm/stdlibs/time/time.gno index f3395142d1d..60a4dd3181a 100644 --- a/gnovm/stdlibs/time/time.gno +++ b/gnovm/stdlibs/time/time.gno @@ -1,283 +1,45 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package time provides functionality for measuring and displaying time. -// -// The calendrical calculations always assume a Gregorian calendar, with -// no leap seconds. -// -// # Monotonic Clocks -// -// Operating systems provide both a “wall clock,” which is subject to -// changes for clock synchronization, and a “monotonic clock,” which is -// not. The general rule is that the wall clock is for telling time and -// the monotonic clock is for measuring time. Rather than split the API, -// in this package the Time returned by time.Now contains both a wall -// clock reading and a monotonic clock reading; later time-telling -// operations use the wall clock reading, but later time-measuring -// operations, specifically comparisons and subtractions, use the -// monotonic clock reading. -// -// For example, this code always computes a positive elapsed time of -// approximately 20 milliseconds, even if the wall clock is changed during -// the operation being timed: -// -// start := time.Now() -// ... operation that takes 20 milliseconds ... -// t := time.Now() -// elapsed := t.Sub(start) -// -// Other idioms, such as time.Since(start), time.Until(deadline), and -// time.Now().Before(deadline), are similarly robust against wall clock -// resets. -// -// The rest of this section gives the precise details of how operations -// use monotonic clocks, but understanding those details is not required -// to use this package. -// -// The Time returned by time.Now contains a monotonic clock reading. -// If Time t has a monotonic clock reading, t.Add adds the same duration to -// both the wall clock and monotonic clock readings to compute the result. -// Because t.AddDate(y, m, d), t.Round(d), and t.Truncate(d) are wall time -// computations, they always strip any monotonic clock reading from their results. -// Because t.In, t.Local, and t.UTC are used for their effect on the interpretation -// of the wall time, they also strip any monotonic clock reading from their results. -// The canonical way to strip a monotonic clock reading is to use t = t.Round(0). -// -// If Times t and u both contain monotonic clock readings, the operations -// t.After(u), t.Before(u), t.Equal(u), and t.Sub(u) are carried out -// using the monotonic clock readings alone, ignoring the wall clock -// readings. If either t or u contains no monotonic clock reading, these -// operations fall back to using the wall clock readings. -// -// On some systems the monotonic clock will stop if the computer goes to sleep. -// On such a system, t.Sub(u) may not accurately reflect the actual -// time that passed between t and u. -// -// Because the monotonic clock reading has no meaning outside -// the current process, the serialized forms generated by t.GobEncode, -// t.MarshalBinary, t.MarshalJSON, and t.MarshalText omit the monotonic -// clock reading, and t.Format provides no format for it. Similarly, the -// constructors time.Date, time.Parse, time.ParseInLocation, and time.Unix, -// as well as the unmarshalers t.GobDecode, t.UnmarshalBinary. -// t.UnmarshalJSON, and t.UnmarshalText always create times with -// no monotonic clock reading. -// -// The monotonic clock reading exists only in Time values. It is not -// a part of Duration values or the Unix times returned by t.Unix and -// friends. -// -// Note that the Go == operator compares not just the time instant but -// also the Location and the monotonic clock reading. See the -// documentation for the Time type for a discussion of equality -// testing for Time values. -// -// For debugging, the result of t.String does include the monotonic -// clock reading if present. If t != u because of different monotonic clock readings, -// that difference will be visible when printing t.String() and u.String(). package time -import ( - "errors" -) +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. -// -// A Time value can be used by multiple goroutines simultaneously except -// that the methods GobDecode, UnmarshalBinary, UnmarshalJSON and -// UnmarshalText are not concurrency-safe. -// -// 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. -// -// Representations of a Time value saved by the GobEncode, MarshalBinary, -// MarshalJSON, and MarshalText methods store the Time.Location's offset, but not -// the location name. They therefore lose information about Daylight Saving Time. -// -// In addition to the required “wall clock” reading, a Time may contain an optional -// reading of the current process's monotonic clock, to provide additional precision -// for comparison or subtraction. -// See the “Monotonic Clocks” section in the package documentation for details. -// -// Note that the Go == operator compares not just the time instant but also the -// Location and the monotonic clock reading. Therefore, Time values should not -// be used as map or database keys without first guaranteeing that the -// identical Location has been set for all values, which can be achieved -// through use of the UTC or Local method, and that the monotonic clock reading -// has been stripped by setting t = t.Round(0). In general, prefer t.Equal(u) -// to t == u, since t.Equal uses the most accurate comparison available and -// correctly handles the case when only one of its arguments has a monotonic -// clock reading. type Time struct { - // wall and ext encode the wall time seconds, wall time nanoseconds, - // and optional monotonic clock reading in nanoseconds. - // - // From high to low bit position, wall encodes a 1-bit flag (hasMonotonic), - // a 33-bit seconds field, and a 30-bit wall time nanoseconds field. - // The nanoseconds field is in the range [0, 999999999]. - // If the hasMonotonic bit is 0, then the 33-bit field must be zero - // and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext. - // If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit - // unsigned wall seconds since Jan 1 year 1885, and ext holds a - // signed 64-bit monotonic clock reading, nanoseconds since process start. - wall uint64 - ext int64 - - // loc specifies the Location that should be used to - // determine the minute, hour, month, day, and year - // that correspond to this Time. - // The nil location means UTC. - // All UTC times are represented with loc==nil, never loc==&utcLoc. - loc *Location -} - -const ( - hasMonotonic = 1 << 63 - maxWall = wallToInternal + (1<<33 - 1) // year 2157 - minWall = wallToInternal // year 1885 - nsecMask = 1<<30 - 1 - nsecShift = 30 -) - -// These helpers for manipulating the wall and monotonic clock readings -// take pointer receivers, even when they don't modify the time, -// to make them cheaper to call. - -// nsec returns the time's nanoseconds. -func (t *Time) nsec() int32 { - return int32(t.wall & nsecMask) -} - -// sec returns the time's seconds since Jan 1 year 1. -func (t *Time) sec() int64 { - if t.wall&hasMonotonic != 0 { - return wallToInternal + int64(t.wall<<1>>(nsecShift+1)) - } - return t.ext + sec int64 + nsec int32 } // unixSec returns the time's seconds since Jan 1 1970 (Unix time). -func (t *Time) unixSec() int64 { return t.sec() + internalToUnix } +func (t *Time) unixSec() int64 { return t.sec + internalToUnix } // addSec adds d seconds to the time. func (t *Time) addSec(d int64) { - if t.wall&hasMonotonic != 0 { - sec := int64(t.wall << 1 >> (nsecShift + 1)) - dsec := sec + d - if 0 <= dsec && dsec <= 1<<33-1 { - t.wall = t.wall&nsecMask | uint64(dsec)< t.ext) == (d > 0) { - t.ext = sum + // Check if the sum of t.sec and d overflows and handle it properly. + sum := t.sec + d + if (sum > t.sec) == (d > 0) { + t.sec = sum } else if d > 0 { - t.ext = 1<<63 - 1 + t.sec = 1<<63 - 1 } else { - t.ext = -(1<<63 - 1) - } -} - -// setLoc sets the location associated with the time. -func (t *Time) setLoc(loc *Location) { - if loc == &utcLoc { - loc = nil - } - t.stripMono() - t.loc = loc -} - -// stripMono strips the monotonic clock reading in t. -func (t *Time) stripMono() { - if t.wall&hasMonotonic != 0 { - t.ext = t.sec() - t.wall &= nsecMask - } -} - -// setMono sets the monotonic clock reading in t. -// If t cannot hold a monotonic clock reading, -// because its wall time is too large, -// setMono is a no-op. -func (t *Time) setMono(m int64) { - if t.wall&hasMonotonic == 0 { - sec := t.ext - if sec < minWall || maxWall < sec { - return - } - t.wall |= hasMonotonic | uint64(sec-minWall)< u.ext - } - ts := t.sec() - us := u.sec() - return ts > us || ts == us && t.nsec() > u.nsec() + 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 { - if t.wall&u.wall&hasMonotonic != 0 { - return t.ext < u.ext - } - ts := t.sec() - us := u.sec() - return ts < us || ts == us && t.nsec() < u.nsec() + 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 and 4:00 UTC are Equal. // See the documentation on the Time type for the pitfalls of using == with // Time values; most code should use Equal instead. func (t Time) Equal(u Time) bool { - if t.wall&u.wall&hasMonotonic != 0 { - return t.ext == u.ext - } - return t.sec() == u.sec() && t.nsec() == u.nsec() + return t.sec == u.sec && t.nsec == u.nsec } -// A Month specifies a month of the year (January = 1, ...). type Month int const ( @@ -431,53 +193,16 @@ const ( // 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 + 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 - // Avoid function calls when possible. - if l == nil || l == &localLoc { - l = l.get() - } +// rename of the "abs" method in the original time.go +// since we removed the location & zoneinfo related code +func (t Time) internal() uint64 { sec := t.unixSec() - 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)) } -// locabs is a combination of the Zone and abs methods, -// extracting both return values from a single zone lookup. -func (t Time) locabs() (name string, offset int, abs uint64) { - l := t.loc - if l == nil || l == &localLoc { - l = l.get() - } - // Avoid function call if we hit the local time cache. - sec := t.unixSec() - if l != &utcLoc { - if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd { - name = l.cacheZone.name - offset = l.cacheZone.offset - } else { - name, offset, _, _, _ = l.lookup(sec) - } - sec += int64(offset) - } else { - name = "UTC" - } - abs = uint64(sec + (unixToInternal + internalToAbsolute)) - return -} - // 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) @@ -504,13 +229,12 @@ func (t Time) Day() int { // Weekday returns the day of the week specified by t. func (t Time) Weekday() Weekday { - return absWeekday(t.abs()) + return internalWeekday(t.internal()) } -// absWeekday is like Weekday but operates on an absolute time. -func absWeekday(abs uint64) Weekday { - // January 1 of the absolute year, like January 1 of 2001, was a Monday. - sec := (abs + uint64(Monday)*secondsPerDay) % secondsPerWeek +// internalWeekday is like Weekday but operates on an internal time. +func internalWeekday(it uint64) Weekday { + sec := (it + uint64(Monday)*secondsPerDay) % secondsPerWeek return Weekday(int(sec) / secondsPerDay) } @@ -529,26 +253,26 @@ func (t Time) ISOWeek() (year, week int) { // 1 2 3 4 5 6 7 // +3 +2 +1 0 -1 -2 -3 // the offset to Thursday - abs := t.abs() - d := Thursday - absWeekday(abs) + it := t.internal() + d := Thursday - internalWeekday(it) // handle Sunday if d == 4 { - d = -3 + d -= 3 } // find the Thursday of the calendar week - abs += uint64(d) * secondsPerDay - year, _, _, yday := absDate(abs, false) + it += uint64(d) * secondsPerDay + year, _, _, yday := internalDate(it, false) return year, yday/7 + 1 } // Clock returns the hour, minute, and second within the day specified by t. func (t Time) Clock() (hour, min, sec int) { - return absClock(t.abs()) + return internalClock(t.internal()) } -// absClock is like clock but operates on an absolute time. -func absClock(abs uint64) (hour, min, sec int) { - sec = int(abs % secondsPerDay) +// internalClock is like clock but operates on an internal time. +func internalClock(it uint64) (hour, min, sec int) { + sec = int(it % secondsPerDay) hour = sec / secondsPerHour sec -= hour * secondsPerHour min = sec / secondsPerMinute @@ -558,23 +282,23 @@ func absClock(abs uint64) (hour, min, sec int) { // 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 + return int(t.internal()%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 + return int(t.internal()%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) + return int(t.internal() % 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()) + return int(t.nsec) } // YearDay returns the day of the year specified by t, in the range [1,365] for non-leap years, @@ -716,8 +440,7 @@ func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) { return w, v } -// fmtInt formats v into the tail of buf. -// It returns the index where the output begins. +// add v at the end of buf and return the index where the number starts in buf func fmtInt(buf []byte, v uint64) int { w := len(buf) if v == 0 { @@ -830,10 +553,10 @@ func (d Duration) Abs() Duration { } } -// Add returns the time t+d. +// Add returns the time t+d func (t Time) Add(d Duration) Time { dsec := int64(d / 1e9) - nsec := t.nsec() + int32(d%1e9) + nsec := t.nsec + int32(d%1e9) if nsec >= 1e9 { dsec++ nsec -= 1e9 @@ -841,18 +564,7 @@ func (t Time) Add(d Duration) Time { dsec-- nsec += 1e9 } - t.wall = t.wall&^nsecMask | uint64(nsec) // update nsec - t.addSec(dsec) - if t.wall&hasMonotonic != 0 { - te := t.ext + int64(d) - if d < 0 && te > t.ext || d > 0 && te < t.ext { - // Monotonic clock reading now out of range; degrade to wall-only. - t.stripMono() - } else { - t.ext = te - } - } - return t + return Time{t.sec + dsec, nsec} } // Sub returns the duration t-u. If the result exceeds the maximum (or minimum) @@ -860,54 +572,34 @@ func (t Time) Add(d Duration) Time { // will be returned. // To compute t-d for a duration d, use t.Add(-d). func (t Time) Sub(u Time) Duration { - if t.wall&u.wall&hasMonotonic != 0 { - te := t.ext - ue := u.ext - d := Duration(te - ue) - if d < 0 && te > ue { - return maxDuration // t - u is positive out of range - } - if d > 0 && te < ue { - return minDuration // t - u is negative out of range - } - return d + sec := t.sec - u.sec + nsec := t.nsec - u.nsec + if sec > 0 && nsec < 0 { + sec-- + nsec += 1e9 + } else if sec < 0 && nsec > 0 { + sec++ + nsec -= 1e9 } - d := Duration(t.sec()-u.sec())*Second + Duration(t.nsec()-u.nsec()) - // Check for overflow or underflow. - switch { - case u.Add(d).Equal(t): - return d // d is correct - case t.Before(u): - return minDuration // t - u is negative out of range - default: - return maxDuration // t - u is positive out of range + if sec > int64(maxDuration) { + return maxDuration + } + if sec < int64(minDuration) { + return minDuration } + return Duration(sec*1e9 + int64(nsec)) } // Since returns the time elapsed since t. // It is shorthand for time.Now().Sub(t). func Since(t Time) Duration { - var now Time - if t.wall&hasMonotonic != 0 { - // Common case optimization: if t has monotonic time, then Sub will use only it. - now = Time{hasMonotonic, runtimeNano() - startNano, nil} - } else { - now = Now() - } - return now.Sub(t) + return Now().Sub(t) } // Until returns the duration until t. // It is shorthand for t.Sub(time.Now()). func Until(t Time) Duration { - var now Time - if t.wall&hasMonotonic != 0 { - // Common case optimization: if t has monotonic time, then Sub will use only it. - now = Time{hasMonotonic, runtimeNano() - startNano, nil} - } else { - now = Now() - } - return t.Sub(now) + return t.Sub(Now()) } // AddDate returns the time corresponding to adding the @@ -921,7 +613,7 @@ func Until(t Time) Duration { 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.Location()) + return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec), UTC) } const ( @@ -937,13 +629,12 @@ const ( // date computes the year, day of year, and 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) { - return absDate(t.abs(), full) + return internalDate(t.internal(), full) } -// absDate is like date but operates on an absolute time. -func absDate(abs uint64, full bool) (year int, month Month, day int, yday int) { - // Split into time and day. - d := abs / secondsPerDay +// internalDate is like date but operates on an internal time. +func internalDate(it uint64, full bool) (year int, month Month, day int, yday int) { + d := it / secondsPerDay // Account for 400 year cycles. n := d / daysPer400Years @@ -1068,93 +759,22 @@ func daysSinceEpoch(year int) uint64 { return d } -func now() (sec int64, nsec int32, mono int64) // injected +func now() (sec int64, nsec int32, mono int64) // injected by runtime -// runtimeNano returns the current value of the runtime clock in nanoseconds. -func runtimeNano() int64 { - _, _, mono := now() - return mono -} - -// Monotonic times are reported as offsets from startNano. -// We initialize startNano to runtimeNano() - 1 so that on systems where -// monotonic time resolution is fairly low (e.g. Windows 2008 -// which appears to have a default resolution of 15ms), -// we avoid ever reporting a monotonic time of 0. -// (Callers may want to use 0 as "time not set".) -var startNano int64 = runtimeNano() - 1 - -// Now returns the current local time. +// Now returns the current UTC time. func Now() Time { - sec, nsec, mono := now() - mono -= startNano - sec += unixToInternal - minWall - if uint64(sec)>>33 != 0 { - return Time{uint64(nsec), sec + minWall, Local} - } - return Time{hasMonotonic | uint64(sec)< 32767 { - return nil, errors.New("Time.MarshalBinary: unexpected zone offset") - } - offsetMin = int16(offset) - } - - sec := t.sec() - nsec := t.nsec() + sec := t.sec + nsec := t.nsec enc := []byte{ - version, // byte 0 : version - byte(sec >> 56), // bytes 1-8: seconds + // encode seconds (int64) / bytes 0 to 7 + byte(sec >> 56), byte(sec >> 48), byte(sec >> 40), byte(sec >> 32), @@ -1235,17 +830,12 @@ func (t Time) MarshalBinary() ([]byte, error) { byte(sec >> 16), byte(sec >> 8), byte(sec), - byte(nsec >> 24), // bytes 9-12: nanoseconds + // encode nanoseconds (int32) / bytes 8 to 11 + byte(nsec >> 24), byte(nsec >> 16), byte(nsec >> 8), byte(nsec), - byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes - byte(offsetMin), - } - if version == timeBinaryVersionV2 { - enc = append(enc, byte(offsetSec)) } - return enc, nil } @@ -1255,62 +845,22 @@ func (t *Time) UnmarshalBinary(data []byte) error { if len(buf) == 0 { return errors.New("Time.UnmarshalBinary: no data") } - - version := buf[0] - if version != timeBinaryVersionV1 && version != timeBinaryVersionV2 { - return errors.New("Time.UnmarshalBinary: unsupported version") - } - - wantLen := /*version*/ 1 + /*sec*/ 8 + /*nsec*/ 4 + /*zone offset*/ 2 - if version == timeBinaryVersionV2 { - wantLen++ - } - if len(buf) != wantLen { + if len(buf) != 12 { // 8 bytes for sec (int64) + 4 bytes for nsec (int32) return errors.New("Time.UnmarshalBinary: invalid length") } - - buf = buf[1:] 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:] 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 version == timeBinaryVersionV2 { - offset += int(buf[2]) - } - *t = Time{} - t.wall = uint64(nsec) - t.ext = sec - - if offset == -1*60 { - t.setLoc(&utcLoc) - } else if _, localoff, _, _, _ := Local.lookup(t.unixSec()); offset == localoff { - t.setLoc(Local) - } else { - t.setLoc(FixedZone("", offset)) - } + t.sec = sec + t.nsec = nsec return nil } -// TODO(rsc): Remove GobEncoder, GobDecoder, MarshalJSON, UnmarshalJSON in Go 2. -// The same semantics will be provided by the generic MarshalBinary, MarshalText, -// UnmarshalBinary, UnmarshalText. - -// GobEncode implements the gob.GobEncoder interface. -func (t Time) GobEncode() ([]byte, error) { - return t.MarshalBinary() -} - -// GobDecode implements the gob.GobDecoder interface. -func (t *Time) GobDecode(data []byte) error { - return t.UnmarshalBinary(data) -} - // MarshalJSON implements the json.Marshaler interface. // The time is a quoted string in RFC 3339 format, with sub-second precision added if present. func (t Time) MarshalJSON() ([]byte, error) { @@ -1390,12 +940,6 @@ func UnixMicro(usec int64) Time { return Unix(usec/1e6, (usec%1e6)*1e3) } -// IsDST reports whether the time in the configured location is in Daylight Savings Time. -func (t Time) IsDST() bool { - _, _, _, _, isDST := t.loc.lookup(t.Unix()) - return isDST -} - func isLeap(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) } @@ -1422,25 +966,11 @@ func norm(hi, lo, base int) (nhi, nlo int) { // // 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. +// Location is always UTC, regardless of the given time zone. 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) @@ -1469,36 +999,12 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T abs += uint64(hour*secondsPerHour + min*secondsPerMinute + sec) unix := int64(abs) + (absoluteToInternal + internalToUnix) - - // Look for zone offset for expected time, so we can adjust to UTC. - // The lookup function expects UTC, so first we pass unix 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 { - utc := unix - int64(offset) - // If utc is valid for the time zone we found, then we have the right offset. - // If not, we get the correct offset by looking up utc in the location. - if utc < start || utc >= end { - _, offset, _, _, _ = loc.lookup(utc) - } - unix -= int64(offset) - } - t := unixTime(unix, int32(nsec)) - t.setLoc(loc) return t } // Truncate returns the result of rounding t down to a multiple of d (since the zero time). -// If d <= 0, Truncate returns t stripped of any monotonic clock reading but otherwise unchanged. -// -// Truncate operates on the time as an absolute duration since the -// zero time; it does not operate on the presentation form of the -// time. Thus, Truncate(Hour) may return a time with a non-zero -// minute, depending on the time's Location. func (t Time) Truncate(d Duration) Time { - t.stripMono() if d <= 0 { return t } @@ -1508,14 +1014,7 @@ func (t Time) Truncate(d Duration) Time { // Round returns the result of rounding t to the nearest multiple of d (since the zero time). // The rounding behavior for halfway values is to round up. -// If d <= 0, Round returns t stripped of any monotonic clock reading but otherwise unchanged. -// -// Round operates on the time as an absolute duration since the -// zero time; it does not operate on the presentation form of the -// time. Thus, Round(Hour) may return a time with a non-zero -// minute, depending on the time's Location. func (t Time) Round(d Duration) Time { - t.stripMono() if d <= 0 { return t } @@ -1531,8 +1030,8 @@ func (t Time) Round(d Duration) Time { // but it's still here in case we change our minds. func div(t Time, d Duration) (qmod2 int, r Duration) { neg := false - nsec := t.nsec() - sec := t.sec() + nsec := t.nsec + sec := t.sec if sec < 0 { // Operate on absolute value. neg = true diff --git a/gnovm/stdlibs/time/timezoneinfo.gno b/gnovm/stdlibs/time/timezoneinfo.gno index 826c2988a8c..a8678f855ea 100644 --- a/gnovm/stdlibs/time/timezoneinfo.gno +++ b/gnovm/stdlibs/time/timezoneinfo.gno @@ -307,7 +307,7 @@ func tzset(s string, initEnd, sec int64) (name string, offset int, start, end in return "", 0, 0, 0, false, false } - year, _, _, yday := absDate(uint64(sec+unixToInternal+internalToAbsolute), false) + year, _, _, yday := internalDate(uint64(sec+unixToInternal+internalToAbsolute), false) ysec := int64(yday*secondsPerDay) + sec%secondsPerDay @@ -625,38 +625,6 @@ var zoneinfo *string func loadFromEmbeddedTZData(name string) ([]byte, bool) // injected // XXX var zoneinfoOnce sync.Once - -// 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". -// -// LoadLocation looks for the IANA Time Zone database in the following -// locations in order: -// -// - the directory or uncompressed zip file named by the ZONEINFO environment variable -// - on a Unix system, the system standard installation location -// - $GOROOT/lib/time/zoneinfo.zip -// - the time/tzdata package, if it was imported -func LoadLocation(name string) (*Location, error) { - if name == "" || name == "UTC" { - return UTC, nil - } - if name == "Local" { - return Local, nil - } - if containsDotDot(name) || name[0] == '/' || name[0] == '\\' { - // No valid IANA Time Zone name contains a single dot, - // much less dot dot. Likewise, none begin with a slash. - return nil, errLocation - } - - return loadLocation(name) -} - // containsDotDot reports whether s contains "..". func containsDotDot(s string) bool { if len(s) < 2 { diff --git a/gnovm/stdlibs/time/zoneinfo_read.gno b/gnovm/stdlibs/time/zoneinfo_read.gno deleted file mode 100644 index 2621b2b6631..00000000000 --- a/gnovm/stdlibs/time/zoneinfo_read.gno +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Parse "zoneinfo" time zone file. -// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. -// See tzfile(5), https://en.wikipedia.org/wiki/Zoneinfo, -// and ftp://munnari.oz.au/pub/oldtz/ - -package time - -import ( - "errors" -) - -// Simple I/O interface to binary blob of data. -type dataIO struct { - p []byte - error bool -} - -func (d *dataIO) 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 *dataIO) big4() (n uint32, ok bool) { - p := d.read(4) - if len(p) < 4 { - d.error = true - return 0, false - } - return uint32(p[3]) | uint32(p[2])<<8 | uint32(p[1])<<16 | uint32(p[0])<<24, true -} - -func (d *dataIO) big8() (n uint64, ok bool) { - n1, ok1 := d.big4() - n2, ok2 := d.big4() - if !ok1 || !ok2 { - d.error = true - return 0, false - } - return (uint64(n1) << 32) | uint64(n2), true -} - -func (d *dataIO) byte() (n byte, ok bool) { - p := d.read(1) - if len(p) < 1 { - d.error = true - return 0, false - } - return p[0], true -} - -// rest returns the rest of the data in the buffer. -func (d *dataIO) rest() []byte { - r := d.p - d.p = nil - return r -} - -// 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 errBadData = errors.New("malformed time zone information") - -// LoadLocationFromTZData returns a Location with the given name -// initialized from the IANA Time Zone database-formatted data. -// The data should be in the format of a standard IANA time zone file -// (for example, the content of /etc/localtime on Unix systems). -func LoadLocationFromTZData(name string, data []byte) (*Location, error) { - d := dataIO{data, false} - - // 4-byte magic "TZif" - if magic := d.read(4); string(magic) != "TZif" { - return nil, errBadData - } - - // 1-byte version, then 15 bytes of padding - var version int - var p []byte - if p = d.read(16); len(p) != 16 { - return nil, errBadData - } else { - switch p[0] { - case 0: - version = 1 - case '2': - version = 2 - case '3': - version = 3 - default: - return nil, errBadData - } - } - - // 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, errBadData - } - if uint32(int(nn)) != nn { - return nil, errBadData - } - n[i] = int(nn) - } - - // If we have version 2 or 3, then the data is first written out - // in a 32-bit format, then written out again in a 64-bit format. - // Skip the 32-bit format and read the 64-bit one, as it can - // describe a broader range of dates. - - is64 := false - if version > 1 { - // Skip the 32-bit data. - skip := n[NTime]*4 + - n[NTime] + - n[NZone]*6 + - n[NChar] + - n[NLeap]*8 + - n[NStdWall] + - n[NUTCLocal] - // Skip the version 2 header that we just read. - skip += 4 + 16 - d.read(skip) - - is64 = true - - // Read the counts again, they can differ. - for i := 0; i < 6; i++ { - nn, ok := d.big4() - if !ok { - return nil, errBadData - } - if uint32(int(nn)) != nn { - return nil, errBadData - } - n[i] = int(nn) - } - } - - size := 4 - if is64 { - size = 8 - } - - // Transition times. - txtimes := dataIO{d.read(n[NTime] * size), false} - - // Time zone indices for transition times. - txzones := d.read(n[NTime]) - - // Zone info structures - zonedata := dataIO{d.read(n[NZone] * 6), false} - - // Time zone abbreviations. - abbrev := d.read(n[NChar]) - - // Leap-second time pairs - d.read(n[NLeap] * (size + 4)) - - // 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, errBadData - } - - var extend string - rest := d.rest() - if len(rest) > 2 && rest[0] == '\n' && rest[len(rest)-1] == '\n' { - extend = string(rest[1 : len(rest)-1]) - } - - // Now we can build up a useful data structure. - // First the zone information. - // utcoff[4] isdst[1] nameindex[1] - nzone := n[NZone] - if nzone == 0 { - // Reject tzdata files with no zones. There's nothing useful in them. - // This also avoids a panic later when we add and then use a fake transition (golang.org/issue/29437). - return nil, errBadData - } - zones := make([]zone, nzone) - for i := range zones { - var ok bool - var n uint32 - if n, ok = zonedata.big4(); !ok { - return nil, errBadData - } - if uint32(int(n)) != n { - return nil, errBadData - } - zones[i].offset = int(int32(n)) - var b byte - if b, ok = zonedata.byte(); !ok { - return nil, errBadData - } - zones[i].isDST = b != 0 - if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { - return nil, errBadData - } - zones[i].name = byteString(abbrev[b:]) - } - - // Now the transition time info. - tx := make([]zoneTrans, n[NTime]) - for i := range tx { - var n int64 - if !is64 { - if n4, ok := txtimes.big4(); !ok { - return nil, errBadData - } else { - n = int64(int32(n4)) - } - } else { - if n8, ok := txtimes.big8(); !ok { - return nil, errBadData - } else { - n = int64(n8) - } - } - tx[i].when = n - if int(txzones[i]) >= len(zones) { - return nil, errBadData - } - 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 - } - } - - if len(tx) == 0 { - // Build fake transition to cover all time. - // This happens in fixed locations like "Etc/GMT0". - tx = append(tx, zoneTrans{when: alpha, index: 0}) - } - - // Committed to succeed. - l := &Location{zone: zones, tx: tx, name: name, extend: extend} - - // 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 = omega - l.cacheZone = &l.zone[tx[i].index] - if i+1 < len(tx) { - l.cacheEnd = tx[i+1].when - } else if l.extend != "" { - // If we're at the end of the known zone transitions, - // try the extend string. - if name, offset, estart, eend, isDST, ok := tzset(l.extend, l.cacheStart, sec); ok { - l.cacheStart = estart - l.cacheEnd = eend - // Find the zone that is returned by tzset to avoid allocation if possible. - if zoneIdx := findZone(l.zone, name, offset, isDST); zoneIdx != -1 { - l.cacheZone = &l.zone[zoneIdx] - } else { - l.cacheZone = &zone{ - name: name, - offset: offset, - isDST: isDST, - } - } - } - } - break - } - } - - return l, nil -} - -func findZone(zones []zone, name string, offset int, isDST bool) int { - for i, z := range zones { - if z.name == name && z.offset == offset && z.isDST == isDST { - return i - } - } - return -1 -} - -// get4 returns the little-endian 32-bit value in b. -func get4(b []byte) int { - if len(b) < 4 { - return 0 - } - return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24 -} - -// get2 returns the little-endian 16-bit value in b. -func get2(b []byte) int { - if len(b) < 2 { - return 0 - } - return int(b[0]) | int(b[1])<<8 -} - -// loadLocation returns the Location with the given name from the -// embedded data.The first timezone data matching the given name -// that is successfully loaded and parsed is returned as a Location. -func loadLocation(name string) (*Location, error) { - zoneData, ok := loadFromEmbeddedTZData(name) - if !ok { - return nil, errors.New("unknown time zone " + name) - } - - if loc, err := LoadLocationFromTZData(name, zoneData); err == nil { - return loc, nil - } - - return nil, errors.New("unknown time zone " + name) -} diff --git a/gnovm/tests/files/time0_stdlibs.gno b/gnovm/tests/files/time0_stdlibs.gno index d9f7e64d958..da1db306396 100644 --- a/gnovm/tests/files/time0_stdlibs.gno +++ b/gnovm/tests/files/time0_stdlibs.gno @@ -9,4 +9,4 @@ func main() { } // Output: -// 2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 +// 2009-02-13 23:31:30 +0000 UTC diff --git a/gnovm/tests/files/tz_locations.gno b/gnovm/tests/files/tz_locations.gno deleted file mode 100644 index 83ba201bbeb..00000000000 --- a/gnovm/tests/files/tz_locations.gno +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import "time" - -func main() { - const layout = "2006-01-02 15:04:05 -0700 MST" - - loc, err := time.LoadLocation("America/New_York") - if err != nil { - panic(err) - } - - // US eastern war time - t := time.Date(1944, time.August, 15, 0, 0, 0, 0, loc) - println(t.Format(layout)) - - // Us eastern peace time - t = t.Add(24 * time.Hour * 30 * 13) - println(t.Format(layout)) - - loc, err = time.LoadLocation("America/Chicago") - if err != nil { - panic(err) - } - - // US central time - t = time.Date(1935, time.April, 5, 11, 11, 11, 0, loc) - println(t.Format(layout)) - - // US eastern time for a bit - t = t.Add(24 * time.Hour * 365) - println(t.Format(layout)) - - // They didn't like it -- stayed light too late -- back to central - t = t.Add(24 * time.Hour * 365) - println(t.Format(layout)) - - loc, err = time.LoadLocation("Asia/Kathmandu") - if err != nil { - panic(err) - } - - // Nepalese time -- :30 off the hour - t = time.Date(1985, time.September, 17, 12, 12, 12, 0, loc) - println(t.Format(layout)) - - // :30 off the hour is too hard so let's change it to on the hour. - // Wait, no, let's switch to :45 off the hour, for convenience :) - t = t.Add(24 * time.Hour * 365) - println(t.Format(layout)) - - loc, err = time.LoadLocation("Pacific/Kwajalein") - if err != nil { - panic(err) - } - - // Marshall Islands -- where the world's day ends - t = time.Date(1993, time.July, 4, 8, 0, 0, 0, loc) - println(t.Format(layout)) - - // They didn't like that. They want to be where the world's day begins. - t = t.Add(24 * time.Hour * 60) - println(t.Format(layout)) - - loc, err = time.LoadLocation("Pacific/Guam") - if err != nil { - panic(err) - } - - // Guam - t = time.Date(1999, time.December, 25, 12, 0, 0, 0, loc) - println(t.Format(layout)) - - // Sometimes you want to change your timezone abbreviation for the sake of national identity. - // A merry Christmas indeed! - t = t.Add(24 * time.Hour * 365) - println(t.Format(layout)) - - loc, err = time.LoadLocation("Europe/Paris") - if err != nil { - panic(err) - } - - // Paris -- days of yore -- local mean time, determined by longitude - t = time.Date(1891, time.February, 14, 9, 0, 0, 0, loc) - println(t.Format(layout)) - - // Paris mean time - t = t.Add(24 * time.Hour * 365) - println(t.Format(layout)) - - // Paris in the present -- CET -- a month earlier than the original date - // due to 130 years worth of leap days. - t = t.Add(24 * time.Hour * 365 * 130) - println(t.Format(layout)) - - // Paris in the summer -- CEST - t = t.Add(24 * time.Hour * 30 * 5) - println(t.Format(layout)) -} - -// Output: -// 1944-08-15 00:00:00 -0400 EWT -// 1945-09-09 00:00:00 -0400 EPT -// 1935-04-05 11:11:11 -0600 CST -// 1936-04-04 12:11:11 -0500 EST -// 1937-04-04 11:11:11 -0600 CST -// 1985-09-17 12:12:12 +0530 +0530 -// 1986-09-17 12:27:12 +0545 +0545 -// 1993-07-04 08:00:00 -1200 -12 -// 1993-09-03 08:00:00 +1200 +12 -// 1999-12-25 12:00:00 +1000 GST -// 2000-12-24 12:00:00 +1000 ChST -// 1891-02-14 09:00:00 +0009 LMT -// 1892-02-14 09:00:00 +0009 PMT -// 2022-01-13 09:50:39 +0100 CET -// 2022-06-12 10:50:39 +0200 CEST