[How-To] Parse DateTime strings (PowerShell hybrid)

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
aGerman
Expert
Posts: 4080
Joined: 22 Jan 2010 18:01
Location: Germany

[How-To] Parse DateTime strings (PowerShell hybrid)

#1 Post by aGerman » 21 Feb 2021 07:02

Preface: Aacini provided the excelent 3rd party tools StdTime.exe and StdDate.exe which have similar functionality. If they meet your requirements and if performance matters, you will likely prefer using them:
viewtopic.php?f=3&t=3428

The code below contains the ParseDateTime macro which wraps .NET functions in a little PowerShell code. The date and/or time strings to be converted are redirected to the standard input. The current date and time is assumed for an empty string or in case nothing is redirected. The output of the macro is a space-separated string with 10 tokens.
You are able to customize the code by adding specific format patterns.
For more information refer to the description in the code.

Note: Even if all examples in the code below are successfully converted for me, don't assume they can be converted for you as well. They contain localized German strings which are expected to fail on machines where German isn't the default culture. Replace them with the localized strings for your default language settings.

Code: Select all

@echo off &setlocal

:: The ParseDateTime macro reads a string from StdIn and tries to parse it as
::  DateTime value.
:: If an empty string is redirected or if the macro is executed without a
::  redirected string, the current date and time will be assumed.
:: If the macro succeeds to parse the string, it outputs 10 space-separated
::  values with fixed lengths:
:: yyyy MM dd HH mm ss fff n iii ww
::   yyyy  year
::   MM    month
::   dd    day
::   HH    hours (00 .. 24)
::   mm    minutes
::   ss    seconds
::   fff   fraction (milliseconds)
::   n     day of week (0 for Sunday .. 6 for Saturday)
::   iii   day of year (001 .. 365/366)
::   ww    ISO 8601 week of year
:: Date values which are not specified in the input string are set to 1.
:: Time values are set to 0, respectively.
:: 
:: DMTF (CIM_DATETIME), ISO 8601, and RFC1123 formatted datetime strings are
::  supported. Long and short date and time strings are converted as long as
::  they meet the invariant or the local format.
:: Extra spaces in the converted string are automatically ignored.
:: For more information about standard formats refer to:
:: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings
:: 
:: The macro can be customized by specifying additional format strings. Right
::  now the list of custom formats in $cfo contains only one string for input
::  from the asctime C function (as used in ROBOCOPY). However, more format
::  strings can be appended, separated by a comma each. E.g.:
::  $cfo=[string[]]('ddd MMM d HH:mm:ss yyyy','HH:mm \h');^
:: For a list of custom format specifiers and syntax refer to:
:: https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
set ParseDateTime=powershell -nop -ep Bypass -c ^"$s=[string]$input;if(-Not $s){$dt=[DateTime]::Now}else{^
$cfo=[string[]]('ddd MMM d HH:mm:ss yyyy');^
$ic=[Globalization.CultureInfo]::InvariantCulture;^
try{$dt=[DateTime]::ParseExact($s,$cfo,$ic,15)}catch{^
try{$dt=[DateTime]::ParseExact($s,$cfo,$null,15)}catch{^
try{$dt=[Management.ManagementDateTimeConverter]::ToDateTime($s)}catch{^
$s=$s -replace '(\d),(\d)','$1.$2';^
try{$dt=[DateTime]::Parse($s,$ic,143)}catch{^
try{$dt=[DateTime]::Parse($s,$null,143)}catch{^
exit}}}}}}$dow=[int]$dt.DayOfWeek;($dt.toString('yyyy MM dd HH mm ss fff ')+$dow+$dt.DayOfYear.toString(' 000')+^
$(Get-Culture).Calendar.GetWeekOfYear($(if($dow -match '[1-3]'){$dt.AddDays(3)}else{$dt}),2,1).toString(' 00'))^"

:: EXAMPLES:

echo *** no redirected string, leading zeros removed using SET /A ***
for /f "tokens=1-10" %%a in ('%ParseDateTime%') do set /a "year=1%%a-10000,mon=1%%b-100,day=1%%c-100,hh=1%%d-100,mm=1%%e-100,ss=1%%f-100,ms=1%%g-1000,dow=%%h,doy=1%%i-1000,woy=1%%j-100"
echo year         %year%
echo month        %mon%
echo day          %day%
echo hours        %hh%
echo minutes      %mm%
echo seconds      %ss%
echo milliseconds %ms%
echo day of week  %dow%
echo day of year  %doy%
echo week of year %woy%

echo(
echo *** several tests, beginning with an empty string ***
for %%S in (
  ""                                         %= an empty string results in the output of the current date and time =%
  "19720126204745.800000+060"                %= DMTF formatted (as received from WMI) =%
  "26.01.1972 20:47:45,80"                   %= date and time variables, localized German output (comma as decimal separator) =%
  " 26. 01. 1972  20: 47: 45,80 "            %= date and time, localized German, extra spaces =%
  "26/01/1972 20:47:45.80"                   %= different date and decimal separators =%
  "26.01.1972"                               %= date only, localized German output =%
  "26.01.72"                                 %= date only, localized German, year with 2 digits =%
  "1972-01-26"                               %= different order and separator, ISO 8601 =%
  "Januar 1972"                              %= German month + year =%
  "January 1972"                             %= English month + year =%
  "20:47:45,80"                              %= time only, localized German =%
  "08:47:45.80 PM"                           %= time only, English =%
  "20:47:45"                                 %= time without fraction =%
  "20:47"                                    %= time without seconds =%
  "1972-01-26T20:47:45.8Z"                   %= RFC3339, ISO 8601 UTC notation =%
  "1972-01-26T20:47:45.8+1:00"               %= RFC3339, ISO 8601 with offset =%
  "Wed Jan 26 20:47:45 1972"                 %= C asctime()-like (as received from ROBOCOPY) =%
  "Mi Jan 26 20:47:45 1972"                  %= same but localized German =%
  "Wed 1972-Jan-26 20:47:45"                 %= as received from MAKECAB =%
  "Wed, 26 Jan 1972 20:47:45 GMT"            %= RFC1123 =%
  "Mittwoch, 26. Januar 1972 20:47:45"       %= long localized German =%
  "January, 26 1972 20:47:45"                %= long English =%
  "Wednesday, January 26, 1972 8:47:45.8 PM" %= another long English =%
) do (
  REM Note that the surrounding quotes are removed for the redirection.
  echo(%%~S
  for /f "tokens=1-10" %%a in ('echo(%%~S^|%ParseDateTime%') do echo -^> y:%%a M:%%b d:%%c H:%%d m:%%e s:%%f ms:%%g DoW:%%h DoY:%%i WoY:%%j
  echo(
)

pause


Output on my machine:

Code: Select all

*** no redirected string, leading zeros removed using SET /A ***
year         2021
month        2
day          22
hours        22
minutes      19
seconds      16
milliseconds 486
day of week  1
day of year  53
week of year 8

*** several tests, beginning with an empty string ***

-> y:2021 M:02 d:22 H:22 m:19 s:16 ms:830 DoW:1 DoY:053 WoY:08

19720126204745.800000+060
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

26.01.1972 20:47:45,80
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

 26. 01. 1972  20: 47: 45,80
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

26/01/1972 20:47:45.80
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

26.01.1972
-> y:1972 M:01 d:26 H:00 m:00 s:00 ms:000 DoW:3 DoY:026 WoY:04

26.01.72
-> y:1972 M:01 d:26 H:00 m:00 s:00 ms:000 DoW:3 DoY:026 WoY:04

1972-01-26
-> y:1972 M:01 d:26 H:00 m:00 s:00 ms:000 DoW:3 DoY:026 WoY:04

Januar 1972
-> y:1972 M:01 d:01 H:00 m:00 s:00 ms:000 DoW:6 DoY:001 WoY:52

January 1972
-> y:1972 M:01 d:01 H:00 m:00 s:00 ms:000 DoW:6 DoY:001 WoY:52

20:47:45,80
-> y:0001 M:01 d:01 H:20 m:47 s:45 ms:800 DoW:1 DoY:001 WoY:01

08:47:45.80 PM
-> y:0001 M:01 d:01 H:20 m:47 s:45 ms:800 DoW:1 DoY:001 WoY:01

20:47:45
-> y:0001 M:01 d:01 H:20 m:47 s:45 ms:000 DoW:1 DoY:001 WoY:01

20:47
-> y:0001 M:01 d:01 H:20 m:47 s:00 ms:000 DoW:1 DoY:001 WoY:01

1972-01-26T20:47:45.8Z
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

1972-01-26T20:47:45.8+1:00
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

Wed Jan 26 20:47:45 1972
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:000 DoW:3 DoY:026 WoY:04

Mi Jan 26 20:47:45 1972
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:000 DoW:3 DoY:026 WoY:04

Wed 1972-Jan-26 20:47:45
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:000 DoW:3 DoY:026 WoY:04

Wed, 26 Jan 1972 20:47:45 GMT
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:000 DoW:3 DoY:026 WoY:04

Mittwoch, 26. Januar 1972 20:47:45
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:000 DoW:3 DoY:026 WoY:04

January, 26 1972 20:47:45
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:000 DoW:3 DoY:026 WoY:04

Wednesday, January 26, 1972 8:47:45.8 PM
-> y:1972 M:01 d:26 H:20 m:47 s:45 ms:800 DoW:3 DoY:026 WoY:04

Drücken Sie eine beliebige Taste . . .
Some information to the magic numbers in the code for those who are wondering ...
The 15 passed to the 4th parameter of ParseExact and the 143 passed to the 3rd parameter of Parse are DateTimeStyles AllowWhiteSpaces + NoCurrentDateDefault (+ RoundtripKind).
The 2 passed to the 2nd parameter of GetWeekOfYear is the CalendarWeekRule FirstFourDayWeek, the 1 passed to the 3rd parameter is the DayOfWeek Monday.


Steffen
Last edited by aGerman on 25 Feb 2021 12:06, edited 3 times in total.
Reason: macro code shortened, explanation for the magic numbers added

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

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#2 Post by Squashman » 21 Feb 2021 18:33

I will definitely use this. Day of year (52) would be a nice feature as well.

aGerman
Expert
Posts: 4080
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#3 Post by aGerman » 22 Feb 2021 06:01

Good call! Updated :wink:

Steffen

misol101
Posts: 470
Joined: 02 May 2016 18:20

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#4 Post by misol101 » 22 Feb 2021 06:38

Nice, can you add week number as well? :wink: Don't know about other countries but in Sweden we are obsessed with referring to the current week number (in the workplace etc)

aGerman
Expert
Posts: 4080
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#5 Post by aGerman » 22 Feb 2021 08:31

Even if the MS methods aim to be ISO 8601 compliant, they in fact don't. This would need some further investigation. If I have to roll own code to calculate it, I'm not sure if the macro length will explode :lol: I'll try to include it but don't want to promise yet :wink:

Steffen

aGerman
Expert
Posts: 4080
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#6 Post by aGerman » 22 Feb 2021 15:22

ISO 8601 calendar week is the tenth field now.

Steffen

misol101
Posts: 470
Joined: 02 May 2016 18:20

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#7 Post by misol101 » 23 Feb 2021 02:58

Sweet 8)

aGerman
Expert
Posts: 4080
Joined: 22 Jan 2010 18:01
Location: Germany

Re: [How-To] Parse DateTime strings (PowerShell hybrid)

#8 Post by aGerman » 25 Feb 2021 12:13

I shortened the macro length. Because some field names of enumerations are replaced with their integer values, I added a paragraph about their meaning.

Steffen

Post Reply