Merge pull request #252 from huskar-t/master
fix: GetVariantDate return with milliseconds
diff --git a/variant_date_386.go b/variant_date_386.go
index 1b970f6..973a217 100644
--- a/variant_date_386.go
+++ b/variant_date_386.go
@@ -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.")
}
diff --git a/variant_date_386_test.go b/variant_date_386_test.go
new file mode 100644
index 0000000..b572b7f
--- /dev/null
+++ b/variant_date_386_test.go
@@ -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)
+ }
+ })
+ }
+}
diff --git a/variant_date_amd64.go b/variant_date_amd64.go
index 6952f1f..5929ad7 100644
--- a/variant_date_amd64.go
+++ b/variant_date_amd64.go
@@ -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.")
}
diff --git a/variant_date_amd64_test.go b/variant_date_amd64_test.go
new file mode 100644
index 0000000..fe273a9
--- /dev/null
+++ b/variant_date_amd64_test.go
@@ -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)
+ }
+ })
+ }
+}