Skip to content

Instantly share code, notes, and snippets.

@unixdj
Created December 9, 2023 00:31
Show Gist options
  • Save unixdj/e31f75d5b49b5a2b3b4fbbae69368937 to your computer and use it in GitHub Desktop.
Save unixdj/e31f75d5b49b5a2b3b4fbbae69368937 to your computer and use it in GitHub Desktop.
Go #63844 absDate and Date using uint64
// 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 main
import (
"fmt"
"time"
)
const (
hasMonotonic = 1 << 63
maxWall = wallToInternal + (1<<33 - 1) // year 2157
minWall = wallToInternal // year 1885
nsecMask = 1<<30 - 1
nsecShift = 30
)
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
// 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
wallToInternal int64 = (1884*365 + 1884/4 - 1884/100 + 1884/400) * secondsPerDay
)
const (
secondsPerMinute = 60
secondsPerHour = 60 * secondsPerMinute
secondsPerDay = 24 * secondsPerHour
secondsPerWeek = 7 * secondsPerDay
daysPer400Years = 365*400 + 97
daysPer100Years = 365*100 + 24
daysPer4Years = 365*4 + 1
)
// The algorithm is figure 12 of Neri, Schneider, "Euclidean affine functions
// and their application to calendar algorithms".
// https://onlinelibrary.wiley.com/doi/full/10.1002/spe.3172
func absDate(abs uint64, full bool) (year int, month time.Month, day int) {
// daysAbs := int64(abs / secondsPerDay)
// daysUnix := int64(daysAbs - (unixToInternal+internalToAbsolute)/secondsPerDay)
daysUnix := abs/secondsPerDay - 59
// N := uint64(daysUnix + K)
// Century
N_1 := 4*daysUnix + 3
C := N_1 / 146097
// Year
R := N_1 % 146097
N_2 := R | 3
P_2 := 2939745 * uint64(N_2)
Z := uint32(P_2 / 4294967296)
N_Y := uint32(P_2%4294967296) / 2939745 / 4
J := 0
if N_Y >= 306 {
J = 1
}
Y := 100*C + uint64(Z)
year = int(Y) + J + absoluteZeroYear
if !full {
return
}
// Month and day
N_3 := 2141*N_Y + 197913
M := N_3 / 65536
D := N_3 % 65536 / 2141
month = time.Month(M)
if J == 1 {
month = time.Month(M - 12)
}
day = int(D + 1)
return
}
// The algorithm is basicaly figure 12 of Neri, Schneider, "Euclidean affine functions
// and their application to calendar algorithms", adapted to calculate yday.
// https://onlinelibrary.wiley.com/doi/full/10.1002/spe.3172
func absDateWithYday(abs uint64, full bool) (year int, month time.Month, day int, yday int) {
daysAbs := int64(abs / secondsPerDay)
daysUnix := int32(daysAbs - (unixToInternal+internalToAbsolute)/secondsPerDay)
// Shift and correction constants.
s := uint32(3670) // chosen so that 1970 is roughly in the middle of the range
K := uint32(719468 + 146097*s)
L := int(400 * s)
N := uint32(daysUnix) + K
// Century
N_1 := 4*N + 3
C := N_1 / 146097
// Year
R := N_1 % 146097
N_2 := R | 3
P_2 := 2939745 * uint64(N_2)
Z := uint32(P_2 / 4294967296)
N_Y := uint32(P_2%4294967296) / 2939745 / 4
isLeapYear := 0
if Z != 0 {
if Z%4 == 0 {
isLeapYear = 1
}
} else if C%4 == 0 {
isLeapYear = 1
}
J := 0
if N_Y >= 306 {
J = 1
}
yday = int(N_Y)
if J == 1 {
yday = yday - 306
} else {
yday = yday + 31 + 28 + isLeapYear
}
Y := 100*C + Z
year = int(Y) - L + J
if !full {
return
}
// Month and day
N_3 := 2141*N_Y + 197913
M := N_3 / 65536
D := N_3 % 65536 / 2141
month = time.Month(M)
if J == 1 {
month = time.Month(M - 12)
}
day = int(D + 1)
return
}
func absYearDay(abs uint64) int {
daysAbs := int64(abs / secondsPerDay)
daysUnix := int32(daysAbs - (unixToInternal+internalToAbsolute)/secondsPerDay)
// Shift and correction constants.
s := uint32(3670) // chosen so that 1970 is roughly in the middle of the range
K := uint32(719468 - 306 + 146097*s)
N := uint32(daysUnix) + K
// Century
N_1 := 4*N + 3
// Year
R := N_1 % 146097
N_2 := R | 3
P_2 := 2939745 * uint64(N_2)
N_Y := uint32(P_2%4294967296) / 2939745 / 4
return int(N_Y)
}
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
}
func daysSinceEpoch(year int, month time.Month, day int) uint64 {
// s := uint32(3670) // chosen so that 1970 is roughly in the middle of the range
// K := uint32(719468 + 146097*s)
// L := int(400 * s)
y := uint64(int64(year) - absoluteZeroYear)
if month < 3 {
y--
month += 12
}
c := y / 100
y_star := 1461*y/4 - c + c/4
m_star := (153*uint64(month) - 457) / 5
n := y_star + m_star + uint64(day)
return uint64(n + 58)
}
// 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.
//
// The normalized year must be in [-32767, 32767].
// For example, for month==13 and year==2020, the Date converts to
// January 2021.
//
// 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 or if normalized year is out of the range [-32767, 32767].
func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) time.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)
// if year > 32767 || year < -32767 {
// panic("time: year is out of range [-32767, 32767]")
// }
month = time.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)
d := daysSinceEpoch(year, month, day)
unix := int64(int(d)*secondsPerDay+hour*secondsPerHour+min*secondsPerMinute+sec) + (absoluteToInternal + internalToUnix)
// Compute days since the absolute epoch.
// d := daysSinceEpochOriginal(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)
t := time.Unix(unix, int64(nsec))
return t
}
func main() {
a := time.Now().Unix() + unixToInternal + internalToAbsolute
fmt.Println(absDate(uint64(a), true))
a = time.Date(-absoluteZeroYear, 5, 23, 0, 0, 0, 0, time.UTC).Unix() + unixToInternal + internalToAbsolute
fmt.Println(absDate(uint64(a), true))
t := Date(2023, 5, 23, 0, 0, 0, 0, time.UTC)
fmt.Println(t)
t = Date(-absoluteZeroYear, 5, 23, 0, 0, 0, 0, time.UTC)
fmt.Println(t)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment