Skip to content
Merged
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
31 changes: 28 additions & 3 deletions variant_date_386.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
//go:build windows && 386
// +build windows,386

package ole

import (
"errors"
"math"
"syscall"
"time"
"unsafe"
)

const ONETHOUSANDMILLISECONDS = 0.0000115740740740

// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value uint64) (time.Time, error) {
halfSecond := ONETHOUSANDMILLISECONDS / 2.0
dVariantTime := math.Float64frombits(value)
var st syscall.Systemtime
v1 := uint32(value)
v2 := uint32(value >> 32)
adjustedVariantTime := dVariantTime - halfSecond
uAdjustedVariantTime := math.Float64bits(adjustedVariantTime)
v1 := uint32(uAdjustedVariantTime)
v2 := uint32(uAdjustedVariantTime >> 32)
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
fraction := dVariantTime - float64(int(dVariantTime))
hours := (fraction - float64(int(fraction))) * 24
minutes := (hours - float64(int(hours))) * 60
seconds := (minutes - float64(int(minutes))) * 60
milliseconds := (seconds - float64(int(seconds))) * 1000
milliseconds = milliseconds + 0.5
if milliseconds < 1.0 || milliseconds > 999.0 {
var st2 syscall.Systemtime
v1 = uint32(value)
v2 = uint32(value >> 32)
r2, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st2)))
if r2 != 0 {
return time.Date(int(st2.Year), time.Month(st2.Month), int(st2.Day), int(st2.Hour), int(st2.Minute), int(st2.Second), 0, time.UTC), nil
} else {
return time.Now(), errors.New("Could not convert to time, passing current time.")
}
}
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(int16(milliseconds))*1e6, time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}
111 changes: 111 additions & 0 deletions variant_date_386_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//go:build windows && 386
// +build windows,386

package ole

import (
"errors"
"math"
"reflect"
"syscall"
"testing"
"time"
"unsafe"
)

func TestGetVariantDate(t *testing.T) {
type args struct {
value uint64
}
tests := []struct {
name string
args args
want time.Time
wantErr bool
}{
{
name: "2023-10-30 23:30:30:000",
args: args{value: math.Float64bits(45229.9795138889)},
want: time.Date(2023, 10, 30, 23, 30, 30, 0, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:355",
args: args{value: math.Float64bits(45229.979518)},
want: time.Date(2023, 10, 30, 23, 30, 30, 355000000, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:960",
args: args{value: math.Float64bits(45229.979525)},
want: time.Date(2023, 10, 30, 23, 30, 30, 960000000, time.UTC),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetVariantDate(tt.args.value)
if (err != nil) != tt.wantErr {
t.Errorf("GetVariantDate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetVariantDate() got = %v, want %v", got, tt.want)
}
})
}
}

func getVariantDateWithoutMillSeconds(value uint64) (time.Time, error) {
var st syscall.Systemtime
v1 := uint32(value)
v2 := uint32(value >> 32)
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

func TestGetVariantDateWithoutMillSeconds(t *testing.T) {
type args struct {
value uint64
}
tests := []struct {
name string
args args
want time.Time
wantErr bool
}{
{
name: "2023-10-30 23:30:30:000",
args: args{value: math.Float64bits(45229.9795138889)},
want: time.Date(2023, 10, 30, 23, 30, 30, 0, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:355",
args: args{value: math.Float64bits(45229.979518)},
want: time.Date(2023, 10, 30, 23, 30, 30, 0, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:960",
args: args{value: math.Float64bits(45229.979525)},
want: time.Date(2023, 10, 30, 23, 30, 31, 0, time.UTC),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getVariantDateWithoutMillSeconds(tt.args.value)
if (err != nil) != tt.wantErr {
t.Errorf("GetVariantDate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetVariantDate() got = %v, want %v", got, tt.want)
}
})
}
}
27 changes: 25 additions & 2 deletions variant_date_amd64.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,43 @@
//go:build windows && amd64
// +build windows,amd64

package ole

import (
"errors"
"math"
"syscall"
"time"
"unsafe"
)

const ONETHOUSANDMILLISECONDS = 0.0000115740740740

// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value uint64) (time.Time, error) {
halfSecond := ONETHOUSANDMILLISECONDS / 2.0
dVariantTime := math.Float64frombits(value)
var st syscall.Systemtime
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
adjustedVariantTime := dVariantTime - halfSecond
uAdjustedVariantTime := math.Float64bits(adjustedVariantTime)
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(uAdjustedVariantTime), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
fraction := dVariantTime - float64(int(dVariantTime))
hours := (fraction - float64(int(fraction))) * 24
minutes := (hours - float64(int(hours))) * 60
seconds := (minutes - float64(int(minutes))) * 60
milliseconds := (seconds - float64(int(seconds))) * 1000
milliseconds = milliseconds + 0.5
if milliseconds < 1.0 || milliseconds > 999.0 {
var st2 syscall.Systemtime
r2, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st2)))
if r2 != 0 {
return time.Date(int(st2.Year), time.Month(st2.Month), int(st2.Day), int(st2.Hour), int(st2.Minute), int(st2.Second), 0, time.UTC), nil
} else {
return time.Now(), errors.New("Could not convert to time, passing current time.")
}
}
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(int16(milliseconds))*1e6, time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}
109 changes: 109 additions & 0 deletions variant_date_amd64_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//go:build windows && amd64
// +build windows,amd64

package ole

import (
"errors"
"math"
"reflect"
"syscall"
"testing"
"time"
"unsafe"
)

func TestGetVariantDate(t *testing.T) {
type args struct {
value uint64
}
tests := []struct {
name string
args args
want time.Time
wantErr bool
}{
{
name: "2023-10-30 23:30:30:000",
args: args{value: math.Float64bits(45229.9795138889)},
want: time.Date(2023, 10, 30, 23, 30, 30, 0, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:355",
args: args{value: math.Float64bits(45229.979518)},
want: time.Date(2023, 10, 30, 23, 30, 30, 355000000, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:960",
args: args{value: math.Float64bits(45229.979525)},
want: time.Date(2023, 10, 30, 23, 30, 30, 960000000, time.UTC),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GetVariantDate(tt.args.value)
if (err != nil) != tt.wantErr {
t.Errorf("GetVariantDate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetVariantDate() got = %v, want %v", got, tt.want)
}
})
}
}

func getVariantDateWithoutMillSeconds(value uint64) (time.Time, error) {
var st syscall.Systemtime
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

func TestGetVariantDateWithoutMillSeconds(t *testing.T) {
type args struct {
value uint64
}
tests := []struct {
name string
args args
want time.Time
wantErr bool
}{
{
name: "2023-10-30 23:30:30:000",
args: args{value: math.Float64bits(45229.9795138889)},
want: time.Date(2023, 10, 30, 23, 30, 30, 0, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:355",
args: args{value: math.Float64bits(45229.979518)},
want: time.Date(2023, 10, 30, 23, 30, 30, 0, time.UTC),
wantErr: false,
},
{
name: "2023-10-30 23:30:30:960",
args: args{value: math.Float64bits(45229.979525)},
want: time.Date(2023, 10, 30, 23, 30, 31, 0, time.UTC),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getVariantDateWithoutMillSeconds(tt.args.value)
if (err != nil) != tt.wantErr {
t.Errorf("GetVariantDate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetVariantDate() got = %v, want %v", got, tt.want)
}
})
}
}