Page 1 of 4

getTimestamp.bat for time and date processing

Posted: 16 Jul 2013 23:08
by dbenham
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:

Code: Select all

>getTimestamp
2013-07-16T23:38:38.709-04:00
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:

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 :twisted:

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

Re: System Date -90 Days and -180 Days

Posted: 17 Jul 2013 00:22
by foxidrive
Another nice bit of kit, Dave. :thumbsup:

Re: System Date -90 Days and -180 Days

Posted: 17 Jul 2013 12:45
by dbenham
I updated my getTimestamp.bat script above to include a -Z option that specifies the time zone of the output. I also fixed a few obscure bugs, and improved the documentation.


Dave Benham

Re: System Date -90 Days and -180 Days

Posted: 18 Jul 2013 08:22
by brinda
:D wow. thanks for providing this.

Re: getTimestamp.bat for time and date processing

Posted: 11 Aug 2013 04:57
by foxidrive
Dave, I split this off to it's own thread so people can find it. Cheers.

Re: getTimestamp.bat for time and date processing

Posted: 02 Dec 2013 11:00
by gonzalezea
Great and impressive script !!! :D :D thanks !!

Re: getTimestamp.bat for time and date processing

Posted: 02 Dec 2013 21:29
by julesverne
Awesome!

Re: getTimestamp.bat for time and date processing

Posted: 20 Jun 2014 12:21
by FunWithLibelium
Dave, thanks very much. This worked perfectly for my needs. However please see the following text for a small bug report.

Background: I'm a recovering software designer and systems programmer from the '80s. So, I have some (rusty) chops, but almost no experience programming in Windows or even Dos.

So, you can imagine how happy I was to stumble on this when I found myself needing to slap yesterday's date into the args to a call to java from a little batch file. Because it was necessary for getTimestamp, and because it looked sufficient for my needs by itself, and because there's an example that does almost exactly what I want, I started with jEval.bat and the subtract 90 days example immediately above it. Ultimately, I made that work, but I believe I found a bug in the example.

If you just copy/paste jeval.bat and the "current date minus 90 days" example to a directory and run the example, it gives a date that is 120 days ago, not 90. I believe this is caused by the fact that jscript months are 0 ... 11 rather than 1 ... 12. My proposed fix is:

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%


And here's the little batch file I used to demonstrate the behavior:

Code: Select all

@echo off
setlocal

echo batch thinks current date is %date%

for /f "tokens=1-3" %%A in (
  'jeval "d=new Date();d.setDate(d.getDate());d.getFullYear()+' '+d.getMonth()+' '+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 jEval current date (YYYYMMDD) = %dt%

for /f "tokens=1-3" %%A in (
  'jeval "d=new Date();d.setDate(d.getDate()-90);d.getFullYear()+' '+d.getMonth()+' '+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 jEval current date - 90 days (YYYYMMDD) = %dt%

echo(
echo Accounting for jscript 0-indexed months,

for /f "tokens=1-3" %%A in (
  'jeval "d=new Date();d.setDate(d.getDate());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 jEval current date (YYYYMMDD) = %dt%

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 jEval current date - 90 days (YYYYMMDD) = %dt%


Because I didn't need or use it, I don't know if the above affects getTimestamp.bat or not.

Thanks again.

Paul Robinson

Re: getTimestamp.bat for time and date processing

Posted: 03 Aug 2014 05:48
by dbenham
Yep - that was a bug in my JEval example. I had detected and fixed the issue with getTimeStamp.bat during initial development, but I forgot to adjust my jEval example. I've edited the initial post.

Thanks Paul

Re: getTimestamp.bat for time and date processing

Posted: 02 Dec 2014 22:24
by dbenham
getTimestamp.bat version 2.2 is here :!:

This is a major rewrite, but not much new functionality. I am thinking of creating a JScript time formatter function for use in my JREN.BAT utility, and in preparation I modified getTimestamp.bat to do nearly everything in JScript instead of batch.

The old functionality and syntax is unchanged, despite the rewrite.

New features:
- runs about 50% faster than version 1.1
- New -V option to display version
- Options may now be specified as /Option instead of -Option
- Significantly improved error detection and handling
- New {{} format component to allow unambiguous { literal in output

Code: Select all

@if (@X)==(@Y) @end  /* harmless hybrid line that begins a JScript comment
@goto :batch
::
::getTimestamp.bat version 2.2
::
::  Release History:
::    2.2 2014-12-03 - Doc fix: -R ReturnVariable is unchanged if err occurs
::    2.1 2014-12-03 - Added GOTO at top to increase performance (32% faster)
::    2.0 2014-12-02 - Major rewrite: most code now in JScript (25% faster)
::                     Better error handling
::                     May now use /Option or -Option
::                     Added -V option
::                     Added {{} format value to enable unambiguous { literal
::    1.1 ????-??-?? - Added -Z option and obscure bug fixes
::    1.0 2013-07-17 - Initial release
::
::============ 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.
:::
:::  Options may be prefaced with - or /
:::
:::  The following options are all case insensitive:
:::
:::    -?
:::
:::       Prints this documentation for getTimestamp
:::
:::    -V
:::
:::       Prints the version of 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
:::
:::         {{}     A { character
:::
:::    -R ReturnVariable
:::
:::       Save the timestamp in ReturnVariable instead of displaying it.
:::       ReturnVariable is unchanged 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.
:::

============= :Batch portion ===========
@echo off
setlocal enableDelayedExpansion

:: Define options
set ^"options=^
 -?:^
 -v:^
 -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 "arg=%~1"
  if "!arg:~0,1!" equ "/" set "arg=-!arg:~1!"
  for /f delims^=^ eol^= %%A in ("!arg!") do set "test=!options:*%%A:=! "
  if "!test!"=="!options! " (
      >&2 echo Error: Invalid option %~1. Use %~nx0 -? to get help.
      exit /b 1
  ) else if "!test:~0,1!"==" " (
      set "!arg!=1"
      if /i "!arg!" equ "-U" set "-z="
  ) else (
      set "!arg!=%~2"
      shift /1
  )
  shift /1
  goto :loop
)
if defined -z set "-u=1"
if defined -r set "%-r%="

:: Display help
if defined -? (
  for /f "delims=: tokens=*" %%A in ('findstr "^:::" "%~f0"') do echo(%%A
  exit /b 0
)

:: Display version
if defined -v (
  for /f "tokens=* delims=:" %%A in ('findstr "^::getTimestamp\.bat" "%~f0"') do @echo(%%A
  exit /b 0
)

:: Execute the JScript script and return the result
for /f "delims=" %%A in ('cscript //E:JScript //nologo "%~f0"') do (
  endlocal
  if "%-R%" neq "" (set "%-R%=%%A") else (echo(%%A)
  exit /b 0
)
exit /b 1;


************ JScript portion ***********/
var env  = WScript.CreateObject("WScript.Shell").Environment("Process"),
    utc  = env('-U'),
    wkd     = env('-WKD').split(' '),
    weekday = env('-WEEKDAY').split(' '),
    mth     = env('-MTH').split(' '),
    month   = env('-MONTH').split(' '),
    stderr  = WScript.StdErr,
    y,m,d,w,h,h12,n,s,f,u,z,zs,za;

if (wkd.length!=7)     badOp('-WKD');
if (weekday.length!=7) badOp('-WEEKDAY');
if (mth.length!=12)    badOp('-MTH');
if (month.length!=12)  badOp('-MONTH');

try {
  var dt = eval('new Date('+env('-D')+')');
} catch(e) {
} finally {
  if (isNaN(dt)) badOp('-D');
}

if (env('-OY')) dt.setFullYear(     dt.getFullYear()    +getNum('-OY') );
if (env('-OM')) dt.setMonth(        dt.getMonth()       +getNum('-OM') );
if (env('-OD')) dt.setDate(         dt.getDate()        +getNum('-OD') );
if (env('-OH')) dt.setHours(        dt.getHours()       +getNum('-OH') );
if (env('-ON')) dt.setMinutes(      dt.getMinutes()     +getNum('-ON') );
if (env('-OS')) dt.setSeconds(      dt.getSeconds()     +getNum('-OS') );
if (env('-OF')) dt.setMilliseconds( dt.getMilliseconds()+getNum('-OF') );
if (env('-Z'))  dt.setMinutes(      dt.getMinutes()  +(z=getNum('-Z')) );

y = utc ? dt.getUTCFullYear(): dt.getFullYear();
m = utc ? dt.getUTCMonth()   : dt.getMonth();
d = utc ? dt.getUTCDate()    : dt.getDate();
w = utc ? dt.getUTCDay()     : dt.getDay();
h = utc ? dt.getUTCHours()   : dt.getHours();
n = utc ? dt.getUTCMinutes() : dt.getMinutes();
s = utc ? dt.getUTCSeconds() : dt.getSeconds();
f = utc ? dt.getUTCMilliseconds() : dt.getMilliseconds();
u = dt.getTime();

h12 = h%12;
if (!h12) h12=12;

if (z==undefined) if (utc) z=0; else z=-dt.getTimezoneOffset();
zs = z<0 ? '-' : '+';
za = Math.abs(z);

WScript.echo( env('-F').replace( /\{(.*?)\}/gi, repl ) );
WScript.Quit(0);

function lpad( val, pad ) {
  var rtn=val.toString();
  return (rtn.length<pad.length) ? (pad+rtn).slice(-pad.length) : val;
}

function getNum( v ) {
  var rtn;
  try {
    rtn = Number(eval(env(v)));
  } catch(e) {
  } finally {
    if (isNaN(rtn-rtn)) badOp(v);
    return rtn;
  }
}

function badOp(option) {
  stderr.WriteLine('Error: Invalid '+option+' value');
  WScript.Quit(1);
}

function trunc( n ) { return Math[n>0?"floor":"ceil"](n); }

function repl($0,$1) {
  switch ($1.toUpperCase()) {
    case 'YYYY' : return lpad(y,'0000');
    case 'YY'   : return ('00'+y.toString()).slice(-2);
    case 'Y'    : return y.toString();
    case 'MM'   : return lpad(m+1,'00');
    case 'M'    : return (m+1).toString();
    case 'DD'   : return lpad(d,'00');
    case 'D'    : return d.toString();
    case 'W'    : return w.toString();
    case 'HH'   : return lpad(h,'00');
    case 'H'    : return h.toString();
    case 'HH12' : return lpad(h12,'00');
    case 'H12'  : return h12.toString();
    case 'NN'   : return lpad(n,'00');
    case 'N'    : return n.toString();
    case 'SS'   : return lpad(s,'00');
    case 'S'    : return s.toString();
    case 'FFF'  : return lpad(f,'000');
    case 'F'    : return f.toString();
    case 'AM'   : return h>=12 ? 'PM' : 'AM';
    case 'PM'   : return h>=12 ? 'pm' : 'am';
    case 'UMS'  : return u.toString();
    case 'USD'  : return (u/1000).toString();
    case 'UMD'  : return (u/1000/60).toString();
    case 'UHD'  : return (u/1000/60/60).toString();
    case 'UDD'  : return (u/1000/60/60/24).toString();
    case 'U'    : return trunc(u/1000).toString();
    case 'US'   : return trunc(u/1000).toString();
    case 'UM'   : return trunc(u/1000/60).toString();
    case 'UH'   : return trunc(u/1000/60/60).toString();
    case 'UD'   : return trunc(u/1000/60/60/24).toString();
    case 'ZZZZ' : return zs+lpad(za,'000');
    case 'Z'    : return z.toString();
    case 'ZS'   : return zs;
    case 'ZH'   : return lpad(trunc(za/60),'00');
    case 'ZM'   : return lpad(za%60,'00');
    case 'TZ'   : return zs+lpad(trunc(za/60),'00')+':'+lpad(za%60,'00');
    case 'WEEKDAY': return weekday[w];
    case 'WKD'    : return wkd[w];
    case 'MONTH'  : return month[m];
    case 'MTH'    : return mth[m];
    case '{'      : return $1;
    default       : return $0;
  }
}


Dave Benham

Re: getTimestamp.bat for time and date processing

Posted: 03 Dec 2014 20:47
by dbenham
Updated the prior post to version 2.1.

The only change was the addition of a GOTO at the very top so that batch no longer wastes its time parsing the documentation comments. I was mildly surprised that this small change results in 32% speed improvement over version 2.0. So now version 2.1 is 50% faster than 1.1.


Dave Benham

Re: getTimestamp.bat for time and date processing

Posted: 03 Dec 2014 21:01
by Squashman
Well it is 300 lines of excellent documentation.

Re: getTimestamp.bat for time and date processing

Posted: 03 Dec 2014 22:32
by dbenham
Squashman wrote:Well it is 300 lines of excellent documentation.
:D

But funny you should say that. I just discovered a long standing mistake in the documentation, so I updated the prior code to version 2.2 with the corrected doc.

Going all the way back to version 1, I thought I was undefining the -R return variable upon error. But I was doing that before any ENDLOCAL, so any pre-existing value remained after error. :roll:

Upon reflection, my old intent was ambiguous, because the error might occur before I have parsed the -R option.

I've decided to change the documentation instead of changing the behavior. Version 2.2 is just a minimal change to the Documentation.


Dave Benham

Re: getTimestamp.bat for time and date processing

Posted: 25 Jan 2015 12:16
by Squashman
I am definitely not understanding how to use this.

I have been trying to feed it different date/Time formats for the /D option but everyone of them comes back with an invalid -D option error.

Code: Select all

C:\BatchFiles\JGetTimeStamp>for /F "delims=" %G in ('dir /b "file(2).txt"') do gettimestamp /D "%~tG" /F "{UH}"

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "01/25/2015 11:55 AM" /F "{UH}"
Error: Invalid -D value

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "01/25/15 11:55 AM" /F "{UH}"
Error: Invalid -D value

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "1/25/15 11:55 AM" /F "{UH}"
Error: Invalid -D value

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "1/25/15" /F "{UH}"
0

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "1/25/2015" /F "{UH}"
0

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "1/25/2015 23:00" /F "{UH}"
Error: Invalid -D value

Re: getTimestamp.bat for time and date processing

Posted: 25 Jan 2015 12:48
by Squashman
This confuses me even more.

Code: Select all

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "2015,01,25,11,25"
2015-02-25T11:25:00.000-06:00

C:\BatchFiles\JGetTimeStamp>gettimestamp /D "2015,1,25,11,25"
2015-02-25T11:25:00.000-06:00