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
45 changes: 43 additions & 2 deletions src/crystal/system/win32/time.cr
Original file line number Diff line number Diff line change
@@ -1,9 +1,50 @@
require "c/winbase"
require "winerror"

module Crystal::System::Time
# Win32 epoch is 1601-01-01 00:00:00 UTC
WINDOWS_EPOCH_IN_SECONDS = 50_491_123_200_i64

# Resolution of FILETIME is 100 nanoseconds
NANOSECONDS_PER_FILETIME_TICK = 100

NANOSECONDS_PER_SECOND = 1_000_000_000
FILETIME_TICKS_PER_SECOND = NANOSECONDS_PER_SECOND / NANOSECONDS_PER_FILETIME_TICK

# TODO: For now, this method returns the UTC offset currently in place, ignoring *seconds*.
def self.compute_utc_offset(seconds : Int64) : Int32
raise NotImplementedError.new("Crystal::System::Time.compute_utc_offset")
ret = LibC.GetTimeZoneInformation(out zone_information)
raise WinError.new("GetTimeZoneInformation") if ret == -1

zone_information.bias.to_i32 * -60
end

def self.compute_utc_seconds_and_nanoseconds : {Int64, Int32}
raise NotImplementedError.new("Crystal::System::Time.compute_utc_seconds_and_nanoseconds")
# TODO: Needs a check if `GetSystemTimePreciseAsFileTime` is actually available (only >= Windows 8)
# and use `GetSystemTimeAsFileTime` as fallback.
LibC.GetSystemTimePreciseAsFileTime(out filetime)
since_epoch = (filetime.dwHighDateTime.to_u64 << 32) | filetime.dwLowDateTime.to_u64

seconds = (since_epoch / FILETIME_TICKS_PER_SECOND).to_i64 + WINDOWS_EPOCH_IN_SECONDS
nanoseconds = since_epoch.remainder(FILETIME_TICKS_PER_SECOND).to_i32 * NANOSECONDS_PER_FILETIME_TICK

{seconds, nanoseconds}
end

@@performance_frequency : Int64 = begin
ret = LibC.QueryPerformanceFrequency(out frequency)
if ret == 0
raise WinError.new("QueryPerformanceFrequency")
end

frequency
end

def self.monotonic : {Int64, Int32}
if LibC.QueryPerformanceCounter(out ticks) == 0
raise WinError.new("QueryPerformanceCounter")
end

{ticks / @@performance_frequency, (ticks.remainder(NANOSECONDS_PER_SECOND) * NANOSECONDS_PER_SECOND / @@performance_frequency).to_i32}
end
end
4 changes: 4 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/win_def.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
lib LibC
alias WORD = UInt16
alias BOOL = Int32
end
2 changes: 2 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/win_nt.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
lib LibC
alias LPSTR = Char*
alias LONG = Int32
alias WCHAR = UInt16
end
34 changes: 34 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/winbase.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "c/win_nt"
require "c/win_def"
require "c/int_safe"

lib LibC
Expand All @@ -13,4 +14,37 @@ lib LibC

fun FormatMessageA(dwFlags : DWORD, lpSource : Void*, dwMessageId : DWORD, dwLanguageId : DWORD,
lpBuffer : LPSTR, nSize : DWORD, arguments : Void*) : DWORD

struct FILETIME
dwLowDateTime : DWORD
dwHighDateTime : DWORD
end

struct SYSTEMTIME
wYear : WORD
wMonth : WORD
wDayOfWeek : WORD
wDay : WORD
wHour : WORD
wMinute : WORD
wSecond : WORD
wMilliseconds : WORD
end

struct TIME_ZONE_INFORMATION
bias : LONG
standardName : StaticArray(WCHAR, 32)
standardDate : SYSTEMTIME
standardBias : LONG
daylightName : StaticArray(WCHAR, 32)
daylightDate : SYSTEMTIME
daylightBias : LONG
end

fun GetTimeZoneInformation(tz_info : TIME_ZONE_INFORMATION*) : DWORD
fun GetSystemTimeAsFileTime(time : FILETIME*)
fun GetSystemTimePreciseAsFileTime(time : FILETIME*)

fun QueryPerformanceCounter(performance_count : Int64*) : BOOL
fun QueryPerformanceFrequency(frequency : Int64*) : BOOL
end