jTimestamp.bat date/time utility v2.1 - Replacement for getTimestamp.bat

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

jTimestamp.bat date/time utility v2.1 - Replacement for getTimestamp.bat

#1 Post by dbenham » 01 Nov 2016 21:30

Here is the current version of jTimestamp.bat
jTimestamp2.1.zip
(10.21 KiB) Downloaded 1880 times
Development history and prior versions are availabe in subsequent posts.

Release History
Version 2.1 - Bug fix for -D ISO 8601 week date computation when week is 08 or 09
Version 2.0 - ISO8601 support added to -D, and new -CZ option
Version 1.1 - Initial Release[/url]


Here is the full code, in case you want to look at it without downloading the zip file.

Code: Select all

@if (@X)==(@Y) @end  /* harmless hybrid line that begins a JScript comment
::
::jTimestamp.bat version 2.1 by Dave Benham
::
::  Release History:
::    2018-01-16 v2.1 - Bug fix: computation of -D ISO 8601 week date when
::                      week is 08 or 09.
::    2017-01-23 v2.0 - Added support for ISO 8601 format to the -D option.
::                    - Added -CZ option to specify timezone for computations.
::                    - Bug fix: computation of ISO 8601 week numbering year,
::                      week number, and ordinal day when -Z or -U is used.
::                    - Fixed some documentation errors for -D option with
::                      "'Date [Time] [TimeZone]'" argument.
::    2016-11-02 v1.1 - Remove getTimestamp from documentation and code
::    2016-11-01 v1.0 - Initial release derived from v2.6 of getTimestamp.bat
::                      Syntax and behavior of computations changed, plus added
::                      more computation options.
::
::============ Documentation ===========
:::
:::jTimestamp.bat  [-option [value]]...
:::
:::  Computes and displays a formatted timestamp. Defaults to the current local
:::  date and time using an ISO 8601 format with milliseconds, time zone
:::  and punctuation, using period as a decimal mark.
:::
:::  Returned ERRORLEVEL is 0 upon success, 1 if failure.
:::
:::  Options may be prefaced with - or /
:::
:::  All options have default values. The default value can be overridden by
:::  defining an evironment variable of the form "jTimestamp-OPTION=VALUE".
:::  For example, upper case English weekday abbreviations can be specified as
:::  the default by defining "jTimestamp-WKD=SUN MON TUE WED THU FRI SAT".
:::
:::  Command line option values take precedence, followed by environment variable
:::  defaults, followed by built in defaults.
:::
:::  The following options are all case insensitive:
:::
:::    -?  : Prints this documentation for jTimestamp
:::
:::    -?? : Prints this documentation with pagination via MORE
:::
:::    -V  : Prints the version of jTimeStamp
:::
:::    -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/4/1 00:00 -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 10: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.
:::
:::           Examples: (all in local time)
:::             "2012,0,1,12,45,3,121"  12:45:03.121 on Jan 1, 2012
:::             "2012,0,1,12"           Noon on Jan 1, 2012
:::             "2012,1,1"              Midnight on Feb 1, 2012
:::             "2012,2,0"              Midnight on Feb 29, 2012 (last day of Feb)
:::             "2012,2"                Midnight on Feb 29, 2012 (last day of Feb)
:::
:::           The ability to use JScript expressions makes it convenient to do
:::           date and time offset computations in a very compact form.
:::
:::           For example, starting with:
:::             "2015,8,7,14,5"        Sep 7, 2015 at 14:05:00
:::
:::           It is simple to subtract 30 days and 30 minutes from the above:
:::             "2015,8,7-30,14,5-30"  Aug 8, 2015 at 13:35:00
:::
:::         "ISO {date}[T{time}[{zone}]]"
:::
:::           Any valid ISO 8601 formatted date with optional time
:::           and time zone.
:::
:::             ISO  = Literal string indicating ISO 8601 format
:::             T    = Literal string indicating presence of time info
:::
:::           {date} = {calendarDate}|{weekDate}|{ordinalDate}
:::
:::           {calendarDate} = yyyy[[-]mm[[-]dd]]
:::             yyyy = Year         (0000-9999)
:::             mm   = Month        (01-12)
:::             dd   = Day of Month (01-31)
:::
:::           {weekDate} = yyyy[-]Www[-]d
:::             yyyy = Week-numbering Year (0000-9999)
:::             W    = Literal string indicating an Ordinal week follows
:::             ww   = Week number (01-53)
:::             d    = Day of Week (1-7)
:::
:::           {ordinalDate} = yyyy[-]ddd
:::             yyyy = Year        (0000-9999)
:::             ddd  = Day of Year (001-366)
:::
:::           {time} = hh[[:]mm[[:]ss[{.|,}f...]]
:::             hh   = Hour   (00-23)
:::             mm   = Minute (00-59)
:::             ss   = Second (00-59)
:::             f... = Fractional Second, any number of digits
:::
:::           {zone} = Z|+hh[[:]mm]|-hh[[:]mm]
:::             Z    = Literal string indicating UTC
:::             hh   = Hour offset from UTC   (00-23)
:::             mm   = Minute offset from UTC (00-59)
:::
:::           Unspecified Month defaults to 01
:::           Unspecified Day of Month defaults to 01
:::           Unspecified {time} defaults to midnight local time
:::           Unspecified Minute defaults to 00
:::           Unspecified Second defaults to 00
:::           Unspecified Fractional Second defaults to .000
:::           Unspecified {zone} defaults to local time
:::           Unspecified Minute offset defaults to 00
:::           Fractional Seconds are truncated to milliseconds
:::
:::    -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.
:::       The TimeZoneMinuteOffset is truncated to an integral value.
:::       Default is empty, meaning local timezone.
:::
:::    -CZ TimeZoneMinuteOffset
:::
:::       Specifies the timezone to use for local time computations performed
:::       by the -C option. TimeZoneMinuteOffset is a JScript numeric expression
:::       that represents the number of minutes offset from UTC. The value is
:::       truncated to an integral value.
:::       Default is empty, meaning the machine's local timezone.
:::
:::    -C "Operation=Value[,Operation=Value]..."
:::
:::       Change the timestamp by applying a comma delimited list of one or more
:::       computations. The computation list must be enclosed within double
:::       quotes.
:::
:::       Each computation consists of an Operation, followed by =, followed
:::       by a Value.
:::
:::       The last character of the Operation specifies the timestamp component
:::       that is being modified:
:::          Y=Year, M=Month, D=Day of month,
:::          H=Hour, N=miNute, S=Second, F=millisecond (Fractional second)
:::
:::       If the character before the end is U, then the operation uses UTC time,
:::       otherwise local time is used. The local time defaults to the timezone
:::       specified by the machines locale. An alternate timezone may be chosen
:::       via the -CZ option. Note that local time computations are only affected
:::       by daylight savings if the default local timezone is used.
:::
:::       If the first character of the Operation is O, then the Operation adds
:::       an Offset to the current value. If not O, then the Operation sets an
:::       absolute value.
:::
:::       Offset operations can impact multiple components. Absolute operations
:::       generally set only the specified component, except that absolute Year
:::       and Month changes have the potential to modify the Day of the month.
:::
:::       All numeric Values are interpreted as JScript numerical expressions.
:::       Standard JScript arithmetic may be used, and the end result is
:::       truncated to an integral value.
:::
:::       Computations are performed from left to right. There is no limit to
:::       the number of computations - Operations may be repeated.
:::
:::       Details of every possible Operation are listed below:
:::
:::       OY=LocalYearOffset
:::
:::         Adds the LocalYearOffset to the local year.
:::         The offset value may be positive or negative.
:::
:::         The change will be a multiple of 24 hours, unless the operation
:::         crosses an odd number of local standard/daylightSavings boundries.
:::
:::       OUY=UTCYearOffset
:::
:::         Adds the UTCYearOffset to the UTC year.
:::         The offset value may be positive or negative.
:::
:::         The change is guaranteed to be a multiple of 24 hours.
:::
:::       OM=LocalMonthOffset
:::
:::         Adds the LocalMonthOffset to the local month.
:::         The offset value may be positive or negative.
:::
:::         The change will be a multiple of 24 hours, unless the operation
:::         crosses an odd number of local standard/daylightSavings boundries.
:::
:::       OUM=UTCMonthOffset
:::
:::         Adds the UTCMonthOffset to the UTC month.
:::
:::         The change is guaranteed to be a multiple of 24 hours.
:::
:::       OD=LocalDayOffset
:::
:::         Adds the LocalDayOffset to the current local day of the month.
:::         The offset value may be positive or negative.
:::
:::         The change will be a multiple of 24 hours, unless the operation
:::         crosses an odd number of local standard/daylightSavings boundries.
:::
:::       OD=LocalDayOfWeek:Occurrence
:::
:::         Sets the local date to a prior or subsequent LocalDayOfWeek,
:::         depending on the Occurrence. An Occurrence of 1 finds the next
:::         instance of the LocalDayOfWeek (no change if current local date
:::         already matches LocalDayOfWeek). An Occurrence of 2 finds the 2nd
:::         instance, etc. An Occurrence of -1 finds the prior LocalDayOfWeek,
:::         -2 the 2nd prior instance, etc.
:::
:::         The LocalDayOfWeek may be specified as the Weekday abbreviation or
:::         name (case ignored), or as a number, with 1=Sunday, and 7=Saturday.
:::
:::         The change will be a multiple of 24 hours, unless the operation
:::         crosses an odd number of local standard/daylightSavings boundries.
:::
:::       OUD=UTCDayOffset
:::
:::         Adds the UTCDayOffset to the current UTC day of the month.
:::         The offset value may be positive or negative.
:::
:::         The change is guaranteed to be a multiple of 24 hours.
:::
:::       OUD=UTCDayOfWeek:Occurrence
:::
:::         Sets the UTC date to a prior or subsequent UTCDayOfWeek,
:::         depending on the Occurrence. An Occurrence of 1 finds the next
:::         instance of the UTCDayOfWeek (no change if current UTC date
:::         already matches UTCDayOfWeek). An Occurrence of 2 finds the 2nd
:::         instance, etc. An Occurrence of -1 finds the prior UTCDayOfWeek,
:::         -2 the 2nd prior instance, etc.
:::
:::         The UTCDayOfWeek may be specified as the Weekday abbreviation or
:::         name (case ignored), or as a number, with 1=Sunday, and 7=Saturday.
:::
:::         The change is guaranteed to be a multiple of 24 hours.
:::
:::       OH=HourOffset
:::       OUH=HourOffset
:::
:::         Adds the HourOffset to the current timestamp.
:::         The offset value may be positive or negative.
:::         UTC and local hour offsets are handled the same way - the offset
:::         represents a fixed time interval, irrespective of timezone.
:::
:::       ON=MinuteOffset
:::       OUN=MinuteOffset
:::
:::         Adds the MinuteOffset to the current timestamp.
:::         The offset value may be positive or negative.
:::         UTC and local minute offsets are handled the same way - the offset
:::         represents a fixed time interval, irrespective of timezone.
:::
:::       OS=SecondOffset
:::       OUS=SecondOffset
:::
:::         Adds the SecondOffset to the current timestamp.
:::         The offset value may be positive or negative.
:::         UTC and local second offsets are handled the same way - the offset
:::         represents a fixed time interval, irrespective of timezone.
:::
:::       OF=MillisecondOffset
:::       OUF=MillisecondOffset
:::
:::         Adds the MillisecondOffset to the current timestamp.
:::         The offset value may be positive or negative.
:::         UTC and local millisecond offsets are handled the same way - the
:::         offset represents a fixed time interval, irrespective of timezone.
:::
:::       Y=LocalYear
:::
:::         Sets the local year value to LocalYear, without changing any other
:::         local values, with one possible exception - February 29 may become
:::         Feb 28 if the resultant year is not a leap year.
:::
:::       UY=UTCYear
:::
:::         Sets the UTC year value to UTCYear, without changing any other
:::         UTC values, with one possible exception - February 29 may become
:::         Feb 28 if the resultant year is not a leap year.
:::
:::       M=LocalMonth
:::
:::         Sets the local month to LocalMonth without changing any other values,
:::         except the date will be reduced to the last day of the month if the
:::         resultant month is shorter than the original day of month value.
:::         The LocalMonth value must be in the range 1 to 12.
:::
:::       UM=UTCMonth
:::
:::         Sets the UTC month to UTCMonth without changing any other values,
:::         except the date will be reduced to the last day of the month if the
:::         resultant month is shorter than the original day of month value.
:::         The UTCMonth value must be in the range 1 to 12.
:::
:::       D=LocalDay
:::
:::         Sets the local day of month according to the LocalDay value.
:::         The value may be positive or negative.
:::
:::         A positive value in the range 1 to 31, simply sets the date as
:::         specified. If the month has fewer days then LocalDay, then the date
:::         is set to the last day of the month.
:::
:::         A negative value in the range -1 to -32 is counted backward from the
:::         end of the month, where -1 represents the last day of the month.
:::         If the absolute value of LocalDay exceeds the number of days in the
:::         month, then the date is set to the first day of the month.
:::
:::       D=LocalDayOfWeek:Occurrence
:::
:::         Sets the local date to a specific day of the week within the current
:::         local month. The LocalDayOfWeek may be specified as the weekday name
:::         or abbreviation (case ignored), or as a number between 1 and 7, where
:::         1=Sunday, and 7=Saturday.
:::
:::         The Occurrence may be positive, in the range 1 to 5, where 1 is the
:::         first occurrence of the LocalDayOfWeek, 2 the 2nd, etc. If Occurrence
:::         is 5, but there are only 4 instances, then the date is set to the
:::         last occurence within the month.
:::
:::         The Occurrence may also be negative, in the range -1 to -5, where -1
:::         represents the last occurrence of the LocalDayOfWeek, -2 the next-to-
:::         last, etc. If the Occurrence is -5, but there are only 4 instances,
:::         then the date is set to the first occurence within the month.
:::
:::       UD=UTCDay
:::
:::         Sets the UTC day of month according to the UTCDay value.
:::         The value may be positive or negative.
:::
:::         A positive value in the range 1 to 31 simply sets the date as
:::         specified. If the month has fewer days then UTCDay, then the date
:::         is set to the last day of the month.
:::
:::         A negative value in the range -1 to -32 is counted backward from the
:::         end of the month, where -1 represents the last day of the month.
:::         If the absolute value of UTCDay exceeds the number of days in the
:::         month, then the date is set to the first day of the month.
:::
:::       UD=UTCDayOfWeek:Occurrence
:::
:::         Sets the UTC date to a specific day of the week within the current
:::         UTC month. The UTCDayOfWeek may be specified as the weekday name
:::         or abbreviation (case ignored), or as a number between 1 and 7, where
:::         1=Sunday, and 7=Saturday.
:::
:::         The Occurrence may be positive, in the range 1 to 5, where 1 is the
:::         first occurrence of the UTCDayOfWeek, 2 the 2nd, etc. If Occurrence
:::         is 5, but there are only 4 instances, then the date is set to the
:::         last occurence within the month.
:::
:::         The Occurrence may also be negative, in the range -1 to -5, where -1
:::         represents the last occurrence of the UTCDayOfWeek, -2 the next-to-
:::         last, etc. If the Occurrence is -5, but there are only 4 instances,
:::         then the date is set to the first occurence within the month.
:::
:::       H=LocalHour
:::
:::         Sets the local hour to LocalHour.
:::         The value must be in the range 0 to 23.
:::
:::       UH=UTCHour
:::
:::         Sets the UTC hour to UTCHour.
:::         The value must be in the range 0 to 23.
:::
:::       N=LocalMinute
:::
:::         Sets the local minute to LocalMinute
:::         The value must be in the range 0 to 59.
:::
:::       UN=UTCMinute
:::
:::         Sets the UTC minute to UTCMinute
:::         The value must be in the range 0 to 59.
:::
:::       S=LocalSecond
:::
:::         Sets the local second to LocalSecond.
:::         The value must be in the range 0 to 59.
:::
:::       US=UTCSecond
:::
:::         Sets the UTC second to UTCSecond.
:::         The value must be in the range 0 to 59.
:::
:::       F=LocalMillisecond
:::
:::         Sets the local millisecond to LocalMillisecond.
:::         The value must be in the range 0 to 999.
:::
:::       UF=UTCMillisecond
:::
:::         Sets the UTC millisecond to UTCMillisecond.
:::         The value must be in the range 0 to 999.
:::
:::    -F FormatString
:::
:::       Specify the returned timestamp format.
:::       Default is "{ISO-TS.}"
:::       Strings within braces are dynamic components.
:::       All other strings are literals.
:::       Available components (case ignored) are:
:::
:::         {YYYY}  4 digit year, zero padded
:::
:::         {YY}    2 digit year, zero padded
:::
:::         {Y}     year without zero padding
:::
:::         {MONTH} month name, as preferentially specified by:
:::                    1) -MONTH option
:::                    2) jTimestamp-MONTH environment variable
:::                    3) Mixed case, English month names
:::
:::         {MTH}   month abbreviation, as preferentially specified by:
:::                    1) -MTH option
:::                    2) jTimestamp-MTH environment variable
:::                    3) Mixed case, English month abbreviations
:::
:::         {MM}    2 digit month number, zero padded
:::
:::         {M}     month number without zero padding
:::
:::         {WEEKDAY} day of week name, as preferentially specified by:
:::                    1) -WEEKDAY option
:::                    2) jTimestamp-WEEKDAY environment variable
:::                    3) Mixed case, English day names
:::
:::         {WKD}   day of week abbreviation, as preferentially specified by:
:::                    1) -WKD option
:::                    2) jTimestamp-WKD environment variable
:::                    3) Mixed case, English day abbreviations
:::
:::         {W}     day of week number: 0=Sunday, 6=Saturday
:::
:::         {DD}    2 digit day of month number, zero padded
:::
:::         {D}     day of month number, without zero padding
:::
:::         {DDY}   3 digit day of year number, zero padded
:::
:::         {DY}    day of year number, 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
:::
:::         {ISOTS}  same as {ISOTS.}
:::
:::         {ISOTS.} YYYYMMDDThhmmss.fff+hhss
:::                  Compressed ISO 8601 date/time (timestamp) with milliseconds
:::                  and time zone, using . as decimal mark
:::
:::         {ISOTS,} YYYYMMDDThhmmss,fff+hhss
:::                  Compressed ISO 8601 date/time (timestamp) with milliseconds
:::                  and time zone, using , as decimal mark
:::
:::         {ISODT}  YYYYMMDD
:::                  Compressed ISO 8601 date format
:::
:::         {ISOTM}  same as {ISOTM.}
:::
:::         {ISOTM.} hhmmss.fff
:::                  Compressed ISO 8601 time format with milliseconds,
:::                  using . as decimal mark
:::
:::         {ISOTM,} hhmmss,fff
:::                  Compressed ISO 8601 time format with milliseconds,
:::                  using , as decimal mark
:::
:::         {ISOTZ}  +hhmm
:::                  Compressed ISO 8601 timezone format
:::
:::         {ISOWY}  yyyy
:::                  ISO 8601 week numbering year
:::                  Dec 29, 30, or 31 may belong to the next Jan year
:::                  Jan 01, 02, or 03 may belong to the prior Dec year
:::
:::         {ISOWK}  ww
:::                  ISO 8601 week number
:::                  Week 01 is the week with the year's first Thursday
:::
:::         {ISOWD}  d
:::                  ISO 8601 day of week: 1=Monday, 7=Sunday
:::
:::         {ISODTW} yyyyWwwd
:::                  Compressed ISO 8601 week date format
:::
:::         {ISODTO} YYYYDDD
:::                  Compressed ISO 8601 ordinal date format
:::
:::         {ISO-TS} same as {ISO-TS.}
:::
:::         {ISO-TS.} YYYY-MM-DDThh:mm:ss.fff+hh:ss
:::                  ISO 8601 date/time (timestamp) with milliseconds and time zone
:::                  using . as decimal mark
:::
:::         {ISO-TS,} YYYY-MM-DDThh:mm:ss,fff+hh:ss
:::                  ISO 8601 date/time (timestamp) with milliseconds and time zone
:::                  using , as decimal mark
:::
:::         {ISO-DT} YYYY-MM-DD
:::                  ISO 8601 date format
:::
:::         {ISO-TM} same as {ISO-TM.}
:::
:::         {ISO-TM.} hh:mm:ss.fff
:::                  ISO 8601 time format with milliseconds,
:::                  using . as decimal mark
:::
:::         {ISO-TM,} hh:mm:ss,fff
:::                  ISO 8601 time format with milliseconds,
:::                  using , as decimal mark
:::
:::         {ISO-TZ} +hh:mm
:::                  ISO 8601 timezone  (same as {TZ})
:::
:::         {ISO-DTW} yyyy-Www-d
:::                  ISO 8601 week date format
:::
:::         {ISO-DTO} YYYY-DDD
:::                  ISO 8601 ordinal date format
:::
:::         {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
:::
:::         {U}     Same as {US}
:::
:::         {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.
:::       Default is empty, meaning write to stdout.
:::
:::    -WKD "Abbreviated day of week list"
:::
:::       Override the default day abbreviations with a space delimited,
:::       quoted list, starting with Sun.
:::       Default is mixed case, 3 character English day abbreviations.
:::
:::    -WEEKDAY "Day of week list"
:::
:::       Override the default day names with a space delimited, quoted list,
:::       starting with Sunday.
:::       Default is mixed case English day names.
:::
:::    -MTH "Abbreviated month list"
:::
:::       Override the default month abbreviations with a space delimited,
:::       quoted list, starting with Jan.
:::       Default is mixed case, 3 character English month abbreviations.
:::
:::    -MONTH "Month list"
:::
:::       Override the default month names with a space delimited, quoted list,
:::       starting with January.
:::       Default is mixed case English month names.
:::
:::
:::  jTimestamp.bat was written by Dave Benham and originally posted at
:::  http://www.dostips.com/forum/viewtopic.php?f=3&t=7523
:::

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

:: Define options
set ^"options=^
 -?:^
 -??:^
 -v:^
 -u:^
 -z:""^
 -cz:""^
 -f:"{ISO-TS}"^
 -d:""^
 -c:""^
 -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 (
  if defined jTimestamp%%A (set "%%A=!jTimestamp%%A!") else set "%%A=%%~B"
)
set "-?="
set "-??="

:: 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 paged help
if defined -?? (
  (for /f "delims=: tokens=*" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A)|more /e
  exit /b 0
) 2>nul

:: 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 "^::jTimestamp\.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 _g = new Object();
_g.eval=function(str){return eval(str);}
_g.main=function(){
  var env  = WScript.CreateObject("WScript.Shell").Environment("Process"),
      wkd     = env('-WKD').split(' '),
      weekday = env('-WEEKDAY').split(' '),
      mth     = env('-MTH').split(' '),
      month   = env('-MONTH').split(' '),
      stderr  = WScript.StdErr,
      utc,y,m,d,w,h,h12,n,s,f,u,z,zs,za,
      pc=':', pcm=',', pd='-', pp='.', p2='00', p3='000', p4='0000',
      dt, isoDt, isoTmStr, isoDtTest;

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

  // Extend Date prototype
  Date.prototype.getYr   = function(utc){ return utc ? this.getUTCFullYear()    : this.getFullYear();}
  Date.prototype.getMo   = function(utc){ return utc ? this.getUTCMonth()       : this.getMonth();}
  Date.prototype.getDt   = function(utc){ return utc ? this.getUTCDate()        : this.getDate();}
  Date.prototype.getDy   = function(utc){ return utc ? this.getUTCDay()         : this.getDay();}
  Date.prototype.getHr   = function(utc){ return utc ? this.getUTCHours()       : this.getHours();}
  Date.prototype.getMin  = function(utc){ return utc ? this.getUTCMinutes()     : this.getMinutes();}
  Date.prototype.getSec  = function(utc){ return utc ? this.getUTCSeconds()     : this.getSeconds();}
  Date.prototype.getMsec = function(utc){ return utc ? this.getUTCMilliseconds(): this.getMilliseconds();}
  Date.prototype.setYr   = function(utc,y){   if(utc) this.setUTCFullYear(y);     else this.setFullYear(y);}
  Date.prototype.setMo   = function(utc,m,d){ if(utc) this.setUTCMonth(m,d);      else this.setMonth(m,d);}
  Date.prototype.setDt   = function(utc,d){   if(utc) this.setUTCDate(d);         else this.setDate(d);}
  Date.prototype.setHr   = function(utc,h){   if(utc) this.setUTCHours(h);        else this.setHours(h);}
  Date.prototype.setMin  = function(utc,m){   if(utc) this.setUTCMinutes(m);      else this.setMinutes(m);}
  Date.prototype.setSec  = function(utc,s){   if(utc) this.setUTCSeconds(s);      else this.setSeconds(s);}
  Date.prototype.setMsec = function(utc,f){   if(utc) this.setUTCMilliseconds(f); else this.setMilliseconds(f);}

  function newDate(utc,y,m,d,h,n,s,f) {
    var dt = new Date(0);
    if(utc) dt.setUTCFullYear(y,m,d); else dt.setFullYear(y,m,d);
    if(utc) dt.setUTCHours(h,n,s,f);  else dt.setHours(h,n,s,f);
    return dt;
  }

  // Establish base date dt
  isoDt = env('-D').match( new RegExp(
    //                    1=year        2=week                    3=weekDay
    "^\\s*[Ii][Ss][Oo]\\s+(\\d{4})(?:-?W(0[1-9]|[1-4]\\d|5[0-3])-?([1-7])" +
    //  4=ordinalDay                                          5=month             6=monthDay                     7=hour
    "|-?(00[1-9]|0[1-9]\\d|[12]\\d\\d|3[0-5]\\d|36[0-6])|(?:-?(0[1-9]|1[0-2])(?:-?(0[1-9]|[12]\\d|3[01]))?)?)(?:T([01]\\d|2[0-3])" +
    //    8=minute       9=second            10=fraction                11=Z   12=sgn 13=zoneHourOffset       14=zoneMinuteOffset
    "(?::?([0-5]\\d)(?::?([0-5]\\d)(?:[.,](?:(\\d\\d?\\d?)\\d*))?)?)?(?:(Z)|(?:([+-])([01]\\d|[+-]2[0-3])(?::?([0-5]\\d))?))?)?\\s*$", ""
  ));

  if (isoDt) {
    utc = (isoDt[11]||isoDt[12]);
    y = parseInt(isoDt[1],10);
    h = isoDt[7]  ? parseInt(isoDt[7],10) : 0;
    n = isoDt[8]  ? parseInt(isoDt[8],10) : 0;
    s = isoDt[9]  ? parseInt(isoDt[9],10) : 0;
    f = isoDt[10] ? parseInt((isoDt[10]+'000').substring(0,3),10) : 0;
    if (isoDt[2]) {
      // Week Date
      dt = newDate(utc,y,0,1,h,n,s,f);
      w = dt.getDy(utc);
      if (isoDt[2]=='53') {
        y = parseInt(isoDt[1],10);
        if (w!=4 && (w!=3||y%4||(!(y%100)&&y%400))) badOp('-D');
      }
      d = (w<=4)?1-w:(w==5)?3:2;
      dt.setDt( utc, ((parseInt(isoDt[2],10)-1)*7) + parseInt(isoDt[3],10) + d );
    } else if (isoDt[4]) {
      // Ordinal Date
      if (isoDt[4]=='366' && (y%4||(!(y%100)&&y%400)) ) badOp('-D');
      dt = newDate(utc,y,0,parseInt(isoDt[4],10),h,n,s,f);
    } else {
      // Calendar Date
      d = isoDt[6]?parseInt(isoDt[6],10):1
      dt = newDate(utc,y,isoDt[5]?parseInt(isoDt[5],10)-1:0,d,h,n,s,f);
      if ( d>=29 && dt.getDt(utc)<10 ) badOp('-D');
    }
    if (isoDt[12]) {
      dt.setUTCHours( dt.getUTCHours()-parseInt(isoDt[12]+isoDt[13],10) );
      if (isoDt[14]) dt.setUTCMinutes( dt.getUTCMinutes()-parseInt(isoDt[12]+isoDt[14],10) );
    }
  } else {
    try {
      dt = _g.eval('new Date('+env('-D')+')');
    } catch(e) {
    } finally {
      if (isNaN(dt)) badOp('-D');
    }
  }

  compute(env('-C'));
  function compute(comp) {
    var i, op, val, mnth, day, num, unit, tz, utc0, utc, offset, mem ;
    function lookup(ar,val) {
      var i;
      val=val.toUpperCase();
      for (i=ar.length-1; i>=0; i-- ) if (ar[i].toUpperCase()==val) return i;
      return i;
    }
    if (!comp) return;
    comp=comp.split(',');
    if (env('-CZ')) tz=getNum(env('-CZ'),'-CZ value');
    for (i=0; i<comp.length; i++) {
      mem='-C['+i+'] - '
      op=comp[i].split('=');
      if (!op[0]) badOp(mem+'Missing operation');
      if (op.length==1) badOp(mem+'Missing value');
      if (op.length>2) badOp(mem+'Invalid syntax');
      val=op[1].split(':');
      op=op[0].toUpperCase();
      unit=op.charAt(op.length-1);
      offset=(op.charAt(0)=='O');
      utc0=(op.charAt(op.length-2)=='U');
      utc=( utc0 || env('-CZ') );
      switch (val.length) {
        case 1:
          if (!val[0]) badOp(mem+'Missing value');
          num=getNum(val[0],mem+'Invalid value');
          if (!offset) switch (unit) {
            case 'M': if (num<1 || num>12)             badOp(mem+'Invalid month');       break;
            case 'D': if (num<-31 || num==0 || num>31) badOp(mem+'Invalid month date');  break;
            case 'H': if (num<0 || num>23)             badOp(mem+'Invalid hour');        break;
            case 'N': if (num<0 || num>59)             badOp(mem+'Invalid minute');      break;
            case 'S': if (num<0 || num>59)             badOp(mem+'Invalid second');      break;
            case 'F': if (num<0 || num>999)            badOp(mem+'Invalid millisecond'); break;
          }
          day=undefined;
          break;
        case 2:
          if (unit!='D') badOp(mem+'Invalid syntax');
          if (!val[0]) badOp(mem+'Missing day');
          if (!val[1]) badOp(mem+'Missing occurrence');
          day=lookup(wkd,val[0])+1;
          if (!day) day=lookup(weekday,val[0])+1;
          if (!day) day=getNum(val[0],mem+'Invalid day');
          num=getNum(val[1],mem+'Invalid occurrence');
          if (day<1 || day>7) badOp(mem+'Invalid day');
          if (!offset) if (num<-5 || num==0 || num>5) badOp(mem+'Invalid occurrence')
          break;
        default:
          badOp(mem+'Invalid syntax');
      }
      if (!utc0 && utc) dt.setUTCMinutes( dt.getUTCMinutes()+tz );
      switch(unit) {
        case 'Y':
          day= dt.getDt(utc);
          dt.setYr( utc, num+(offset?dt.getYr(utc):0) );
          if (dt.getDt(utc)!=day) dt.setDt(utc,0);
          break;
        case 'M':
          day=dt.getDt(utc);
          dt.setMo( utc, (offset?num:num-1)+(offset?dt.getMo(utc):0), dt.getDt(utc) );
          if (dt.getDt(utc)!=day) dt.setDt(utc,0);
          break;
        case 'D':
          if (offset) {
            if (day) {
              day=day-1-dt.getDy(utc);
              num=(num-(num>0?1:0))*7 + (day<0?day+7:day);
            }
            dt.setDt( utc, dt.getDt(utc)+num );
          } else {
            if (day) {
              if (num>0) {
                dt.setDt(utc,1);
                day=day-dt.getDy(utc);
                num = (day<=0?day:day-7) + num*7;
                dt.setDt(utc,num);
                if (dt.getDt(utc)!=num) dt.setDt( utc, dt.getDt(utc)-7 );
              } else {
                num=-num;
                dt.setMo( utc, dt.getMo(utc)+1, 0 );
                day=day-dt.getDy(utc)-1;
                num = dt.getDt(utc) + (day<=0?day+7:day) - num*7
                dt.setDt( utc, num<1?num+7:num );
              }
            } else {
              if (num>0) {
                dt.setDt( utc, num )
                if (dt.getDt(utc)!=num) dt.setDt(utc,0);
              } else {
                mnth=dt.getMo(utc);
                dt.setMo( utc, mnth+1, num+1 );
                if (dt.getMo(utc)!=mnth) dt.setMo( utc, dt.getMo(utc)+1, 1 );
              }
            }
          }
          break;
        case 'H':
          if (offset) dt.setUTCHours(num+dt.getUTCHours());
          else dt.setHr(utc,num);
          break;
        case 'N':
          if (offset) dt.setUTCMinutes(num+dt.getUTCMinutes());
          else dt.setMin(utc,num);
          break;
        case 'S':
          if (offset) dt.setUTCSeconds(num+dt.getUTCSeconds());
          else dt.setSec(utc,num);
          break;
        case 'F':
          if (offset) dt.setUTCMilliseconds(num+dt.getUTCMilliseconds());
          else dt.setMsec(utc,num);
          break;
        default:
          badOp(mem+'Invalid operation');
      }
      if (!utc0 && utc) dt.setUTCMinutes( dt.getUTCMinutes()-tz );
    }
  }

  utc = env('-U')!='';
  if (env('-Z')) {
    dt.setUTCMinutes( dt.getUTCMinutes() + (z=getNum(env('-Z'),'-Z value')) );
  } else if (utc) z=0; else z=-dt.getTimezoneOffset();
  zs = z<0 ? '-' : '+';
  za = Math.abs(z);

  y = dt.getYr(utc);
  m = dt.getMo(utc);
  d = dt.getDt(utc);
  w = dt.getDy(utc);
  h = dt.getHr(utc);
  n = dt.getMin(utc);
  s = dt.getSec(utc);
  f = dt.getMsec(utc);
  u = dt.getTime();

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

  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( str, name ) {
    var rtn;
    try {
      rtn = Number(_g.eval(str));
    } catch(e) {
    } finally {
      if (isNaN(rtn-rtn)) badOp(name);
      return rtn;
    }
  }
  
  function writeErr(str) {
    stderr.WriteLine(str);
  }

  function badOp(str) {
    writeErr('Error: Invalid '+str);
    WScript.Quit(1);
  }
  
  function trunc( n ) { return Math[n>0?"floor":"ceil"](n); }
  
  function weekNum(dt) {
    var dt2, day, now;
    dt2 = new Date(dt.valueOf());
    day = (dt2.getDy(utc)+6)%7;
    dt2.setDt( utc, dt2.getDt(utc)-day+3);
    now = dt2.valueOf();
    dt2.setMo( utc, 0, 1 );
    if (dt2.getDy(utc) != 4) dt2.setMo( utc, 0, (11-dt2.getDy(utc))%7 + 1 );
    return 1 + Math.ceil((now-dt2.valueOf())/604800000);
  }

  function weekYr(dt) {
    var dt2 = new Date(dt.valueOf());
    dt2.setDt( utc, dt2.getDt(utc)+3-(dt2.getDy(utc)+6)%7 );
    return dt2.getYr(utc);
  }
  
  function ordDt() {
    return trunc( ( (new Date(0)).setUTCFullYear(y,m,d) - (new Date(0)).setUTCFullYear(y,0,0) ) / 86400000 )
  }
  
  function repl($0,$1) {
    switch ($1.toUpperCase()) {
      case 'YYYY' : return lpad(y,p4);
      case 'YY'   : return (p2+y.toString()).slice(-2);
      case 'Y'    : return y.toString();
      case 'MM'   : return lpad(m+1,p2);
      case 'M'    : return (m+1).toString();
      case 'DD'   : return lpad(d,p2);
      case 'D'    : return d.toString();
      case 'W'    : return w.toString();
      case 'DY'   : return ordDt().toString();
      case 'DDY'  : return lpad( ordDt(), p3);
      case 'HH'   : return lpad(h,p2);
      case 'H'    : return h.toString();
      case 'HH12' : return lpad(h12,p2);
      case 'H12'  : return h12.toString();
      case 'NN'   : return lpad(n,p2);
      case 'N'    : return n.toString();
      case 'SS'   : return lpad(s,p2);
      case 'S'    : return s.toString();
      case 'FFF'  : return lpad(f,p3);
      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'    :
      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,p3);
      case 'Z'    : return z.toString();
      case 'ZS'   : return zs;
      case 'ZH'   : return lpad(trunc(za/60),p2);
      case 'ZM'   : return lpad(za%60,p2);
      case 'TZ'   :
      case 'ISO-TZ' : return ''+zs+lpad(trunc(za/60),p2)+pc+lpad(za%60,p2);
      case 'ISOTS'  :
      case 'ISOTS.' : return ''+lpad(y,p4)+lpad(m+1,p2)+lpad(d,p2)+'T'+lpad(h,p2)+lpad(n,p2)+lpad(s,p2)+pp
                             +lpad(f,p3)+zs+lpad(trunc(za/60),p2)+lpad(za%60,p2);
      case 'ISOTS,' : return ''+lpad(y,p4)+lpad(m+1,p2)+lpad(d,p2)+'T'+lpad(h,p2)+lpad(n,p2)+lpad(s,p2)+pcm
                             +lpad(f,p3)+zs+lpad(trunc(za/60),p2)+lpad(za%60,p2);
      case 'ISODT'  : return ''+lpad(y,p4)+lpad(m+1,p2)+lpad(d,p2);
      case 'ISOTM'  :
      case 'ISOTM.' : return ''+lpad(h,p2)+lpad(n,p2)+lpad(s,p2)+pp+lpad(f,p3);
      case 'ISOTM,' : return ''+lpad(h,p2)+lpad(n,p2)+lpad(s,p2)+pcm+lpad(f,p3);
      case 'ISOTZ'  : return ''+zs+lpad(trunc(za/60),p2)+lpad(za%60,p2);
      case 'ISO-TS' :
      case 'ISO-TS.': return ''+lpad(y,p4)+pd+lpad(m+1,p2)+pd+lpad(d,p2)+'T'+lpad(h,p2)+pc+lpad(n,p2)+pc
                             +lpad(s,p2)+pp+lpad(f,p3)+zs+lpad(trunc(za/60),p2)+pc+lpad(za%60,p2);
      case 'ISO-TS,': return ''+lpad(y,p4)+pd+lpad(m+1,p2)+pd+lpad(d,p2)+'T'+lpad(h,p2)+pc+lpad(n,p2)+pc
                             +lpad(s,p2)+pcm+lpad(f,p3)+zs+lpad(trunc(za/60),p2)+pc+lpad(za%60,p2);
      case 'ISO-DT' : return ''+lpad(y,p4)+pd+lpad(m+1,p2)+pd+lpad(d,p2);
      case 'ISO-TM' :
      case 'ISO-TM.': return ''+lpad(h,p2)+pc+lpad(n,p2)+pc+lpad(s,p2)+pp+lpad(f,p3);
      case 'ISO-TM,': return ''+lpad(h,p2)+pc+lpad(n,p2)+pc+lpad(s,p2)+pcm+lpad(f,p3);
      case 'ISOWY'  : return ''+lpad(weekYr(dt),p4);
      case 'ISOWK'  : return ''+lpad(weekNum(dt),p2);
      case 'ISOWD'  : return ''+((w+6)%7+1);
      case 'ISODTW' : return ''+lpad(weekYr(dt),p4)+'W'+lpad(weekNum(dt),p2)+((w+6)%7+1);
      case 'ISODTO' : return ''+lpad(y,p4)+lpad(ordDt(),p3);
      case 'ISO-DTW': return ''+lpad(weekYr(dt),p4)+pd+'W'+lpad(weekNum(dt),p2)+pd+((w+6)%7+1);
      case 'ISO-DTO': return ''+lpad(y,p4)+pd+lpad(ordDt(),p3);
      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;
    }
  }
}
_g.main();

Dave Benham

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#2 Post by dbenham » 23 Jan 2017 22:24

I was working on some enhancements to getTimestamp.bat, and ran into a situation where the behavior and calling syntax had to change. I generally want successive versions to be backward compatible, but the new features made that impossible. So I decided to use a new name so existing scripts that use getTimestamp would not be broken.

So with this post I introduce the new jTimestamp.bat v1.1
jTimestamp1.1.zip


Below is a summary of new features, and differences from the old getTimestamp.bat

1) The many offset options of getTimestamp have been collapsed into a single -C (compute) option that contains a comma delimited list of operations. This change was necessary because the order of operations has an impact on the result, so it is important that the user has control. The fundamental architecture of getTimestamp.bat did not allow this.

In addition, operations have been added to set any date/time component to a specific value, without impacting other components (with a few necessary exceptions)

Also, offset and/or absolute day of month operations may be specified as a particular occurrence of a day of the week.

Example - Set the date to the last Monday of next month: First I add one to the current month, and then I compute the last Monday of the resultant month.

Code: Select all

>:: Show current date
>jTimestamp /F "{wkd} {mm}/{dd}/{yyyy}"
Tue 11/01/2016

>:: Get last Monday of next month
>jTimestamp /C "OM=1,D=mon:-1" /F "{wkd} {mm}/{dd}/{yyyy}"
Mon 12/26/2016


2) All computations may now be performed using UTC values or local values. This can have subtle, but important differences.

Example:

Code: Select all

>:: Set the local date to the 15th of the month, preserving current time.
>jTimestamp /D "'10/31/2016 22:30:00 EDT'" /C "D=15"
2016-10-15T22:30:00.000-04:00

>:: Set the UTC date to the 15th of the month, preserving current UTC time. Result is still displayed in local time.
>jTimestamp /D "'10/31/2016 22:30:00 EDT'" /C "UD=15"
2016-11-14T21:30:00.000-05:00
The last result could use some explanation :wink:
The starting local time is shortly before midnight, but the UTC time is after midnight the next day. The next day happens to be the next month, so we end up with 11/15 instead of 10/15, and then the displayed date is converted back to local date/time (the 14th). Also, the 14th is after daylight savings transitions back to standard time, so local time "falls back" 1 hour, and the time zone changes from -04:00 to -05:00.


3) Offset computations involving Month and Year now give a result that is more intuitive. The old behavior that used default JScript date behavior would sometimes give results that were counter-intuitive, and not particularly useful.

For example, adding 1 month to 3/31/2016 now gives a reasonable 4/30/2016, whereas getTimestamp would give 5/1/2016.
And adding 1 year to 2/29/2016 now gives 2/28/2017, whereas getTimestamp gave 3/1/2017.

Code: Select all

>getTimestamp /D '3/31/2016' /OM 1 /F {mm}/{dd}/{yyyy}
05/01/2016

>jTimestamp /D '3/31/2016' /C "OUM=1" /F {mm}/{dd}/{yyyy}
04/30/2016

>getTimestamp /D '2/29/2016' /OY 1 /F {mm}/{dd}/{yyyy}
03/01/2017

>jTimestamp /D '2/29/2016' /C "OUY=1" /F {mm}/{dd}/{yyyy}
02/28/2017


I believe that sums up the changes and enhancements. If I get some more energy, I may try to put some more examples in this post at a later date.

As always full documentation is embedded within the script, and may be accessed from the command line via jTimestamp /?.

Dave Benham

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#3 Post by dbenham » 23 Jan 2017 23:30

Here is version 2.0
jTimestamp2.0.zip
316 downloads from the main release page when 2.0 was the current version
(10.19 KiB) Downloaded 1066 times

New Features:

1) Added support for ISO8601 format to the -D option

I got a request for this at viewtopic.php?f=3&t=7667.

Up until now, jTimestamp.bat (and getTimestamp.bat) relied on the native Date constructor to parse the -D input date. But that doesn't support ISO8601 format. So I had to write a fair amount of new code to add this feature.

I had to do something to differentiate ISO8601 from the other formats, so I chose to require the string "ISO " to prefix the value.

An error will be returned if the format is invalid, or if the specified data is invalid. For example, "ISO 2017-02-29T13:20:00.000Z" will error out because 2017 is not a leap year, so Feb does not have 29 days. This behavior is different than all the other data specifiers. For example "'2/29/2017'" would be interpreted as 3/1/2017 by the default string parser.

Punctuation is optional, and either dot or comma can be used as a decimal indicator - For example, the following are equivalent:

Code: Select all

-D "ISO 2017-07-04T12:30:00.123-05:00"
-D "ISO 20170704T123000,123-0500"

Month and Day are optional for calendar dates. The default month is January, and the default day is 1.
Time (and timezone) are always optional. And time precision can be stopped at any level.
If the timezone is not specified, then it defaults to the machines local time. Timezone cannot be specified unless at least the hours are given.
The following are all equivalent ways of representing midnight on January 1, 2017, local timezone:

Code: Select all

-D "ISO 2017-01-01T00:00:00.000"
-D "ISO 2017-01-01T00:00:00"
-D "ISO 2017-01-01T00:00"
-D "ISO 2017-01-01T00"
-D "ISO 2017-01-01"
-D "ISO 2017-01"
-D "ISO 2017"

The timezone offset minutes are optional - they default to 00 if only the offset hour is given. The following are equivalent:

Code: Select all

-D "ISO 2017-07-04T00:00:00-05:00"
-D "ISO 2017-07-04T00:00:00-05"

The date may also be represented as an ordinal date (number of days from the beginning of the year).
For example, the following represents the last day of 2016 (December 31 since it was a leap year)

Code: Select all

-D "ISO 2016-366"

The date may also be represented as an ISO week date, where every year is considered to have an even number of weeks, and the first day of the week numbering year is typically not January 1. Weeks always start on a Monday (day 1), and the first week is the week that contains the first Thursday in January. For example, Tuesday of the 32nd week numbering year 2017 can be specified as:

Code: Select all

-D "ISO 2017-W32-2"


2) New -CZ option to specify the timezone for -C computations.

Prior to this release, all -C computations were either performed using the machine's local time, or else UTC time. The -CZ option lets you specify the timezone to be used for local time computations. The timezone is specified as the number of minutes offset from UTC (same as the -Z option).


3) Fixed ordinal date, week number, and week numbering year computation for -F option

Version 1.1 had a bug that could result in an incorrect ordinal day of year, week number, or week numbering year -F output if the -U or -Z option was used. Version 2.0 fixes the problem.

The new ISO8601 support for -D made it really convenient to detect the problem, and test the fix. It enables me to specify an ISO8601 format and get the same value back. There is a lot of computation going on, and it won't give the same value back if there is a bug.
For example:

Code: Select all

C:\test>jTimestamp -D "ISO 2004-W53-1T01:23:45.678+15:30" -Z 930 -F "{ISO-DTW}T{ISO-TM}{ISO-TZ}"
2004-W53-1T01:23:45.678+15:30


4) Fixed documentation of the -D option
Some examples given for the -D "'Date Time Zone'" option were wrong, and have been fixed.


Dave Benham

war59312
Posts: 10
Joined: 07 Feb 2017 20:17
Location: U.S.A
Contact:

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#4 Post by war59312 » 07 Feb 2017 20:27

Very nice. Thank you!

I'm using:
CALL jTimestamp /F " {WKD} {MTH} {D}, {yyyy} {H12}:{NN}:{S}{PM}"
Simple enough, sample output of "Tue Feb 7, 2017 4:26:00pm".

Would be great if able to make that show 4:26pm when zero seconds past. That is, hide when zeros. And I don't mean padding. I'd still like to see "4:26:03pm" for example.

war59312
Posts: 10
Joined: 07 Feb 2017 20:17
Location: U.S.A
Contact:

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#5 Post by war59312 » 07 Feb 2017 20:49

Also, I've been using this in my scripts:

Code: Select all

REM Get Script Start Time Using jTimestamp.cmd
CALL jTimestamp -f {ums} -r t1

REM Get Start Time
for /F "tokens=1-4 delims=:.," %%a in ("%time%") do (
   set /A "start=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)

REM Get Script End Time Using getTimestamp.cmd
CALL jTimestamp -f {ums} -r t2

REM Get End Time
for /F "tokens=1-4 delims=:.," %%a in ("%time%") do (
   set /A "end=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)

REM Get Elapsed Time
set /A elapsed=end-start

REM Format Elapsed Time
set /A hh=elapsed/(60*60*100), rest=elapsed%%(60*60*100), mm=rest/(60*100), rest%%=60*100, ss=rest/100, cc=rest%%100
if %mm% lss 10 set mm=0%mm%
if %ss% lss 10 set ss=0%ss%
if %cc% lss 10 set cc=0%cc%
ECHO.

REM Save Elapsed Time Using jTimestamp.cmd
CALL jTimestamp -d %t2%-%t1% -f "AVG 2017 Install Script Ran For {ud} Days {h} Hours {n} Minutes And {s} Seconds" -u >> "%CD%\AvgInstallLog.txt"

REM Show Elapsed Time Using jTimestamp.cmd
CALL :colorEcho e0 "AVG Install Script Ran For"
CALL jTimestamp  -d %t2%-%t1% -f " {ud} Days {h} Hours {n} Minutes And {s} Seconds" -u
ECHO.
Sadly, there is a bug if the day switches.

Will Report Negative Numbers And Leading Zero For Minutes And Seconds

For Example: "Script Ran For -11 Hours 0-22 Minutes And 0-37 Seconds

Any ideas? Probably way better way of doing things?

Thanks for the assistance.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#6 Post by dbenham » 08 Feb 2017 09:09

war59312 wrote:Would be great if able to make that show 4:26pm when zero seconds past. That is, hide when zeros. And I don't mean padding. I'd still like to see "4:26:03pm" for example.

Sorry. It could be done, but that is not a feature I am interested in supporting.

You could get the value you want easily enough if you store the result in a variable, call it TM, and then use:

Code: Select all

set "TM=%TM:00am=am%"
set "TM=%TM:00pm=pm%"
echo %TM%


war59312 wrote:

Code: Select all

REM Show Elapsed Time Using jTimestamp.cmd
CALL :colorEcho e0 "AVG Install Script Ran For"
CALL jTimestamp  -d %t2%-%t1% -f " {ud} Days {h} Hours {n} Minutes And {s} Seconds" -u
ECHO.

Sadly, there is a bug if the day switches.

Will Report Negative Numbers And Leading Zero For Minutes And Seconds

For Example: "Script Ran For -11 Hours 0-22 Minutes And 0-37 Seconds

Any ideas? Probably way better way of doing things?

That is the correct way to get elapsed time using jTimestamp. I cannot reproduce your result. The manner in which t1 and t2 are populated does not matter. I got correct output for a time interval of 58+ days in my testing.

What version of jTimestamp are you using :?:

Note that t2 must be greater than t1. jTimestamp cannot properly compute a negative time interval unless you report a single time unit such as decimal days {UDD}.


Dave Benham

war59312
Posts: 10
Joined: 07 Feb 2017 20:17
Location: U.S.A
Contact:

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#7 Post by war59312 » 09 Feb 2017 11:20

dbenham wrote:
war59312 wrote:Would be great if able to make that show 4:26pm when zero seconds past. That is, hide when zeros. And I don't mean padding. I'd still like to see "4:26:03pm" for example.
Sorry. It could be done, but that is not a feature I am interested in supporting.

You could get the value you want easily enough if you store the result in a variable, call it TM, and then use:

Code: Select all

set "TM=%TM:00am=am%"
set "TM=%TM:00pm=pm%"
echo %TM%
Dave Benham
Thanks For That - Little Correction - Need Double :

Works perfectly:

Code: Select all

REM Fancy Time Stamp
for /F "tokens=*" %%a in (
   'jTimestamp /F " {WKD} {MTH} {D}, {yyyy} {H12}:{NN}:{SS}{PM}"'
) do (
   set Timestamp=%%a
)

set "Timestamp=%Timestamp::00am=am%"
set "Timestamp=%Timestamp::00pm=pm%"
echo Timestamp %Timestamp%
Example Output:

Code: Select all

Timestamp Thu Feb 9, 2017 12:21pm
Regarding:
dbenham wrote:
war59312 wrote:

Code: Select all

REM Show Elapsed Time Using jTimestamp.cmd
CALL :colorEcho e0 "AVG Install Script Ran For"
CALL jTimestamp  -d %t2%-%t1% -f " {ud} Days {h} Hours {n} Minutes And {s} Seconds" -u
ECHO.

Sadly, there is a bug if the day switches.

Will Report Negative Numbers And Leading Zero For Minutes And Seconds

For Example: "Script Ran For -11 Hours 0-22 Minutes And 0-37 Seconds

Any ideas? Probably way better way of doing things?

That is the correct way to get elapsed time using jTimestamp. I cannot reproduce your result. The manner in which t1 and t2 are populated does not matter. I got correct output for a time interval of 58+ days in my testing.

What version of jTimestamp are you using :?:

Note that t2 must be greater than t1. jTimestamp cannot properly compute a negative time interval unless you report a single time unit such as decimal days {UDD}.

Dave Benham
Using jTimestamp 2.0.

OK, I will try and come up with duplication steps.

Thanks again! Awesome work!!

Edit: Sometimes it's off by a second:

Example Output...

Correct:

Code: Select all

AVG Install Started On 071D111100 (10.71.2.33) @ Thu Feb 9, 2017 1:24:53pm

AVG Install Script Ended @ Thu Feb 9, 2017 1:25pm

AVG Install Script Ran For 0 Days 0 Hours 0 Minutes And 7 Seconds
Off by 1 second:

Code: Select all

AVG Install Started On 071D111100 (10.71.2.33) @ Thu Feb 9, 2017 1:25:12pm

AVG Install Script Ended @ Thu Feb 9, 2017 1:25:20pm

AVG Install Script Ran For 0 Days 0 Hours 0 Minutes And 7 Seconds
Code:

Code: Select all

REM Get Script Start Time Using jTimestamp.cmd
CALL jTimestamp -f {ums} -r t1

REM Get Time Script Started At
CALL :colorEcho e0 "AVG Install Started On"

:: Define BS to contain a backspace
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "BS=%%A"

::If prompt is not at line beginning then need an extra char before %BS%
<nul set /p "=.%BS% "

:ComputerName
REM Get Computer Name
FOR /F "usebackq" %%i IN (`hostname`) DO SET ComputerName=%%i

:ComputerIPAddress
for /f "delims=[] tokens=2" %%a in ('ping %computername% -n 1 -4 ^| findstr "["') do (set thisip=%%a)

ECHO|set /p="%ComputerName% (%thisip%) @"

:: Define BS to contain a backspace
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "BS=%%A"

::If prompt is not at line beginning then need an extra char before %BS%
<nul set /p "=.%BS% "

REM Fancy Time Stamp
for /F "tokens=*" %%a in (
   'jTimestamp /F " {WKD} {MTH} {D}, {yyyy} {H12}:{NN}:{SS}{PM}"'
) do (
   set Timestamp=%%a
)

set "Timestamp=%Timestamp::00am=am%"
set "Timestamp=%Timestamp::00pm=pm%"
echo %Timestamp%

REM Time Script Ended
CALL :colorEcho e0 "AVG Install Script Ended"

:: Define BS to contain a backspace
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "BS=%%A"

::If prompt is not at line beginning then need an extra char before %BS%
<nul set /p "=.%BS% @"

REM Get Script End Time Using jTimestamp.cmd
CALL jTimestamp -f {ums} -r t2
   
REM Fancy Time Stamp
for /F "tokens=*" %%a in (
   'jTimestamp /F " {WKD} {MTH} {D}, {yyyy} {H12}:{NN}:{SS}{PM}"'
) do (
   
   set Timestamp=%%a   
)

:: Define BS to contain a backspace
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "BS=%%A"

::If prompt is not at line beginning then need an extra char before %BS%
<nul set /p "=.%BS% "

set "Timestamp=%Timestamp::00am=am%"
set "Timestamp=%Timestamp::00pm=pm%"
echo %Timestamp%

REM Show Elapsed Time Using jTimestamp.cmd
CALL :colorEcho e0 "AVG Install Script Ran For"
CALL jTimestamp -d %t2%-%t1% -f " {ud} Days {h} Hours {n} Minutes And {s} Seconds" -u
ECHO.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#8 Post by Squashman » 09 Feb 2017 14:15

You know there is an environmental variable called computername already. No need to run the hostname command.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#9 Post by Squashman » 09 Feb 2017 14:19

You seriously do not understand why your displayed time is not the same as your calculated time?

war59312
Posts: 10
Joined: 07 Feb 2017 20:17
Location: U.S.A
Contact:

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#10 Post by war59312 » 09 Feb 2017 14:23

Squashman wrote:You know there is an environmental variable called computername already. No need to run the hostname command.
I found that it did not always work for whatever reason and hostname has worked every time.

Mind you we have computers dated as far back as Windows 95 and up to Windows 10.

war59312
Posts: 10
Joined: 07 Feb 2017 20:17
Location: U.S.A
Contact:

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#11 Post by war59312 » 09 Feb 2017 14:28

Squashman wrote:You seriously do not understand why your displayed time is not the same as your calculated time?
Only thing I could think of is that it sometimes takes a little extra time to process the command and can roll over a bit to the next second. Was just hoping something could be down about it. No?

Or did I do something obviously wrong? Hint please?

I'm learning as I go. ;)

Thanks

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#12 Post by Squashman » 09 Feb 2017 14:39

war59312 wrote:Mind you we have computers dated as far back as Windows 95 and up to Windows 10.

Well I am going to scream foul there. Pretty sure a lot of your script will not work on Windows 95. Also, AVG is no longer supported on Win 9x as far as I can tell on their website.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#13 Post by Squashman » 09 Feb 2017 14:43

war59312 wrote:Only thing I could think of is that it sometimes takes a little extra time to process the command and can roll over a bit to the next second.

Well that would be the most logical explanation.

war59312
Posts: 10
Joined: 07 Feb 2017 20:17
Location: U.S.A
Contact:

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#14 Post by war59312 » 09 Feb 2017 15:07

Squashman wrote:
war59312 wrote:Mind you we have computers dated as far back as Windows 95 and up to Windows 10.

Well I am going to scream foul there. Pretty sure a lot of your script will not work on Windows 95. Also, AVG is no longer supported on Win 9x as far as I can tell on their website.
What I've posted is just part of the script(s).. I detect OS before all of this. Not just OS, but CPU type, is it x86 vs x64, etc.

The full script is a few thousand lines long. Figured no need to post the entire thing, besides I can't as its owned by the government since I wrote it all at work.. of course.. It's ok to share parts and seek help.

For older OS like Windows 95 the oldest supported version is installed. And script takes different routines based on what OS it detects.

These old computers are only being used to input data into a database. Think timesheets, clocking in and out.

Not up to me. If it were I'd get rid of them if possible, or disconnect from the Internet and only have access to the required spreadsheet. But it ant up to me. I'm just the lucky soul who gets to "fix it". ha!
Squashman wrote:
war59312 wrote:Only thing I could think of is that it sometimes takes a little extra time to process the command and can roll over a bit to the next second.

Well that would be the most logical explanation. As far as getting the output correctly I would assume something could be done.
Thanks. Any ideas on how to get that correct output? How to really know if it accidentally rolled over to the next second for example.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: jTimestamp.bat date/time utility - Replacement for getTimestamp.bat

#15 Post by Squashman » 09 Feb 2017 15:38

Use your variables!!!!!

Code: Select all

jTimestamp -d %t2% /F " {WKD} {MTH} {D}, {yyyy} {H12}:{NN}:{SS}{PM}"

Post Reply