:: Calculate the length of the NTTE parameter
call :StrLen %1
set /a NTTE_LENGTH = %StrLen%
:: NTTE conversion primer
::
:: Since we can only use 32 bit signed numbers in a shell script, we will need to
:: break the number into a high order 32 bit number, and low order 32 bit number.
:: We use the high order number to calculate the number of days since 1601, and
:: then add the remainder to the low order number. From there we calculate days,
:: hours, minutes, etc.
::
:: We take into account leap years, and start calculating the year. Here are the
:: rules for leap years (I'm an old 'C' dev, with it):
::
:: if (year % 4 == 0) {
:: if (year % 100 == 0) {
:: if (year % 400 == 0)
:: We have a leap year
:: else
:: Not a leap year
:: }
:: else
:: We have a leap year
:: }
:: else
:: Not a leap year
::
:: Now we know how to convert NTTE to UTC. Grab the GMT offset from the registry,
:: and adjust the hours and recalculate to figure out UTC to local time.
::
:: Don't bother trying to convert NTTE to centuries, years, etc. It won't work, and
:: you get math errors. The key is to convert it to days, and then convert the days
:: to centuries, years, etc. Fair enough?
:: Split the NTTE number into high and low order numbers. Add the high order part
:: to the low order part if it is small enough.
::
:: High order part of number of ticks in a day (actually 864,000,000,000)
set TICK_DAY=864
:: High order part of number of ticks in an hour (actually 36,000,000,000)
set TICK_HOUR=36000000
:: High order part of number of ticks in a minute (actually 600,000,000)
set TICK_MINUTE=600000
:: Number of ticks in a second
set TICK_SECOND=10000000
:: Number of ticks in a millisecond
set TICK_MILLISEC=10000
:: Number of days in a year
set /a DAYS_IN_YEAR=365
:: Number of days in 4 years
set /a DAYS_IN_4YEARS=%DAYS_IN_YEAR% * 4 + 1
:: Number of days in 100 years
set /a DAYS_IN_100YEARS=%DAYS_IN_4YEARS% * 25 - 1
:: Number of days in 400 years
set /a DAYS_IN_400YEARS=%DAYS_IN_100YEARS% * 4 + 1
set NTTE_TIME=%1
:: Break the NTTE number into high order and low order parts
if %NTTE_LENGTH% GTR 9 (
set NTTE_HIGH=%NTTE_TIME:~0,-9%
set NTTE_LOW=%NTTE_TIME:~-9%
) else (
set /a NTTE_HIGH=0
set NTTE_LOW=%NTTE_TIME%
)
::echo high=%NTTE_HIGH%
::echo low=%NTTE_LOW%
:: Calculate days, and store remainder for later processing
if %NTTE_HIGH% GTR %TICK_DAY% (
set /a UTC_DAYS=%NTTE_HIGH% / %TICK_DAY%
set /a REMAINDER=%NTTE_HIGH% - !UTC_DAYS! * %TICK_DAY%
) else (
set /a UTC_DAYS=0
set /a REMAINDER=%NTTE_HIGH%
)
::echo days=%UTC_DAYS%
:: Go ahead and add in some low order bits to the number
set /a REMAINDER=%REMAINDER%%NTTE_LOW:~0,-3%
::echo remainder=%REMAINDER%
:: Calculate hours, and store remainder for later processing
if %REMAINDER% GTR %TICK_HOUR% (
set /a UTC_HOURS=%REMAINDER% / %TICK_HOUR%
set /a REMAINDER=%REMAINDER% - !UTC_HOURS! * %TICK_HOUR%
) else (
set /a UTC_HOURS=0
)
::echo hours=%UTC_HOURS%
::echo remainder=%REMAINDER%
:: Calculate minutes, and store remainder for later processing
if %REMAINDER% GTR %TICK_MINUTE% (
set /a UTC_MINUTES=%REMAINDER% / %TICK_MINUTE%
set /a REMAINDER=%REMAINDER% - !UTC_MINUTES! * %TICK_MINUTE%
) else (
set /a UTC_MINUTES=0
)
::echo minutes=%UTC_MINUTES%
:: At this point, we need to add in the remaining low order bits
set /a REMAINDER=%REMAINDER%%NTTE_LOW:~-3%
::echo remainder=%REMAINDER%
:: Calculate seconds, and store remainder for later processing
if %REMAINDER% GTR %TICK_SECOND% (
set /a UTC_SECONDS=%REMAINDER% / %TICK_SECOND%
set /a REMAINDER=%REMAINDER% - !UTC_SECONDS! * %TICK_SECOND%
) else (
set /a UTC_SECONDS=0
)
::echo seconds=%UTC_SECONDS%
::echo remainder=%REMAINDER%
:: Calculate milliseconds, and store remainder for later processing
if %REMAINDER% GTR %TICK_MILLISEC% (
set /a UTC_MILLISEC=%REMAINDER% / %TICK_MILLISEC%
set /a REMAINDER=%REMAINDER% - !UTC_MILLISEC! * %TICK_MILLISEC%
) else (
set /a UTC_MILLISEC=0
)
::echo milliseconds=%UTC_MILLISEC%
set /a UTC_NANOSEC=%REMAINDER%
:echo nanoseconds=%UTC_NANOSEC%
:: OK, now we are ready to calculate the years from the days
set /a QUAD_CENTURY=%UTC_DAYS% / %DAYS_IN_400YEARS%
set /a REMAINDER=%UTC_DAYS% - %QUAD_CENTURY% * %DAYS_IN_400YEARS%
::echo QUAD_CENTURY:%QUAD_CENTURY%
::echo REMAINDER:%REMAINDER%
set /a CENTURY=%REMAINDER% / %DAYS_IN_100YEARS%
set /a REMAINDER=%REMAINDER% - %CENTURY% * %DAYS_IN_100YEARS%
::echo CENTURY:%CENTURY%
::echo REMAINDER:%REMAINDER%
set /a QUAD_YEAR=%REMAINDER% / %DAYS_IN_4YEARS%
set /a REMAINDER=%REMAINDER% - %QUAD_YEAR% * %DAYS_IN_4YEARS%
::echo QUAD_YEAR:%QUAD_YEAR%
::echo REMAINDER:%REMAINDER%
set /a YEARS=%REMAINDER% / %DAYS_IN_YEAR%
set /a REMAINDER=%REMAINDER% - %YEARS% * %DAYS_IN_YEAR%
::echo YEARS:%YEARS%
::echo REMAINDER:%REMAINDER%
set /a UTC_YEAR=1601 + (%QUAD_CENTURY% * 400) + (%CENTURY% * 100) + (%QUAD_YEAR% * 4) + %YEARS%
::echo %UTC_YEAR%
:: See if the year is a leap year, so we can calculate the month and day
set /a LEAP=%UTC_YEAR% %% 4
if %LEAP% == 0 (
set /a LEAP=%UTC_YEAR% %% 100
if %LEAP% == 0 (
set /a LEAP=%UTC_YEAR% %% 400
if %LEAP% == 0 (
set /a LEAP=1
) else (
set /a LEAP=0
)
) else (
set /a LEAP=1
)
) else (
set /a LEAP=0
)
::echo leap:%LEAP%
:: Now we are ready to figure out the month, REMAINDER contains the day
:: in the current year. We add 1 to is because the day starts at 1 not 0
set /a UTC_DAY=%REMAINDER% + 1
set /a UTC_MONTH=1
set /a LAST=0
set /a TMP_DAY=0
for %%a in (31,59,90,120,151,181,212,243,273,304,334) do (
if %%a GTR 31 (set /a TMP_DAY=%%a + %LEAP%) else set /a TMP_DAY=%%a
if %UTC_DAY% LEQ !TMP_DAY! goto day_calc_exit
set /a UTC_MONTH=!UTC_MONTH! + 1
if %%a GTR 31 (set /a LAST=%%a + %LEAP%) else set /a LAST=%%a
)
:day_calc_exit
set /a UTC_DAY=%UTC_DAY%-%LAST%
::echo month:%UTC_MONTH%
::echo day:%UTC_DAY%
:: Extract the GMT offset (in minutes) from the registry, so we can calculate local time
::
call :get_gmt_offset
set /a LOCAL_OFFSET=%get_gmt_offset%
::echo local offset:%LOCAL_OFFSET%
set /a LOCAL_DAY=%UTC_DAY%
set /a LOCAL_MONTH=%UTC_MONTH%
set /a LOCAL_YEAR=%UTC_YEAR%
:: Calculate local time offset in hours and minutes
set /a OFFSET_HOURS=%LOCAL_OFFSET% / 60
set /a OFFSET_MINUTES=%LOCAL_OFFSET% - %OFFSET_HOURS% * 60
::echo offset %OFFSET_HOURS%:%OFFSET_MINUTES%
:: Adjust the minutes, and roll the hour back or forward if necessary
set /a LOCAL_MINUTES=%UTC_MINUTES%+%OFFSET_MINUTES%
if %LOCAL_MINUTES% LSS 0 (
set /a OFFSET_HOURS=%OFFSET_HOURS%-1
set /a LOCAL_MINUTES=60+%LOCAL_MINUTES%
) else (
if %LOCAL_MINUTES% GTR 59 (
set /a OFFSET_HOURS=%OFFSET_HOURS%+1
set /a LOCAL_MINUTES=%LOCAL_MINUTES%-60
)
)
:: Adjust the hours, and roll the day back or forward if necessary
set /a LOCAL_HOURS=%UTC_HOURS%+%OFFSET_HOURS%
::echo local hrs:%LOCAL_HOURS%
if %LOCAL_HOURS% LSS 0 (
set /a LOCAL_DAY=%LOCAL_DAY%-1
set /a LOCAL_HOURS=24+%LOCAL_HOURS%
) else (
if %LOCAL_HOURS% GTR 23 (
set /a LOCAL_DAY=%LOCAL_DAY%+1
set /a LOCAL_HOURS=%LOCAL_HOURS%-23
)
)
:: Do a final check on the day to see if we need to roll the month or year
call :fix_day_month %LOCAL_DAY% %LOCAL_MONTH% %LEAP%
echo.
set BANNER="UTC Time is"
call :print_utc_time %UTC_DAY% %UTC_MONTH% %UTC_YEAR% %UTC_HOURS% %UTC_MINUTES% %UTC_SECONDS% %UTC_MILLISEC% %UTC_NANOSEC% %BANNER%
set BANNER="Local Time is"
call :print_utc_time %LOCAL_DAY% %LOCAL_MONTH% %LOCAL_YEAR% %LOCAL_HOURS% %LOCAL_MINUTES% %UTC_SECONDS% %UTC_MILLISEC% %UTC_NANOSEC% %BANNER%
goto end
:: ********************************************************************************
:: ***
:: *** print_utc_time - Print the specified time in a pretty format
:: *** interested in ActiveTimeBias which is the offset (in minutes)
;: *** from UTC time.
:: ***
:: *** Parameters: %1 - Day, %2 - Month, %3 - Year, %4 - Hour, %5 - Min,
:: *** %6 - Sec, %7 - millisec, %8 - nanosec,
:: *** %9 - Text to print before the time (use quotes if spaces in parameter)
:: *** Return: None.
:: ***
:: ********************************************************************************
:print_utc_time
setlocal
set DAY=%1
set MONTH=%2
set YEAR=%3
set HOURS=%4
set MINUTES=%5
set SECONDS=%6
set MILLISEC=%7
set NANOSEC=%8
if %HOURS% GTR 12 (
set /a SHOW_HOURS=%HOURS%-12
set AMPM=PM
) else (
set /a SHOW_HOURS=%HOURS%
set AMPM=AM
)
if %MINUTES% LSS 10 (
set SHOW_MIN=0%MINUTES%
) else (
set SHOW_MIN=%MINUTES%
)
if %SECONDS% LSS 10 (
set SHOW_SEC=0%SECONDS%
) else (
set SHOW_SEC=%SECONDS%
)
if %MILLISEC% LSS 10 (
set SHOW_MILLISEC=00%MILLISEC%
) else (
if %MILLISEC% LSS 100 (
set SHOW_MILLISEC=0%MILLISEC%
) else (
set SHOW_MILLISEC=%MILLISEC%
)
)
if %NANOSEC% LSS 10 (
set SHOW_NANOSEC=000%NANOSEC%
) else (
if %NANOSEC% LSS 100 (
set SHOW_NANOSEC=00%NANOSEC%
) else (
if %NANOSEC% LSS 1000 (
set SHOW_NANOSEC=0%NANOSEC%
) else (
set SHOW_NANOSEC=%NANOSEC%
)
)
)
echo %~9 %MONTH%/%DAY%/%YEAR% %SHOW_HOURS%:%SHOW_MIN%:%SHOW_SEC%.%SHOW_MILLISEC%%SHOW_NANOSEC% %AMPM%
endlocal
goto :EOF
:: ********************************************************************************
:: ***
:: *** get_gmt_offset - Extract the local time offset from the registry. We are
:: *** interested in ActiveTimeBias which is the offset (in minutes)
;: *** from UTC time.
:: ***
:: *** Parameters: None
:: *** Return: The adjusted value of ActiveTimeBias.
:: ***
:: ********************************************************************************
:get_gmt_offset
setlocal
set junk=0x123
regedit /e pntte.tmp "HKEY_LOCAL_MACHINE\system\currentcontrolset\control\timezoneinformation"
For /F "skip=3 tokens=1-3* delims=:=" %%a in ('type pntte.tmp') do (
:: *** Look for ActiveTimeBias which gives us the offset in seconds, make a valid hex number
if /i %%a=="ActiveTimeBias" set junk=0x%%c
)
:: Convert hex bias to decimal bias, and fix sign (offset FROM GMT instead of offset TO GMT)
if exist pntte.tmp del pntte.tmp
set /a get_gmt_offset=0-!junk!
endlocal & set get_gmt_offset=%get_gmt_offset%
goto :EOF
:: ********************************************************************************
:: ***
:: *** StrLen - Calculate the number of characters in a variable
:: ***
:: *** Parameters: %1 contains the variable which you want the length of
:: *** Return: Returns the length of the string.
:: ***
:: ********************************************************************************
:StrLen
setlocal & set TmpCnt=%*
if not defined TmpCnt (
set StrLen=0
) else (
:Lenloop
set TmpCnt=%TmpCnt:~1%
set /a StrLen +=1
if defined TmpCnt goto Lenloop
)
endlocal & set StrLen=%StrLen%
goto :EOF
:: ********************************************************************************
:: ***
:: *** fix_day_month - Look at the day and month after rolling time, and fix it.
:: ***
:: *** Parameters: %1 - day, %2 - month, %3 - leap year flag (1 = leap year)
:: *** Return: Adjustment to year if necessary.
:: ***
:: ********************************************************************************
:fix_day_month
set fix_day_month=0
if %2 == 1 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=12
set /a fix_day_month=-1
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 2 (
if %3 == 1 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=1
) else (
if %1 GTR 29 (
set /a LOCAL_DAY=%1 - 29
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
) else (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=1
) else (
if %1 GTR 28 (
set /a LOCAL_DAY=%1 - 28
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
)
goto fix_day_month_exit
)
if %2 == 3 (
if %3 == 1 (
if %1 == 0 (
set /a LOCAL_DAY=29
set /a LOCAL_mONTH=2
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
) else (
if %1 == 0 (
set /a LOCAL_DAY=28
set /a LOCAL_mONTH=2
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
)
goto fix_day_month_exit
)
if %2 == 4 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=3
) else (
if %1 GTR 30 (
set /a LOCAL_DAY=%1 - 30
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 5 (
if %1 == 0 (
set /a LOCAL_DAY=30
set /a LOCAL_mONTH=4
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 6 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=5
) else (
if %1 GTR 30 (
set /a LOCAL_DAY=%1 - 30
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 7 (
if %1 == 0 (
set /a LOCAL_DAY=30
set /a LOCAL_mONTH=6
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 8 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=7
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 9 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=8
) else (
if %1 GTR 30 (
set /a LOCAL_DAY=%1 - 30
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 10 (
if %1 == 0 (
set /a LOCAL_DAY=30
set /a LOCAL_mONTH=9
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 11 (
if %1 == 0 (
set /a LOCAL_DAY=31
set /a LOCAL_mONTH=10
) else (
if %1 GTR 30 (
set /a LOCAL_DAY=%1 - 30
set /a LOCAL_mONTH=%LOCAL_mONTH% + 1
)
)
goto fix_day_month_exit
)
if %2 == 12 (
if %1 == 0 (
set /a LOCAL_DAY=30
set /a LOCAL_mONTH=11
) else (
if %1 GTR 31 (
set /a LOCAL_DAY=%1 - 31
set /a LOCAL_mONTH=1
set /a fix_day_month=1
)
)
)
:fix_day_month_exit
set /a LOCAL_YEAR=%LOCAL_YEAR% + %fix_day_month%
::set fix_day_month=%fix_day_month%
goto :EOF
:: *** Error Jmp, no parameters, or bad parameter
:error
echo.
echo ERROR: no NTTE parameter was defined (try %0 /?)
echo.
goto end
:: *** Print usage
:usage
echo.
echo %0 FILETIME
echo.
echo Where FILETIME is a 64 bit number representing a Windows FILETIME
echo.
echo Example:
echo.
echo %0 126036951652030000
echo.
:: *** That's all folks, make sure your seat backs are up, and tray tables are put away...
:end
title Command Prompt
endlocal