Last week I read a blog entry by Raymond Chan regarding the way Task Manager computes the systems up-time and it reminded me of a bug I noticed in AID64's implementation. On Sunday I had a bit of free time before the Manchester United vs Arsenal game kicked off and decided to see what I could come up with.
There are a dozen different methods for calculating up-time, some methods are better and some of these do factor in leap years. Raymond's particular blog mentioned the use of GetTickCount API (contradictory to what he implies it does include sleep time), that method seems a little long winded if your end result is to format the result in to; years, month, days, hours, minutes and seconds. I'll show you a simpler way by using NtQuerySystemInformation and taking advantage of Windows API time functions. You can also use this method if you want to compute the time between two different dates such as how old someone is, although Windows Calculator has that covered with it's date calculations. Of course the example code below is in PureBasic, if you have queries regarding the code please comment...
Declare UpdateSystemTime(void) If OpenWindow(0, 0, 0, 400, 65, "System Up-Time Since BootTime...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget(1, 10, 10, 95, 20, "System Boot Time :") TextGadget(2, 105, 10, 200, 20, "") TextGadget(3, 10, 35, 50, 20, "UpTime :") TextGadget(4, 60, 35, 400, 20, "") CreateThread(@UpdateSystemTime(), #Null) Repeat MyEvent = WaitWindowEvent() Until MyEvent = #PB_Event_CloseWindow EndIf Procedure UpdateSystemTime(void) #SystemTimeOfDayInformation = 3 #STATUS_SUCCESS = 0 Structure SYSTEM_TIMEOFDAY_INFORMATION ; We are only concerned about BootTime and CurrentTime members of this structure. BootTime.FILETIME CurrentTime.FILETIME EndStructure Protected SystemBootTime.SYSTEM_TIMEOFDAY_INFORMATION ; SystemInformation parameter should be large enough to hold an opaque SYSTEM_TIMEOFDAY_INFORMATION structure. Repeat ; Use NtQuerySystemInformation to retrieve BootTime and Current time information. Note "NtQuerySystemInformation may be altered or unavailable in future versions of Windows". If NtQuerySystemInformation_(#SystemTimeOfDayInformation, @SystemBootTime, SizeOf(SystemBootTime), #Null) = #STATUS_SUCCESS ; Subtract the high and low order parts of the file time from LocalTime and BootTime to calculate the time and date difference. SystemBootTime\CurrentTime\dwLowDateTime - SystemBootTime\BootTime\dwLowDateTime SystemBootTime\CurrentTime\dwHighDateTime - SystemBootTime\BootTime\dwHighDateTime ; Convert the FILETIME structure into a time that is easy to display to a user. FileTimeToSystemTime_(@SystemBootTime\BootTime, @lpSystemTime_BootTime.SYSTEMTIME) FileTimeToSystemTime_(@SystemBootTime\CurrentTime, @lpSystemTime_CurrentTime.SYSTEMTIME) ; Convert BootTime time in Coordinated Universal Time (UTC) to local time. SystemTimeToTzSpecificLocalTime_(#Null, @lpSystemTime_BootTime, @lpSystemTime_BootTime) ; Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC) - offset from this date. lpSystemTime_CurrentTime\wYear - 1601 lpSystemTime_CurrentTime\wMonth - 1 lpSystemTime_CurrentTime\wDay - 1 ; Display date and time difference as UpTime Since Last Reboot... SetGadgetText(2, Str(lpSystemTime_BootTime\wDay) + "/" + Str(lpSystemTime_BootTime\wMonth) + "/" + Str(lpSystemTime_BootTime\wYear) + " @ " + Str(lpSystemTime_BootTime\wHour) + ":" + Str(lpSystemTime_BootTime\wMinute) + ":" + Str(lpSystemTime_BootTime\wSecond)) SetGadgetText(4, Str(lpSystemTime_CurrentTime\wYear) + " years, " + Str(lpSystemTime_CurrentTime\wMonth) + " months, " + Str(lpSystemTime_CurrentTime\wDay) + " days, (" + Str(lpSystemTime_CurrentTime\wHour) + " hours, " + Str(lpSystemTime_CurrentTime\wMinute) + " minutes, " + Str(lpSystemTime_CurrentTime\wSecond) + " seconds)") ; Cycle once every second... Sleep_(1000) Else MessageBox_(#Null, "Could not retrieve system information...", "Peanut!", #MB_ICONERROR | #MB_TOPMOST | #MB_SETFOREGROUND) Break EndIf ForEver EndProcedure