getTimestamp.bat for time and date processing
Posted: 16 Jul 2013 23:08
Important notice - I have ceased development of getTimestamp. The new replacement utility is available at jTimestamp.bat.
The last version 2.6 of getTimestamp.bat can be found later in this thread at the following link
viewtopic.php?f=3&t=4847&start=9999#p47680
Below is a general purpose timestamp calculator and formatter called getTimestamp.bat.
There are a great many options for specifying the base date and time, many options for adding positive or negative offsets to the date and time, many options for formatting the result, and an option to capture the result in a variable. Both input and output can be directly expressed as local time, UTC, or any time zone of your choosing.
Here are just a few examples of how it could be used.
Calling getTimestamp with no arguments simply reports the current local date and time in ISO 8601 format:
The default format string used to get the above output is -f "{yyyy}-{mm}-{dd}T{hh}:{nn}:{ss}.{fff}{tz}"
To get the current local date and time in the same format that WMIC OS GET LOCALDATETIME reports:
To get today's date minus 90 days in YYYYMMDD format:
getTimestap.bat can also be used to conveniently compute elapsed time of nearly any time interval, without worrying about the limits of batch arithmetic. This is extremely convenient for timing events.
Two calls to getTimestamp are used to store the current timestamp at the beginning and end of a process. The timestamps are expressed as milliseconds since midnight, January 1, 1970. Then one more call to getTimestamp is used to compute the interval. The date for the interval is specified as timestamp2 - timestamp1, and the format can provide the elapsed time in whatever units are required.
-- OUTPUT --
Below is the actual getTimestamp.bat script The script is large, but note that 2/3 of it is documentation.
Parsing options in batch can often be a chore. For this utility I used a handy technique I developed and posted on StackOverflow: Windows Bat file optional argument parsing. There I describe the theory behind how my option parser works, and provide extensive documentation. I opted to use Unix style options, but it could easily be adapted for Windows style.
EDIT: I added the -Z option to specify the output time zone, fixed a few obscure bugs, and improved the documentation
EDIT 2014-03/02: Added credit info to the documentation.
END OF CODE
Some Preamble from when this post was part of another thread:
There are already many posted methods for working with date and time in batch. I thought I would add another option to the fray
JScript has robust date and time handling, and I already had a hybrid JScript/batch utility called jEval.bat that can dynamically execute any JScript expression and print to stdout the result. The result can be captured by FOR /F for later use. This makes it very easy to incorporate a bit of JScript in any batch script that I want.
Assuming jEval.bat is either in the current directory, or else somewhere in the PATH, then the following simple batch subtracts 90 days from the current date and builds a date string that would work well in a file name.
EDIT 2014-08-03: Bug fix dealing with JScript months having 0 index - thanks Paul
-- OUTPUT --
Here is the jEval.bat code:
All well and good. But then I decided to go crazy with this general technique and add a gazillion options to create a general purpose timestamp calculator and formatter called getTimestamp.bat. See above.
Dave Benham
The last version 2.6 of getTimestamp.bat can be found later in this thread at the following link
viewtopic.php?f=3&t=4847&start=9999#p47680
Below is a general purpose timestamp calculator and formatter called getTimestamp.bat.
There are a great many options for specifying the base date and time, many options for adding positive or negative offsets to the date and time, many options for formatting the result, and an option to capture the result in a variable. Both input and output can be directly expressed as local time, UTC, or any time zone of your choosing.
Here are just a few examples of how it could be used.
Calling getTimestamp with no arguments simply reports the current local date and time in ISO 8601 format:
Code: Select all
>getTimestamp
2013-07-16T23:38:38.709-04:00
To get the current local date and time in the same format that WMIC OS GET LOCALDATETIME reports:
Code: Select all
>getTimestamp -f {yyyy}{mm}{dd}{hh}{nn}{ss}.{fff}000{zzzz}
20130716233838.709000-240
To get today's date minus 90 days in YYYYMMDD format:
Code: Select all
@echo off
setlocal
call getTimeStamp -od -90 -f {yyyy}{mm}{dd} -r dt
echo current date - 90 days (YYYYMMDD) = %dt%
getTimestap.bat can also be used to conveniently compute elapsed time of nearly any time interval, without worrying about the limits of batch arithmetic. This is extremely convenient for timing events.
Two calls to getTimestamp are used to store the current timestamp at the beginning and end of a process. The timestamps are expressed as milliseconds since midnight, January 1, 1970. Then one more call to getTimestamp is used to compute the interval. The date for the interval is specified as timestamp2 - timestamp1, and the format can provide the elapsed time in whatever units are required.
Code: Select all
@echo off
setlocal
call getTimestamp -f {ums} -r t1
:: Some long running process here
call getTimestamp -f {ums} -r t2
:: This computes the elapsed time as decimal hours.
:: It supports both positive and negative intervals.
call getTimestamp -d %t2%-%t1% -f "{uhd} hours"
:: This computes the elapsed time as days, hours, mins, secs, ms
:: It only supports positive intervals
call getTimestamp -d %t2%-%t1% -f "{ud} days {hh}:{nn}:{ss}.{fff}" -u
-- OUTPUT --
Code: Select all
62.35474888888889 hours
2 days 14:21:17.096
Below is the actual getTimestamp.bat script The script is large, but note that 2/3 of it is documentation.
Parsing options in batch can often be a chore. For this utility I used a handy technique I developed and posted on StackOverflow: Windows Bat file optional argument parsing. There I describe the theory behind how my option parser works, and provide extensive documentation. I opted to use Unix style options, but it could easily be adapted for Windows style.
EDIT: I added the -Z option to specify the output time zone, fixed a few obscure bugs, and improved the documentation
EDIT 2014-03/02: Added credit info to the documentation.
Code: Select all
@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScript comment
::************ Documentation ***********
:::getTimestamp [-option [value]]...
:::
::: Displays a formatted timestamp. Defaults to the current local date
::: and time using an ISO 8601 format with milliseconds, time zone
::: and punctuation.
:::
::: Returned ERRORLEVEL is 0 upon success, 1 if failure.
:::
::: The following options are all case insensitive:
:::
::: -?
:::
::: Prints this documentation for getTimestamp
:::
::: -U
:::
::: Returns a UTC timestamp instead of local timestamp.
::: Default is a local timestamp.
:::
::: -Z TimeZoneMinuteOffset
:::
::: Returns the timestamp using the specified time zone offset.
::: TimeZoneMinuteOffset is a JScript numeric expression that
::: represents the number of minutes offset from UTC.
::: Decimal values are truncated.
:::
::: -D DateSpec
:::
::: Specify the base date and time.
::: Default value is current local date and time.
::: The DateSpec supports many formats:
:::
::: "" (no value)
:::
::: Current date and time - the default
:::
::: milliseconds
:::
::: A JScript numeric expression that represents the number of
::: milliseconds since 1970-01-01 00:00:00 UTC.
::: Decimal values are truncated.
::: Negative values represent dates prior to 1970-01-01.
:::
::: "'Date [Time] [TimeZone]'"
:::
::: A string representation of the date and time. The date information
::: is required, the time and time zone are optional. Missing time info
::: is assumed to be 0 (midnight). Missing time zone info is assumed to
::: be local time zone.
:::
::: The Date, Time, and TimeZone information can be represented as any
::: string that is accepted by the JScript Date.Parse() method.
::: There are many formatting options. Documentation is available at:
::: http://msdn.microsoft.com/en-us/library/k4w173wk(v=vs.84).aspx
:::
::: Examples of equivalent representations of Midnight on January 4,
::: 2013 assuming local time zone is U.S Eastern Standard Time (EST):
:::
::: '1-4-2013' Defaults to local time zone
::: "'January 4, 2013 EST'" Explicit Eastern Std Time (US)
::: "'2013/1/4 -05'" Explicit Eastern Std Time (US)
::: "'Jan 3 2013 23: CST'" Central Standard Time (US)
::: "'2013 3 Jan 9:00 pm -0800'" Pacific Standard Time (US)
::: "'01/04/2013 05:00:00 UTC'" Universal Coordinated Time
::: "'1/4/2013 05:30 +0530'" India Standard Time
:::
::: "year, month[, day[, hour[, minute[, second[, millisecond]]]]]"
:::
::: A comma delimited list of numeric JScript expressions representing
::: various components of date and time. Year and month are required,
::: the rest are optional. Missing values are treated as 0.
::: Decimal values are truncated. A 0 month represents January.
::: A 1 day represents the first day of the month. A 0 day represents
::: the last day of the prior month. The date/time value is always
::: in local time. There is no mechanism to specify a time zone.
:::
::: -OY YearOffset
:::
::: Specify the number of years to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -OM MonthOffset
:::
::: Specify the number of months to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -OD DayOffset
:::
::: Specify the number of days to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -OH HourOffset
:::
::: Specify the number of hours to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -ON MinuteOffset
:::
::: Specify the number of minutes to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -OS SecondOffset
:::
::: Specify the number of seconds to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -OF MillisecondOffset
:::
::: Specify the number of milliseconds to offset the base date/time.
::: The JScript numeric expression is truncated to an integral number.
::: Default is 0
:::
::: -F FormatString
:::
::: Specify the timestamp format.
::: Default is "{yyyy}-{mm}-{dd}T{hh}:{nn}:{ss}.{fff}{tz}"
::: Strings within braces are dynamic components.
::: All other strings are literals.
::: Available components (case insensitive) are:
:::
::: {YYYY} 4 digit year, zero padded
:::
::: {YY} 2 digit year, zero padded
:::
::: {Y} year without zero padding
:::
::: {MONTH} month name
:::
::: {MTH} month abbreviation
:::
::: {MM} 2 digit month, zero padded
:::
::: {M} month without zero padding
:::
::: {WEEKDAY} day of week name
:::
::: {WKD} day of week abbreviation
:::
::: {W} day of week number, 0=Sunday
:::
::: {DD} 2 digit day, zero padded
:::
::: {D} day without zero padding
:::
::: {HH} 2 digit hours, 24 hour format, zero padded
:::
::: {H} hours, 24 hour format without zero padding
:::
::: {HH12} 2 digit hours, 12 hour format, zero padded
:::
::: {H12} hours, 12 hour format without zero padding
:::
::: {NN} 2 digit minutes, zero padded
:::
::: {N} minutes without padding
:::
::: {SS} 2 digit seconds, zero padded
:::
::: {S} seconds without padding
:::
::: {FFF} 3 digit milliseconds, zero padded
:::
::: {F} milliseconds without padding
:::
::: {AM} AM or PM in upper case
:::
::: {PM} am or pm in lower case
:::
::: {ZZZZ} timezone expressed as minutes offset from UTC,
::: zero padded to 3 digits with sign
:::
::: {Z} timzone expressed as minutes offset from UTC without padding
:::
::: {ZS} ISO 8601 timezone sign
:::
::: {ZH} ISO 8601 timezone hours (no sign)
:::
::: {ZM} ISO 8601 timezone minutes (no sign)
:::
::: {TZ} ISO 8601 timezone in +/-hh:mm format
:::
::: {U} Unix Epoch time: same as {US}
::: Seconds since 1970-01-01 00:00:00 UTC.
::: Negative numbers represent dates prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UMS} Milliseconds since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {US} Seconds since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UM} Minutes since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UH} Hours since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UD} Days since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {USD} Decimal seconds since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UMD} Decimal minutes since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UHD} Decimal hours since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: {UDD} Decimal days since 1970-01-01 00:00:00.000 UTC.
::: Negative numbers represent days prior to 1970-01-01.
::: This value is unaffected by the -U option.
::: This value should not be used with the -Z option
:::
::: -R ReturnVariable
:::
::: Save the timestamp in ReturnVariable instead of displaying it.
::: ReturnVariable is undefined if an error occurs.
:::
::: -WKD "Abbreviated day of week list"
:::
::: Override the default output day abbreviations with a space delimited
::: quoted list, starting with Sun.
::: Default is mixed case 3 character English abbreviations.
:::
::: -WEEKDAY "Day of week list"
:::
::: Override the default output day names with a space delimited,
::: quoted list, starting with Sunday.
::: Default is mixed case English names.
:::
::: -MTH "Abbreviated month list"
:::
::: Override the default output month abbreviations with a space delimited
::: quoted list, starting with Jan.
::: Default is mixed case English 3 character abbreviations.
:::
::: -MONTH "Month list"
:::
::: Override the default output month names with a space delimited
::: quoted list, starting with January.
::: Default is mixed case English names.
:::
::: getTimestamp.bat was written by Dave Benham. The code was originally posted
::: at http://www.dostips.com/forum/viewtopic.php?f=3&t=4847
:::
::************ Batch portion ***********
@echo off
setlocal enableDelayedExpansion
:: Define options
set ^"options=^
-?:^
-u:^
-z:""^
-f:"{yyyy}-{mm}-{dd}T{hh}:{nn}:{ss}.{fff}{tz}"^
-d:""^
-oy:""^
-om:""^
-od:""^
-oh:""^
-on:""^
-os:""^
-of:""^
-r:""^
-wkd:"Sun Mon Tue Wed Thu Fri Sat"^
-weekday:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday"^
-mth:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"^
-month:"January February March April May June July August September October November December"^"
:: Set default option values
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:: Get options
:loop
if not "%~1"=="" (
set "test=!options:*%~1:=! "
if "!test!"=="!options! " (
>&2 echo Error: Invalid option %~1
exit /b 1
) else if "!test:~0,1!"==" " (
set "%~1=UTC"
if /i "%~1" equ "-U" set "-z="
) else (
set "%~1=%~2"
shift /1
)
shift /1
goto :loop
)
:: Display help
if defined -? (
for /f "delims=: tokens=1*" %%A in ('findstr /bln ":::" "%~f0"') do echo(%%B
exit /b 0
)
:: Initialize ReturnVariable to undefined
if defined -R set "%-R%="
:: Define lookup arrays
for %%N in (-wkd -weekday -mth -month) do (
set /a cnt=0
for %%V in (!%%N!) do (
set "%%N!cnt!=%%V"
set /a cnt+=1
)
)
:: Complete the time zone options
set "-oz=%-z%"
if defined -z set "-u=UTC"
:: Complete the offset options
set "offsets= -oy:FullYear -om:Month -od:Date -oh:Hours -on:Minutes -os:Seconds -of:Milliseconds -oz:Minutes "
for /f "tokens=1,2 delims==" %%A in ('set -o 2^>nul') do (
set "offset=!offsets:* %%A:=!"
if !offset! neq !offsets! for /f %%C in ("!offset!") do set "%%A=d.set%%C(d.get%%C()+(%%B));"
)
:: Define the dynamic JScript script
set "jstr=d=new Date(%-d%);%-oy%%-om%%-od%%-oh%%-on%%-os%%-of%%-oz%u=d.getTime();"
set "jstr=%jstr%d.get%-u%FullYear()+' '+d.get%-u%Month()+' '+d.get%-u%Date()"
set "jstr=%jstr%+' '+d.get%-u%Hours()+' '+d.get%-u%Minutes()"
set "jstr=%jstr%+' '+d.get%-u%Seconds()+' '+d.get%-u%Milliseconds()"
set "jstr=%jstr%+' '+d.get%-u%Day()+' '+u+' '+(u/1000)+' '+(u/1000/60)"
set "jstr=%jstr%+' '+(u/1000/60/60)+' '+(u/1000/60/60/24)"
if defined -z (
set "jstr=%jstr%+' '+(-~~(%-z%));"
) else if defined -u (
set "jstr=%jstr%+' 0';"
) else (
set "jstr=%jstr%+' '+d.getTimezoneOffset();"
)
:: Execute the JScript script and set raw variables
set "ums="
for /f "tokens=1-14" %%A in ('cscript //E:JScript //nologo "%~f0" "%jstr%"') do (
set /a "y=%%A, m=%%B, d=%%C, h=%%D, n=%%E, s=%%F, f=%%G, w=%%H, z=-%%N"
set "ums=%%I"
set "usd=%%J"
set "umd=%%K"
set "uhd=%%L"
set "udd=%%M"
)
:: Check for errors
if not defined ums exit /b 1
if %ums% equ NaN (
>&2 echo ERROR: Invalid date time
exit /b 1
)
:: Lookup day and month alpha values
for %%N in (!w!) do (
set "wkd=!-wkd%%N!"
set "weekday=!-weekday%%N!"
)
for %%N in (!m!) do (
set "mth=!-mth%%N!"
set "month=!-month%%N!"
)
set /a m+=1
:: Define 12 hour values
set "am=AM"
set "pm=am"
set "h12=%h%"
if %h% geq 12 (
set "am=PM"
set "pm=pm"
set /a h12=h-12
)
if %h12% equ 0 set "h12=12"
:: Define values needed for ISO 8601 timezone string
set "zs=+"
if %z% lss 0 (
set "zs=-"
set /a z=-z
)
set /a "zh=z/60, zm=z%%60"
:: Define integral Unix Epoch values
for %%C in (s m h d) do for /f "delims=." %%N in ("!u%%Cd!") do set "u%%C=%%N"
set "u=%us%"
:: Define zero prefixed values
set "yyyy=000%y%"
set "yyyy=%yyyy:~-4%"
for %%A in (yy:y mm:m dd:d hh:h nn:n ss:s hh12:h12 zh:zh zm:zm ) do (
for /f "delims=: tokens=1,2" %%B in ("%%A") do (
if !%%C! lss 10 (set %%B=0!%%C!) else (set %%B=!%%C!)
)
)
set "fff=00%f%"
set "fff=%fff:~-3%"
set "zzzz=00%z%"
set "zzzz=%zs%%zzzz:~-3%"
:: Restore z sign
set /a "z=%zs%z"
:: Define ISO 8601 timezone string
set "tz=%zs%%zh%:%zm%"
:: Substitute values in format string
for %%N in (
yyyy yy y mm m dd d hh12 h12 hh h nn n ss s fff f zzzz z
w wkd weekday mth month am pm zs zh zm tz
u ums us um uh ud usd umd uhd udd
) do for %%V in (!%%N!) do set "-f=!-f:{%%N}=%%V!"
:: Return result
endlocal&if "%-R%" neq "" (set "%-R%=%-F%") else (echo(%-F%)
exit /b 0
************ JScript portion ***********/
WScript.StdOut.Write(eval(WScript.Arguments.Unnamed(0)));
END OF CODE
Some Preamble from when this post was part of another thread:
There are already many posted methods for working with date and time in batch. I thought I would add another option to the fray
JScript has robust date and time handling, and I already had a hybrid JScript/batch utility called jEval.bat that can dynamically execute any JScript expression and print to stdout the result. The result can be captured by FOR /F for later use. This makes it very easy to incorporate a bit of JScript in any batch script that I want.
Assuming jEval.bat is either in the current directory, or else somewhere in the PATH, then the following simple batch subtracts 90 days from the current date and builds a date string that would work well in a file name.
EDIT 2014-08-03: Bug fix dealing with JScript months having 0 index - thanks Paul
Code: Select all
@echo off
setlocal
for /f "tokens=1-3" %%A in (
'jeval "d=new Date();d.setDate(d.getDate()-90);d.getFullYear()+' '+(d.getMonth()+1)+' '+d.getDate()"'
) do set /a "y=%%A, m=%%B, d=%%C"
if %m% lss 10 set "m=0%m%"
if %d% lss 10 set "d=0%d%"
set "dt=%y%%m%%d%
echo current date - 90 days (YYYYMMDD) = %dt%
-- OUTPUT --
Code: Select all
current date - 90 days (YYYYMMDD) = 20130317
Here is the jEval.bat code:
Code: Select all
@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment
::************ Documentation ***********
:::
:::jEval JScriptExpression [/N]
:::jEval /?
:::
::: Evaluates a JScript expression and writes the result to stdout.
:::
::: A newline (CR/LF) is not appended to the result unless the /N
::: option is used.
:::
::: The JScript expression should be enclosed in double quotes.
:::
::: JScript string literals within the expression should be enclosed
::: in single quotes.
:::
::: Example:
:::
::: call jEval "'5/4 = ' + 5/4"
:::
::: Output:
:::
::: 5/4 = 1.25
:::
::************ Batch portion ***********
@echo off
if "%~1" equ "" (
call :err "Insufficient arguments"
exit /b
)
if "%~2" neq "" if /i "%~2" neq "/N" (
call :err "Invalid option"
exit /b
)
if "%~1" equ "/?" (
setlocal enableDelayedExpansion
for /f "delims=" %%A in ('findstr "^:::" "%~f0"') do (
set "ln=%%A"
echo(!ln:~3!
)
exit /b
)
cscript //E:JScript //nologo "%~f0" %*
exit /b
:err
>&2 echo ERROR: %~1. Use jeval /? to get help.
exit /b 1
************ JScript portion ***********/
if (WScript.Arguments.Named.Exists("n")) {
WScript.StdOut.WriteLine(eval(WScript.Arguments.Unnamed(0)));
} else {
WScript.StdOut.Write(eval(WScript.Arguments.Unnamed(0)));
}
All well and good. But then I decided to go crazy with this general technique and add a gazillion options to create a general purpose timestamp calculator and formatter called getTimestamp.bat. See above.
Dave Benham