/* * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package time // llgo:skipall import ( "unsafe" "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/time" ) 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 } // unixSec returns the time's seconds since Jan 1 1970 (Unix time). 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 } else if d > 0 { t.ext = 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() } // 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() } // Compare compares the time instant t with u. If t is before u, it returns -1; // if t is after u, it returns +1; if they're the same, it returns 0. func (t Time) Compare(u Time) int { var tc, uc int64 if t.wall&u.wall&hasMonotonic != 0 { tc, uc = t.ext, u.ext } else { tc, uc = t.sec(), u.sec() if tc == uc { tc, uc = int64(t.nsec()), int64(u.nsec()) } } switch { case tc < uc: return -1 case tc > uc: return +1 } return 0 } // 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() } // 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 ) // String returns the English name of the month ("January", "February", ...). func (m Month) String() string { if January <= m && m <= December { return longMonthNames[m-1] } buf := make([]byte, 20) n := fmtInt(buf, uint64(m)) return "%!Month(" + string(buf[n:]) + ")" } // A Weekday specifies a day of the week (Sunday = 0, ...). type Weekday int const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday ) // String returns the English name of the day ("Sunday", "Monday", ...). func (d Weekday) String() string { if Sunday <= d && d <= Saturday { return longDayNames[d] } buf := make([]byte, 20) n := fmtInt(buf, uint64(d)) return "%!Weekday(" + string(buf[n:]) + ")" } const ( secondsPerMinute = 60 secondsPerHour = 60 * secondsPerMinute secondsPerDay = 24 * secondsPerHour secondsPerWeek = 7 * secondsPerDay daysPer400Years = 365*400 + 97 daysPer100Years = 365*100 + 24 daysPer4Years = 365*4 + 1 ) // 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) } // 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 // 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]) } // daysSinceEpoch takes a year and returns the number of days from // the absolute epoch to the start of that year. // This is basically (year - zeroYear) * 365, but accounting for leap days. func daysSinceEpoch(year int) uint64 { y := uint64(int64(year) - absoluteZeroYear) // 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 return d } // Provided by package runtime. func now() (sec int64, nsec int32, mono int64) { tv := (*time.Timespec)(c.Alloca(unsafe.Sizeof(time.Timespec{}))) time.ClockGettime(time.CLOCK_REALTIME, tv) sec = int64(tv.Sec) nsec = int32(tv.Nsec) mono = runtimeNano() return } // runtimeNano returns the current value of the runtime clock in nanoseconds. func runtimeNano() int64 { tv := (*time.Timespec)(c.Alloca(unsafe.Sizeof(time.Timespec{}))) time.ClockGettime(time.CLOCK_MONOTONIC, tv) return int64(tv.Sec)<>33 != 0 { // Seconds field overflowed the 33 bits available when // storing a monotonic time. This will be true after // March 16, 2157. return Time{uint64(nsec), sec + minWall, Local} } return Time{hasMonotonic | uint64(sec)< 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) } // Microseconds returns the duration as an integer microsecond count. func (d Duration) Microseconds() int64 { return int64(d) / 1e3 } // Milliseconds returns the duration as an integer millisecond count. func (d Duration) Milliseconds() int64 { return int64(d) / 1e6 } // 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)/1e9 } // 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)/(60*1e9) } // 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)/(60*60*1e9) } // Truncate returns the result of rounding d toward zero to a multiple of m. // If m <= 0, Truncate returns d unchanged. func (d Duration) Truncate(m Duration) Duration { if m <= 0 { return d } return d - d%m } // lessThanHalf reports whether x+x < y but avoids overflow, // assuming x and y are both positive (Duration is signed). func lessThanHalf(x, y Duration) bool { return uint64(x)+uint64(x) < uint64(y) } // Round returns the result of rounding d to the nearest multiple of m. // The rounding behavior for halfway values is to round away from zero. // If the result exceeds the maximum (or minimum) // value that can be stored in a Duration, // Round returns the maximum (or minimum) duration. // If m <= 0, Round returns d unchanged. func (d Duration) Round(m Duration) Duration { if m <= 0 { return d } r := d % m if d < 0 { r = -r if lessThanHalf(r, m) { return d + r } if d1 := d - m + r; d1 < d { return d1 } return minDuration // overflow } if lessThanHalf(r, m) { return d - r } if d1 := d + m - r; d1 > d { return d1 } return maxDuration // overflow } // Abs returns the absolute value of d. // As a special case, math.MinInt64 is converted to math.MaxInt64. func (d Duration) Abs() Duration { switch { case d >= 0: return d case d == minDuration: return maxDuration default: return -d } } // Add returns the time t+d. func (t Time) Add(d Duration) Time { dsec := int64(d / 1e9) nsec := t.nsec() + int32(d%1e9) if nsec >= 1e9 { dsec++ nsec -= 1e9 } else if nsec < 0 { 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 } // Sub returns the duration t-u. If the result exceeds the maximum (or minimum) // value that can be stored in a Duration, the maximum (or minimum) duration // 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 } 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 } } // 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) } // 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) } // 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) // Compute days since the absolute epoch. d := daysSinceEpoch(year) // 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 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 } func unixTime(sec int64, nsec int32) Time { return Time{uint64(nsec), sec + unixToInternal, Local} } // 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]. // Not all sec values have a corresponding time value. One such // value is 1<<63-1 (the largest int64 value). 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 unixTime(sec, int32(nsec)) } // UnixMilli returns the local Time corresponding to the given Unix time, // msec milliseconds since January 1, 1970 UTC. func UnixMilli(msec int64) Time { return Unix(msec/1e3, (msec%1e3)*1e6) } // UnixMicro returns the local Time corresponding to the given Unix time, // usec microseconds since January 1, 1970 UTC. func UnixMicro(usec int64) Time { return Unix(usec/1e6, (usec%1e6)*1e3) } 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 } // 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 } _, r := div(t, d) if lessThanHalf(r, d) { return t.Add(-r) } return t.Add(d - r) } // div divides t by d and returns the quotient parity and remainder. // We don't use the quotient parity anymore (round half up instead of round to even) // 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() if sec < 0 { // Operate on absolute value. neg = true sec = -sec nsec = -nsec if nsec < 0 { nsec += 1e9 sec-- // sec >= 1 before the -- so safe } } switch { // Special case: 2d divides 1 second. case d < Second && Second%(d+d) == 0: qmod2 = int(nsec/int32(d)) & 1 r = Duration(nsec % int32(d)) // Special case: d is a multiple of 1 second. case d%Second == 0: d1 := int64(d / Second) qmod2 = int(sec/d1) & 1 r = Duration(sec%d1)*Second + Duration(nsec) // General case. // This could be faster if more cleverness were applied, // but it's really only here to avoid special case restrictions in the API. // No one will care about these cases. default: // Compute nanoseconds as 128-bit number. sec := uint64(sec) tmp := (sec >> 32) * 1e9 u1 := tmp >> 32 u0 := tmp << 32 tmp = (sec & 0xFFFFFFFF) * 1e9 u0x, u0 := u0, u0+tmp if u0 < u0x { u1++ } u0x, u0 = u0, u0+uint64(nsec) if u0 < u0x { u1++ } // Compute remainder by subtracting r<>63 != 1 { d1 <<= 1 } d0 := uint64(0) for { qmod2 = 0 if u1 > d1 || u1 == d1 && u0 >= d0 { // subtract qmod2 = 1 u0x, u0 = u0, u0-d0 if u0 > u0x { u1-- } u1 -= d1 } if d1 == 0 && d0 == uint64(d) { break } d0 >>= 1 d0 |= (d1 & 1) << 63 d1 >>= 1 } r = Duration(u0) } if neg && r != 0 { // If input was negative and not an exact multiple of d, we computed q, r such that // q*d + r = -t // But the right answers are given by -(q-1), d-r: // q*d + r = -t // -q*d - r = t // -(q-1)*d + (d - r) = t qmod2 ^= 1 r = d - r } return }