Convert date format from DD.MM.YYYY to YYYY-MM-DD - string

I have folders on the disk in the german format: "01.05.2019", which I want to convert into the english format: "2019-05-01".
I am using PowerShell. Is there a fancy function for doing this? Or should I get all substrings and reorder them?
Currently I only collect the strings:
$OrgDir = "P:\Fotos\Import"
$folders = Get-ChildItem $($OrgDir) -Directory
foreach ($dir in $folders) {
Write-Host "folder: " $dir
}

Use [DateTime]::ParseExact() to avoid the date parser mixing up the month and day:
$OrgDir = "P:\Fotos\Import"
$folders = Get-ChildItem $OrgDir -Directory
foreach ($dir in $folders) {
Write-Host "folder: $([DateTime]::ParseExact($dir.Name, "dd.MM.yyyy", $null).ToString("yyyy-MM-dd"))"
}
The above prints out the converted names. To efficiently rename the files however, I recommend this:
Get-ChildItem $OrgDir -Directory |
ForEach-Object {
$_ | Rename-Item -NewName (
[DateTime]::ParseExact($_.Name, "dd.MM.yyyy", $null).ToString("yyyy-MM-dd")
)
}
This line of PowerShell renames all directories at $OrgDir to the new date format, given that all directories in the folder are named this way.
Reference
UPDATE:
As #Matt Johnson pointed out, $null uses your system default culture for ParseExact(string, format, culture) (as well as ToString(format, culture)). This may or may not cause problems based on what culture setting your system currently has.
To ensure these settings do not interfere with this function, use [System.Globalization.CultureInfo]::InvariantCulture for the culture parameters in both ParseExact() and ToString().

Matt Johnson provides important pointers in his comments:
To write robust code that works independently of what culture is in effect, specify the culture context explicitly, both when:
parsing strings as [datetime] instances
formatting [datetime] instances as strings
Therefore, use the following in your case:
PS> [datetime]::Parse('01.05.2019', [cultureinfo] 'de-DE').
ToString('yyyy-MM-dd', [cultureinfo]::InvariantCulture)
2019-05-01
Since 01.05.2019 is a valid (day-first) short date in German, no custom parsing is needed, only de-DE (German / Germany) as the cultural context.
.ToString('yyyy-MM-dd', [cultureinfo]::InvariantCulture) specifies an explicit output-format string; [cultureinfo]::InvariantCulture - the invariant culture (based on US-English) - as the cultural context ensures that the date is interpreted based on the Gregorian calendar.
Note that in PowerShell casts (e.g., [datetime] '01.05.2019') and string interpolation (e.g., "$(get-date)") always use the invariant culture - whereas calling [datetime]::Parse() and .ToString() without an explicit culture (format-provider) argument uses the current culture.

Finally this did the trick for me:
PS> [datetime]::Parse('01.05.2019', [cultureinfo] 'de-DE').
ToString('yyyy-MM-dd', [cultureinfo]::InvariantCulture)
All other solutions ended in:
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "15.12.2019" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
Thanks a lot mklement0.

Related

String interpolation in PowerShell for hashmaps [duplicate]

I have the following code:
$DatabaseSettings = #();
$NewDatabaseSetting = "" | select DatabaseName, DataFile, LogFile, LiveBackupPath;
$NewDatabaseSetting.DatabaseName = "LiveEmployees_PD";
$NewDatabaseSetting.DataFile = "LiveEmployees_PD_Data";
$NewDatabaseSetting.LogFile = "LiveEmployees_PD_Log";
$NewDatabaseSetting.LiveBackupPath = '\\LiveServer\LiveEmployeesBackups';
$DatabaseSettings += $NewDatabaseSetting;
When I try to use one of the properties in a string execute command:
& "$SQlBackupExePath\SQLBackupC.exe" -I $InstanceName -SQL `
"RESTORE DATABASE $DatabaseSettings[0].DatabaseName FROM DISK = '$tempPath\$LatestFullBackupFile' WITH NORECOVERY, REPLACE, MOVE '$DataFileName' TO '$DataFilegroupFolder\$DataFileName.mdf', MOVE '$LogFileName' TO '$LogFilegroupFolder\$LogFileName.ldf'"
It tries to just use the value of $DatabaseSettings rather than the value of $DatabaseSettings[0].DatabaseName, which is not valid.
My workaround is to have it copied into a new variable.
How can I access the object's property directly in a double-quoted string?
When you enclose a variable name in a double-quoted string it will be replaced by that variable's value:
$foo = 2
"$foo"
becomes
"2"
If you don't want that you have to use single quotes:
$foo = 2
'$foo'
However, if you want to access properties, or use indexes on variables in a double-quoted string, you have to enclose that subexpression in $():
$foo = 1,2,3
"$foo[1]" # yields "1 2 3[1]"
"$($foo[1])" # yields "2"
$bar = "abc"
"$bar.Length" # yields "abc.Length"
"$($bar.Length)" # yields "3"
PowerShell only expands variables in those cases, nothing more. To force evaluation of more complex expressions, including indexes, properties or even complete calculations, you have to enclose those in the subexpression operator $( ) which causes the expression inside to be evaluated and embedded in the string.
#Joey has the correct answer, but just to add a bit more as to why you need to force the evaluation with $():
Your example code contains an ambiguity that points to why the makers of PowerShell may have chosen to limit expansion to mere variable references and not support access to properties as well (as an aside: string expansion is done by calling the ToString() method on the object, which can explain some "odd" results).
Your example contained at the very end of the command line:
...\$LogFileName.ldf
If properties of objects were expanded by default, the above would resolve to
...\
since the object referenced by $LogFileName would not have a property called ldf, $null (or an empty string) would be substituted for the variable.
Documentation note: Get-Help about_Quoting_Rules covers string interpolation, but, as of PSv5, not in-depth.
To complement Joey's helpful answer with a pragmatic summary of PowerShell's string expansion (string interpolation in double-quoted strings ("...", a.k.a. expandable strings), including in double-quoted here-strings):
Only references such as $foo, $global:foo (or $script:foo, ...) and $env:PATH (environment variables) can directly be embedded in a "..." string - that is, only the variable reference itself, as a whole is expanded, irrespective of what follows.
E.g., "$HOME.foo" expands to something like C:\Users\jdoe.foo, because the .foo part was interpreted literally - not as a property access.
To disambiguate a variable name from subsequent characters in the string, enclose it in { and }; e.g., ${foo}.
This is especially important if the variable name is followed by a :, as PowerShell would otherwise consider everything between the $ and the : a scope specifier, typically causing the interpolation to fail; e.g., "$HOME: where the heart is." breaks, but "${HOME}: where the heart is." works as intended.
(Alternatively, `-escape the :: "$HOME`: where the heart is.", but that only works if the character following the variable name wouldn't then accidentally form an escape sequence with a preceding `, such as `b - see the conceptual about_Special_Characters help topic).
To treat a $ or a " as a literal, prefix it with escape char. ` (a backtick); e.g.:
"`$HOME's value: $HOME"
For anything else, including using array subscripts and accessing an object variable's properties, you must enclose the expression in $(...), the subexpression operator (e.g., "PS version: $($PSVersionTable.PSVersion)" or "1st el.: $($someArray[0])")
Using $(...) even allows you to embed the output from entire commands in double-quoted strings (e.g., "Today is $((Get-Date).ToString('d')).").
Interpolation results don't necessarily look the same as the default output format (what you'd see if you printed the variable / subexpression directly to the console, for instance, which involves the default formatter; see Get-Help about_format.ps1xml):
Collections, including arrays, are converted to strings by placing a single space between the string representations of the elements (by default; a different separator can be specified by setting preference variable $OFS, though that is rarely seen in practice) E.g., "array: $(#(1, 2, 3))" yields array: 1 2 3
Instances of any other type (including elements of collections that aren't themselves collections) are stringified by either calling the IFormattable.ToString() method with the invariant culture, if the instance's type supports the IFormattable interface[1], or by calling .psobject.ToString(), which in most cases simply invokes the underlying .NET type's .ToString() method[2], which may or may not give a meaningful representation: unless a (non-primitive) type has specifically overridden the .ToString() method, all you'll get is the full type name (e.g., "hashtable: $(#{ key = 'value' })" yields hashtable: System.Collections.Hashtable).
To get the same output as in the console, use a subexpression in which you pipe to Out-String and apply .Trim() to remove any leading and trailing empty lines, if desired; e.g.,
"hashtable:`n$((#{ key = 'value' } | Out-String).Trim())" yields:
hashtable:
Name Value
---- -----
key value
[1] This perhaps surprising behavior means that, for types that support culture-sensitive representations, $obj.ToString() yields a current-culture-appropriate representation, whereas "$obj" (string interpolation) always results in a culture-invariant representation - see this answer.
[2] Notable overrides:
• The previously discussed stringification of collections (space-separated list of elements rather than something like System.Object[]).
• The hashtable-like representation of [pscustomobject] instances (explained here) rather than the empty string.
#Joey has a good answer. There is another way with a more .NET look with a String.Format equivalent, I prefer it when accessing properties on objects:
Things about a car:
$properties = #{ 'color'='red'; 'type'='sedan'; 'package'='fully loaded'; }
Create an object:
$car = New-Object -typename psobject -Property $properties
Interpolate a string:
"The {0} car is a nice {1} that is {2}" -f $car.color, $car.type, $car.package
Outputs:
# The red car is a nice sedan that is fully loaded
If you want to use properties within quotes follow as below. You have to use $ outside of the bracket to print property.
$($variable.property)
Example:
$uninstall= Get-WmiObject -ClassName Win32_Product |
Where-Object {$_.Name -like "Google Chrome"
Output:
IdentifyingNumber : {57CF5E58-9311-303D-9241-8CB73E340963}
Name : Google Chrome
Vendor : Google LLC
Version : 95.0.4638.54
Caption : Google Chrome
If you want only name property then do as below:
"$($uninstall.name) Found and triggered uninstall"
Output:
Google Chrome Found and triggered uninstall

Join three CSV columns and convert them to a formatted email link with Powershell [duplicate]

I have the following code:
$DatabaseSettings = #();
$NewDatabaseSetting = "" | select DatabaseName, DataFile, LogFile, LiveBackupPath;
$NewDatabaseSetting.DatabaseName = "LiveEmployees_PD";
$NewDatabaseSetting.DataFile = "LiveEmployees_PD_Data";
$NewDatabaseSetting.LogFile = "LiveEmployees_PD_Log";
$NewDatabaseSetting.LiveBackupPath = '\\LiveServer\LiveEmployeesBackups';
$DatabaseSettings += $NewDatabaseSetting;
When I try to use one of the properties in a string execute command:
& "$SQlBackupExePath\SQLBackupC.exe" -I $InstanceName -SQL `
"RESTORE DATABASE $DatabaseSettings[0].DatabaseName FROM DISK = '$tempPath\$LatestFullBackupFile' WITH NORECOVERY, REPLACE, MOVE '$DataFileName' TO '$DataFilegroupFolder\$DataFileName.mdf', MOVE '$LogFileName' TO '$LogFilegroupFolder\$LogFileName.ldf'"
It tries to just use the value of $DatabaseSettings rather than the value of $DatabaseSettings[0].DatabaseName, which is not valid.
My workaround is to have it copied into a new variable.
How can I access the object's property directly in a double-quoted string?
When you enclose a variable name in a double-quoted string it will be replaced by that variable's value:
$foo = 2
"$foo"
becomes
"2"
If you don't want that you have to use single quotes:
$foo = 2
'$foo'
However, if you want to access properties, or use indexes on variables in a double-quoted string, you have to enclose that subexpression in $():
$foo = 1,2,3
"$foo[1]" # yields "1 2 3[1]"
"$($foo[1])" # yields "2"
$bar = "abc"
"$bar.Length" # yields "abc.Length"
"$($bar.Length)" # yields "3"
PowerShell only expands variables in those cases, nothing more. To force evaluation of more complex expressions, including indexes, properties or even complete calculations, you have to enclose those in the subexpression operator $( ) which causes the expression inside to be evaluated and embedded in the string.
#Joey has the correct answer, but just to add a bit more as to why you need to force the evaluation with $():
Your example code contains an ambiguity that points to why the makers of PowerShell may have chosen to limit expansion to mere variable references and not support access to properties as well (as an aside: string expansion is done by calling the ToString() method on the object, which can explain some "odd" results).
Your example contained at the very end of the command line:
...\$LogFileName.ldf
If properties of objects were expanded by default, the above would resolve to
...\
since the object referenced by $LogFileName would not have a property called ldf, $null (or an empty string) would be substituted for the variable.
Documentation note: Get-Help about_Quoting_Rules covers string interpolation, but, as of PSv5, not in-depth.
To complement Joey's helpful answer with a pragmatic summary of PowerShell's string expansion (string interpolation in double-quoted strings ("...", a.k.a. expandable strings), including in double-quoted here-strings):
Only references such as $foo, $global:foo (or $script:foo, ...) and $env:PATH (environment variables) can directly be embedded in a "..." string - that is, only the variable reference itself, as a whole is expanded, irrespective of what follows.
E.g., "$HOME.foo" expands to something like C:\Users\jdoe.foo, because the .foo part was interpreted literally - not as a property access.
To disambiguate a variable name from subsequent characters in the string, enclose it in { and }; e.g., ${foo}.
This is especially important if the variable name is followed by a :, as PowerShell would otherwise consider everything between the $ and the : a scope specifier, typically causing the interpolation to fail; e.g., "$HOME: where the heart is." breaks, but "${HOME}: where the heart is." works as intended.
(Alternatively, `-escape the :: "$HOME`: where the heart is.", but that only works if the character following the variable name wouldn't then accidentally form an escape sequence with a preceding `, such as `b - see the conceptual about_Special_Characters help topic).
To treat a $ or a " as a literal, prefix it with escape char. ` (a backtick); e.g.:
"`$HOME's value: $HOME"
For anything else, including using array subscripts and accessing an object variable's properties, you must enclose the expression in $(...), the subexpression operator (e.g., "PS version: $($PSVersionTable.PSVersion)" or "1st el.: $($someArray[0])")
Using $(...) even allows you to embed the output from entire commands in double-quoted strings (e.g., "Today is $((Get-Date).ToString('d')).").
Interpolation results don't necessarily look the same as the default output format (what you'd see if you printed the variable / subexpression directly to the console, for instance, which involves the default formatter; see Get-Help about_format.ps1xml):
Collections, including arrays, are converted to strings by placing a single space between the string representations of the elements (by default; a different separator can be specified by setting preference variable $OFS, though that is rarely seen in practice) E.g., "array: $(#(1, 2, 3))" yields array: 1 2 3
Instances of any other type (including elements of collections that aren't themselves collections) are stringified by either calling the IFormattable.ToString() method with the invariant culture, if the instance's type supports the IFormattable interface[1], or by calling .psobject.ToString(), which in most cases simply invokes the underlying .NET type's .ToString() method[2], which may or may not give a meaningful representation: unless a (non-primitive) type has specifically overridden the .ToString() method, all you'll get is the full type name (e.g., "hashtable: $(#{ key = 'value' })" yields hashtable: System.Collections.Hashtable).
To get the same output as in the console, use a subexpression in which you pipe to Out-String and apply .Trim() to remove any leading and trailing empty lines, if desired; e.g.,
"hashtable:`n$((#{ key = 'value' } | Out-String).Trim())" yields:
hashtable:
Name Value
---- -----
key value
[1] This perhaps surprising behavior means that, for types that support culture-sensitive representations, $obj.ToString() yields a current-culture-appropriate representation, whereas "$obj" (string interpolation) always results in a culture-invariant representation - see this answer.
[2] Notable overrides:
• The previously discussed stringification of collections (space-separated list of elements rather than something like System.Object[]).
• The hashtable-like representation of [pscustomobject] instances (explained here) rather than the empty string.
#Joey has a good answer. There is another way with a more .NET look with a String.Format equivalent, I prefer it when accessing properties on objects:
Things about a car:
$properties = #{ 'color'='red'; 'type'='sedan'; 'package'='fully loaded'; }
Create an object:
$car = New-Object -typename psobject -Property $properties
Interpolate a string:
"The {0} car is a nice {1} that is {2}" -f $car.color, $car.type, $car.package
Outputs:
# The red car is a nice sedan that is fully loaded
If you want to use properties within quotes follow as below. You have to use $ outside of the bracket to print property.
$($variable.property)
Example:
$uninstall= Get-WmiObject -ClassName Win32_Product |
Where-Object {$_.Name -like "Google Chrome"
Output:
IdentifyingNumber : {57CF5E58-9311-303D-9241-8CB73E340963}
Name : Google Chrome
Vendor : Google LLC
Version : 95.0.4638.54
Caption : Google Chrome
If you want only name property then do as below:
"$($uninstall.name) Found and triggered uninstall"
Output:
Google Chrome Found and triggered uninstall

Compare two Excel-files in Powershell

I need help comparing two Excel files in Powershell.
I have an Excel-file which contains 6 000 rows and 4-5 columns with headers:
"Number" "Name" "Mobile data".
Let's call it: $Services
Now, I want to compare that file with other Excel-files. For example:
one file containing 50 rows with header columns: "Number", "Name", etc.
Let's call it $Department
The important thing is that in $Services, it contains more important columns like "Mobile data",
so my mission is to compare column: "Number" from $Services with column "Number" from each other Excel file.
Then if they match, write "the whole row" from $Services
I'm not that familiar with Excel, so I thought, this should be possible to do in Powershell.
I'm novice in Powershell, so I only know basic stuff. I'm not that familiar with pscustomobject and param.
Anyway, what I tried to do was to first declare them in variables with ImportExcel:
$Services = Import-Excel -Path 'C:\Users\*.xlsx'
$Department = Import-Excel -Path 'C:\Users\*.xlsx'
Then I made a foreach statement:
foreach ($Service in $Services) {
if (($Service).Number -like ($Department).Number)
{Write-Output "$Service"}
}
The problem with this is that it is collecting all empty columns from ($Services).Number and writing the output of each row in $Services.
I tried to add a nullorEmpty to $Department, if the .Number is empty, but it didn't make any difference. I also tried to add that if the row is empty in .Number, add "1234", but still it collects all .Number that is empty in $Services.
I also tried to do a: $Services | ForEach-Object -Process {if (($_).Number -match ($Department).Number)
{Write-Output $_}} But it didn't match any. When I tried -notmatch it took all.
I don't know but it seems that I have to convert the files to objects, like the columns to object so each string becomes an object. But right now my head is just spinning and I need some hints on where I can start with this.
I would recommend downloading the Module ImportExcel from the PSGallery.
Import-Excel can easily import your Excel sheet(s) to rows of objects, especially if your sheets are 'clean', i.e., only contain (optional) headers and data rows.
Simply import the cells to PowerShell objects and use Compare-Object to discover differences.
EDIT (after reading the additional questions by poster in the comments):
To compare using specific properties you'll need to add these to the Compare-Object parameters.
Using a trivial "PSCustomObject" to create a simple set of objects to show this idea it might look like this:
$l = 1..4 | ForEach-Object { [pscustomobject]#{a=$_;b=$_+1} }
$r = 1,2,4,5 | ForEach-Object { [pscustomobject]#{a=$_;b=$_+1} }
compare-object $l $r -Property B
B SideIndicator
- -------------
6 =>
4 <=
You may also compare multiple properties this way:
compare-object $l $r -Property A,B
A B SideIndicator
- - -------------
5 6 =>
3 4 <=
FYI: I find myself typing "Get-Command -Syntax SomeCommand" so often every day that I just made a function "Get-Syntax" (which also expands aliases) and then aliased this to simply "syn".
90% of the time once you understand the structure of PowerShell cmdlets (at least well-written ones) there is no need to even look at the full help -- the "syntax" blocks are sufficient.
Until then, type HELP (Get-Help) a lot -- 100+ times per day. :)
So the solution for my whole problem was to add -PassThru.
Because my mission was to compare the numbers of the two Excel-files, select the numbers that equals and then take all the properties from one file. So my script became like this:
$Compare = Compare-Object $Services $Department -Property Numbers -IncludeEqual -ExcludeDifferent -PassThru
$Compare | Export-Excel -Path 'C:\Users\*
But I wonder, -PassThru sends all the objects from ReferenceObject, how can I send all the objects from DifferenceObject?

Split string in PowerShell by pattern

I have a fairly long string in PowerShell that I need to split. Each section begins with a date in format mm/dd/yyyy hh:mm:ss AM. Essentially what I am trying to do is get the most recent message in the string. I don't need to keep the date/time part as I already have that elsewhere.
This is what the string looks like:
10/20/2018 1:22:33 AM
Some message the first one in the string
It can be several lines long
With multiple line breaks
But this is still the first message in the string
10/21/2018 4:55:11 PM
This would be second message
Same type of stuff
But its a different message
I know how to split a string on specific characters, but I don't know how on a pattern like date/time.
Note:
The solution below assumes that the section are not necessarily chronologically ordered so that you must inspect all time stamps to determine the most recent one.
If, by contrast, you can assume that the last message is the most recent one, use LotPings' much simpler answer.
If you don't know ahead of time what section has the most recent time stamp, a line-by-line approach is probably best:
$dtMostRecent = [datetime] 0
# Split the long input string ($longString) into lines and iterate over them.
# If input comes from a file, replace
# $longString -split '\r?\n'
# with
# Get-Content file.txt
# If the file is large, replace the whole command with
# Get-Content file.txt | ForEach-Object { ... }
# and replace $line with $_ in the script block (loop body).
foreach ($line in $longString -split '\r?\n') {
# See if the line at hand contains (only) a date.
if ($dt = try { [datetime] $line } catch {}) {
# See if the date at hand is the most recent so far.
$isMostRecent = $dt -ge $dtMostRecent
if ($isMostRecent) {
# Save this time stamp as the most recent one and initialize the
# array to collect the following lines in (the message).
$dtMostRecent = $dt
$msgMostRecentLines = #()
}
} elseif ($isMostRecent) {
# Collect the lines of the message associated with the most recent date.
$msgMostRecentLines += $line
}
}
# Convert the message lines back into a single, multi-line string.
# $msgMostRecent now contains the multi-line message associated with
# the most recent time stamp.
$msgMostRecent = $msgMostRecentLines -join "`n"
Note how try { [datetime] $line } catch {} is used to try to convert a line to a [datetime] instance and fail silently, if it can't, in which case $dt is assigned $null, which in a Boolean context is interpreted as $False.
This technique works irrespective of the culture currently in effect, because PowerShell's casts always use the invariant culture when casting from strings, and the dates in the input are in one of the formats the invariant culture understands.
By contrast, the -as operator, whose use would be more convenient here - $dt =$line -as [datetime] - unexpectedly is culture-sensitive, as Esperento57 points out.
This surprising behavior is discussed in this GitHub issue.
Provided the [datetime] sections are ascending,
it should be sufficient to split on them with a RegEx and get the last one
((Get-Content .\test.txt -Raw) -split "\d+/\d+/\d{4} \d+:\d+:\d+ [AP]M`r?`n")[-1]
Output based on your sample string stored in file test.txt
This would be second message
Same type of stuff
But its a different message
you can split it by timestamp pattern like this:
$arr = $str -split "[0-9]{1,2}/[0-9]{1,2}/[0-9]{1,4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2} [AaPp]M\n"
To my knowledge you can't use any of the static String methods like Split() for this. I tried to find a regular expression that would handle the entire thing, but wasn't able to come up with anything that would quite break it up properly.
So, you'll need to go line by line, testing to see if it that line is a date, then concatenate the lines in between like the following:
$fileContent = Get-Content "inputFile.txt"
$messages = #()
$currentMessage = [string]::Empty
foreach($line in $fileContent)
{
if ([Regex]::IsMatch($line, "\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2}:\d{2} (A|P)M"))
{
# The current line is a date, the current message is complete
# Add the current message to the output, and clear out the old message
# from your temporary storage variable $currentMessage
if (-not [string]::IsNullOrEmpty($currentMessage))
{
$messages += $currentMessage
$currentMessage = [string]::Empty
}
}
else
{
# Add this line to the message you're building.
# Include a new line character, as it was stripped out with Get-Content
$currentMessage += "$line`n"
}
}
# Add the last message to the output
$messages += $currentMessage
# Do something with the message
Write-Output $messages
As the key to all of this is recognizing that a given line is a date and therefore the start of a message, let's look a bit more at the regex. "\d" will match any decimal character 0-9, and the curly braces immediately following indicate the number of decimal characters that need to match. So, "\d{1,2}" means "look for one or two decimal characters" or in this case the month of the year. We then look for a "/", 1 or 2 more decimal characters - "\d{1,2}", another "/" and then exactly 4 decimal characters - "\d{4}". The time is more of the same, with ":" in between the decimal characters instead of "/". At the end, there will either be "AM" or "PM" so we look for either an "A" or a "P" followed by an "M", which as a regular expression is "(A|P)M".
Combine all of that, and you get "\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2}:\d{2} (A|P)M" to determine if you have a date on that line. I believe it would also be possible to use[DateTime]::Parse() to determine if the line is a date, but then you wouldn't get to have fun with Regex's and would need a try-catch. For more info on Regex's in Powershell (which are just the .NET regex) see .NET Regex Quick Reference

Create a new PowerShell string containing defined substrings

I've found myriad methods to retrieve data FROM a string with substrings, but what I want to do is create a new string that contains substrings. The reason for this is that I want to pass that string to a CSV through the Export-CSV cmdlet. This is in a PowerShell Forms created app.
So the plan would be to
1). Read the contents of each text box:
(e.g. $endusername.text $endusernumber.text $locationname.text)
2). Store those into a new string with substrings
($formoutput.endusername $formoutput.endusernumber $formoutput.locationname)
3). Output the string to a .CSV
Export-CSV -InputObject $formoutput "c:\output\formoutput.csv"
Basically, if I take any existing cmdlet (say, Get-Mailbox), store its output as a string, and then pass that string through the Export-CSV in the way explained above, it performs exactly the way I like - creating a .CSV with each of the substrings as a column, and the contents of that substring in the appropriately headed column. I just want to be able to do that with a string containing substrings that I define.
I think you are confusing nomenclature a little bit. It sounds like what you want is a custom object not a string. Here is some pseudo-code to get you going in the right direction:
$formOutput = New-Object PsCustomObject -Property #{'EndUserName' = $endUserName.Text;
'EndUserNumber' = $endUserNumber.Text;
'LocationName' = $locatioName.Text}
$formOutput | Export-CSV .\FileName.csv -NoTypeHeader

Resources