Skip to content

windows: fix filetime conversions and deprecate Nanoseconds #251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions windows/registry/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"runtime"
"syscall"
"time"

"golang.org/x/sys/windows"
)

const (
Expand Down Expand Up @@ -193,18 +195,23 @@ type KeyInfo struct {
ValueCount uint32
MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte
MaxValueLen uint32 // longest data component among the key's values, in bytes
lastWriteTime syscall.Filetime
lastWriteTime windows.Filetime
}

// ModTime returns the key's last write time.
func (ki *KeyInfo) ModTime() time.Time {
return time.Unix(0, ki.lastWriteTime.Nanoseconds())
return time.Unix(ki.lastWriteTime.Unix())
}

// ModTimeZero reports whether the key's last write time is zero.
func (ki *KeyInfo) ModTimeZero() bool {
return ki.lastWriteTime.LowDateTime == 0 && ki.lastWriteTime.HighDateTime == 0
}

// Stat retrieves information about the open key k.
func (k Key) Stat() (*KeyInfo, error) {
var ki KeyInfo
err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
err := windows.RegQueryInfoKey(windows.Handle(k), nil, nil, nil,
&ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
&ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions windows/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ func Ftruncate(fd Handle, length int64) (err error) {
func Gettimeofday(tv *Timeval) (err error) {
var ft Filetime
GetSystemTimeAsFileTime(&ft)
*tv = NsecToTimeval(ft.Nanoseconds())
*tv = FiletimeToTimeval(ft)
return nil
}

Expand Down Expand Up @@ -771,8 +771,8 @@ func Utimes(path string, tv []Timeval) (err error) {
return e
}
defer CloseHandle(h)
a := NsecToFiletime(tv[0].Nanoseconds())
w := NsecToFiletime(tv[1].Nanoseconds())
a := TimevalToFiletime(tv[0])
w := TimevalToFiletime(tv[1])
return SetFileTime(h, nil, &a, &w)
}

Expand Down
71 changes: 60 additions & 11 deletions windows/types_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,10 +763,33 @@ func (tv *Timeval) Nanoseconds() int64 {
return (int64(tv.Sec)*1e6 + int64(tv.Usec)) * 1e3
}

func TimevalToFiletime(tv Timeval) Filetime {
// Convert to 100-nanosecond intervals
hsec := uint64(tv.Sec)*1e7 + uint64(tv.Usec)/100
// Change starting time to January 1, 1601
// Note: No overflow here (11644473600*1e7 < math.MaxUint64/1e2)
hsec += 116444736000000000
// Split into high / low.
return Filetime{
LowDateTime: uint32(hsec & 0xffffffff),
HighDateTime: uint32(hsec >> 32 & 0xffffffff),
}
}

// NsecToTimeval converts a nanosecond value nsec to a Timeval tv. The result is
// undefined if the nanosecond value cannot be represented by a Timeval (values
// equivalent to dates before the year 1901 or after the year 2038).
func NsecToTimeval(nsec int64) (tv Timeval) {
tv.Sec = int32(nsec / 1e9)
tv.Usec = int32(nsec % 1e9 / 1e3)
return
// Ignore overflow (math.MaxInt64/1e9 > math.MaxInt32)
sec := int32(nsec / 1e9)
usec := int32(nsec % 1e9 / 1e3)
if usec < 0 {
usec += 1e6
sec--
}
tv.Sec = sec
tv.Usec = usec
return tv
}

type Overlapped struct {
Expand All @@ -789,22 +812,48 @@ type Filetime struct {
HighDateTime uint32
}

// Nanoseconds returns Filetime ft in nanoseconds
// Unix returns ft in seconds and nanoseconds
// since Epoch (00:00:00 UTC, January 1, 1970).
func (ft *Filetime) Nanoseconds() int64 {
func (ft *Filetime) Unix() (sec, nsec int64) {
// 100-nanosecond intervals since January 1, 1601
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
// change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
nsec -= 116444736000000000
// convert into nanoseconds
nsec *= 100
return nsec
hsec := uint64(ft.HighDateTime)<<32 + uint64(ft.LowDateTime)
// Convert _before_ gauging; the nanosecond difference between Epoch (00:00:00
// UTC, January 1, 1970) and Filetime's zero offset (January 1, 1601) is out
// of bounds for int64: -11644473600*1e7*1e2 < math.MinInt64
sec = int64(hsec/1e7) - 11644473600
nsec = int64(hsec%1e7) * 100
return sec, nsec
}

// Nanoseconds returns ft in nanoseconds since Epoch (00:00:00 UTC,
// January 1, 1970). The result is undefined if the Filetime cannot be
// represented by an int64 (values equivalent to dates before the year
// 1677 or after the year 2262). Note that this explicitly excludes the
// zero value of Filetime, which is equivalent to January 1, 1601.
//
// Deprecated: use Unix instead, which returns both seconds and
// nanoseconds thus covers the full available range of Filetime.
func (ft *Filetime) Nanoseconds() int64 {
sec, nsec := ft.Unix()
return (sec*1e9 + nsec)
}

// FiletimeToTimeval converts a Filetime ft to a Timeval tv. The result is
// undefined if the Filetime cannot be represented by a Timeval (values
// equivalent to dates before the year 1901 or after the year 2038).
func FiletimeToTimeval(ft Filetime) (tv Timeval) {
sec, nsec := ft.Unix()
// Ignore overflow (math.MaxUint64*1e2/1e9 > math.MaxInt32)
tv.Sec = int32(sec)
tv.Usec = int32(nsec / 1e3)
return tv
}

func NsecToFiletime(nsec int64) (ft Filetime) {
// convert into 100-nanosecond
nsec /= 100
// change starting time to January 1, 1601
// note: no overflow here (11644473600*1e7 < math.MaxInt64/1e1)
nsec += 116444736000000000
// split into high / low
ft.LowDateTime = uint32(nsec & 0xffffffff)
Expand Down