Why are placeholders not replaced properly in my strings? - powershell

I have different errors when I am trying to format strings:
$dir = "c:\path"
$logfile = "$dir\logfile_{0}.txt" -f (get-date -format "yyyymmdd_hhmmtt")
Function Log($message) {
$message = "{0}: {1}" -f (get-date) $message
write-host $message
}
Log "Start processing {0}" -f ($_.FullName)
I expect $logfile to be c:\path\logfile_20160616_121012.txt but instead I get formats such as c:\path\logfile_20160116_1201nachm.
I expect Log to output 16.06.2016 12:01:20 Start Processing Myfile.xlsx but instead I get 16.06.2016 12:01:20 Start Processing 16.06.2016 12:01:20
What am I doing wrong?

The tt at the end of your Date formatting string yyyymmdd_hhmmtt, is the AM/PM designator.
Based on the output you receive, you're probably running on an OS with German locale (Nachmittag = PM).
If you want seconds, use ss instead. With no AM/PM designator, you should probably switch to using 24-hour time as well (replace hh with HH).
You can put the date format string inside the placeholder:
"$dir\logfile_{0:yyyymmdd_HHmmss}.txt" -f (Get-Date)
In the Log function, please be aware that the arguments to the -f operator needs to be comma separated.

You are trying to access a pipeline variable at your last line ($_.FullName) but you don't use any pipeline there. You also need to put the format there in parenthesis before you pass the string to the Log function.
You are missing a comma in the format parameters within your Log function:
$dir = "c:\path"
$logfile = "$dir\logfile_{0}.txt" -f (get-date -format "yyyymmdd_hhmmtt")
Function Log($message) {
$message = "{0}: {1}" -f (get-date), $message
write-host $message
}
Log ("Start processing {0}" -f $logfile)

Related

How to add date formats on PowerShell Custom Object prop value returns?

However on the console terminal it works just find and that's the output I want when I extract it to csv or txt file and I check type to see if it was string. Sorry maybe I'm just so new to PS that I am not quite familiar with the syntaxes and what's going on under the hood. anyways here is my Here is my hopeful desperate sad attempt
Sad Attempt
2nd attempt trying to find what I seek from several websites via google search
Sad attempt 2
This is the specific problem code sorry for the linking(Lazy)
# For testing purposes only i
$SpecifiedSelectObj = $Metadata | Select-Object -Property 'Date taken' | ForEach-Object {
($_."Date taken").GetType()
Get-Date $_."Date taken" -Format 'yyyy-MM-dd hh:mm tt'
<# Associated Error
| Cannot bind parameter 'Date'. Cannot convert value "‎10/‎12/‎2014 ‏‎5:31 pm" to type
"System.DateTime". Error: "String '‎10/‎12/‎2014 ‏‎5:31 pm' was not recognized as
| a valid DateTime."
#>
Thank you and much appreciated.
Solution - remove format characters using Unicode category or Unicode block: \p{}. Use
Get-Date ($_."Date taken" -replace "\p{Cf}", '') -Format 'yyyy-MM-dd hh:mm tt'
Explanation (with an auxiliary script):
Function Get-CodePoint {
param(
# named or positional or pipeline: a string to analyse
[Parameter(Position=0, Mandatory, ValueFromPipeline)]
$someString
)
([char[]]$someString |
ForEach-Object { "0x{0:x2}" -f [int]$_ }) -join ','
}
# Analysis
$dateStringCopyPaste = '‎10/‎12/‎2014 ‏‎5:31 pm'
Write-Verbose "Analysis: $(Get-CodePoint -someString $dateStringCopyPaste)" -Verbose
# $dateStringCopyPaste | Format-Hex -Encoding BigEndianUnicode -Verbose
# Solution:- remove format characters using Unicode category: \p{}
$dateRef = [datetime]::Now
$dateString = $dateStringCopyPaste -replace "\p{Cf}", ''
if ( [datetime]::TryParse( $dateString, [ref]$dateRef ) ) {
Get-Date $dateRef -Format 'yyyy-MM-dd hh:mm tt'
} else {
Write-Verbose "Solution: $(Get-CodePoint -someString $dateString)" -Verbose
}
Output in the analysis phase:
VERBOSE: Analysis:
0x200e,0x31,0x30,0x2f,0x200e,0x31,0x32,0x2f,0x200e,0x32,0x30,0x31,0x34,0x20,0x200f,0x200e,0x35,0x3a,0x33,0x31,0x20,0x70,0x6d
Those problematic characters 0x200e and 0x200f are
CodePoint Category Description
--------- -------- -----------
U+200F Cf-Format Right-To-Left Mark
U+200E Cf-Format Left-To-Right Mark
Output: .\SO\69156391.ps1
VERBOSE: Analysis:
0x200e,0x31,0x30,0x2f,0x200e,0x31,0x32,0x2f,0x200e,0x32,0x30,0x31,0x34,0x20,0x200f,0x200e,0x35,0x3a,0x33,0x31,0x20,0x70,0x6d
2014-12-10 05:31 PM

Culture based formatting of time variable

In this example it seems to me that the first two outputs should match, giving me formatting based on my defined culture. The last should be different because French formatting is different. Instead, the last two are the same, and are both getting some kind of default formatting. So, how do I do Culture based formatting when the time is a variable rather than formatting directly with Get-Date? It seems like it should be the same, but it's not.
get-date -format ((Get-Culture).DateTimeFormat.FullDateTimePattern)
$time = Get-Date
$pattern = 'FullDateTimePattern'
$formattedTime = $time -f (Get-Culture).DateTimeFormat.$pattern
Write-Host "$formattedTime"
$culture = New-Object system.globalization.cultureinfo('fr-FR')
$formattedTime = $time -f ($culture).DateTimeFormat.$pattern
Write-Host "$formattedTime"
The output I get is
July 9, 2019 11:22:01 AM
07/09/2019 11:22:01
07/09/2019 11:22:01
What I want to get is
July 9, 2019 11:26:46 AM
July 9, 2019 11:26:46 AM
Tuesday 9 July 2019 11:26:46
EDIT: So, based on I.T Delinquent's response, I tried this
$pattern = 'longDateTimePattern'
$date = Get-Date
$format = (Get-Culture).DateTimeFormat.$pattern
$string = ($date).ToString($format)
Write-Host $string
$culture = New-Object system.globalization.cultureinfo('de-DE')
$format = $culture.DateTimeFormat.$pattern
$string = ($date).ToString($format)
Write-Host $string
And it gave me identical results. Because it's not 'longDateTimePattern', its 'longDatePattern'. Given that the pattern could become a user supplied string, I better validate them.
Your attempt at using the -f operator is flawed (see bottom section).
To get the desired output, use the [datetime] type's appropriate .ToString() overload:
$time.ToString($culture.DateTimeFormat.$pattern, $culture)
Passing $culture as the 2nd argument ensures that the formatting is applied in the context of that culture.
If your intent is truly to use a format from another culture and apply it in the context of the current culture, simply omit the 2nd argument (as an alternative to the Get-Date -Format approach in your question):
$time.ToString($culture.DateTimeFormat.$pattern)
If there's no need to involve a different culture, the task becomes much simpler, by way of the standard date-time format strings, where single-character strings such as "D" refer to standard formats, such as LongDatePattern:
$time.ToString("D")
You can also pass these strings to Get-Date -Format
Get-Date -Format D
As for what you tried:
In order for the -f operator to work correctly, your LHS must be a string template with placeholders ({0} for the first one, {1} for the second, ...), to be replaced with the RHS operands.
Using a simple example:
Format the RHS, an [int], as a number with 2 decimal places.
PS> '{0:N2}' -f 1
1.00
Therefore, $time -f (Get-Culture).DateTimeFormat.$pattern doesn't perform (explicit) formatting at all, because the LHS - $time - contains no placeholders.
That is, the RHS is ignored, and the LHS is returned as a string: It is effectively the same as calling $time.ToString() in the context of the invariant culture (because the result of applying the -f operator is always a string and PowerShell uses the invariant culture in many string-related contexts).
While you can incorporate a specific date-time format string into a template-string placeholder - by following the placeholder index with : and a format string, as shown above ({0:N2}) - you cannot also provide a culture context for it.
You'd have to (temporarily) switch to the desired culture first:
# Save the currently effective culture and switch to the French culture
$prev = [cultureinfo]::CurrentCulture
[cultureinfo]::CurrentCulture = 'fr-FR'
# Format with the desired format string.
"{0:$($culture.DateTimeFormat.$pattern)}" -f $time
[cultureinfo]::CurrentCulture = $prev
I think this has something to do with how the Get-Date is passed using the variable, it seems to lose the format capability. In fact, if you try using Write-Host ($date -Format $format) gives an error:
Unexpected token '$format' in expression or statement
Here are my setup variables:
$pattern = 'FullDateTimePattern'
$format = (Get-Culture).DateTimeFormat.$pattern
$date = Get-Date
As stated above, using Write-Host ($date -f $format) and incorrectly outputs 07/09/2019 12:24:38. However, using any of the below options does work and correctly outputs 09 July 2019 12:24:38:
Write-Host (Get-Date -Format $format)
Write-Host (Get-Date).ToString($format)
Write-Host ($date).ToString($format)
Hope this helps :)

Change format of DateTimeReceived (Built-in feature from Microsoft)

I am working with email and its downloading and i want to set a condition to run a script with If {} only when email is received on the same day.
I do have this 2 lines of script:
$datetime = get-date -f yyyMMdd
$Sfha = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::DateTimeReceived, $datetime)
First declares today in the format of yyymmdd.
In order If condition to work, I also need to change format of DateTimeReceived.
You can pass date as a parameter to the get-date function, it can convert it to a specified format. You can try something like,
$datetime = get-date -f yyyyMMdd
$Sfha = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo((Get-Date ([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::DateTimeReceived) -Format yyyyMMdd), $datetime)

Output the date/time in PowerShell

I want to output the date time in various places in my script for logging so I am doing this:
$b = Get-Date
Write-Output "Backups complete at $b"
# more code here
$c = Get-Date
Write-Output "Backups complete at $c"
I am having to use multiple letters of the alphabet to get the most current date/time.
Is there an easier way of doing this or do I have to reestablish the date each time I want to use it again?
Once you assign the current datetime to a variable, you are capturing the date and time at the moment you ran Get-Date.
Every time you want a new date and time, you need to run it again. You could avoid using a variable:
Write-Output "Backups complete at $(Get-Date)"
Another way to do this is using a format string and since you are using this for logging purpose I would recommend you to write a function because this will allow you to change the format of all log messages in a single place:
function Log-Message
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string]$LogMessage
)
Write-Output ("{0} - {1}" -f (Get-Date), $LogMessage)
}
Now you can simple log using:
Log-Message "Starting Backups"
Log-Message "Backups Completed"
Output:
22.07.2016 08:31:15 - Starting Backups
22.07.2016 08:31:15 - Backups Completed
Here is simple 1 which allow you format date time in your desire format
$currentTime = Get-Date -format "dd-MMM-yyyy HH:mm:ss"
Write-Host $currentTime " Other log string message "
OUTPUT
17-Aug-2020 10:06:19 other log string message

PowerShell - formatting date to yyMMdd format

I run a PowerShell script that passes a parameter, such as "20160428".
I need to format this into yyMMdd (i.e., getting "160428" as the result). I could just strip out the "20", but I would like to get this right. For such I did so many attempts, such as:
#$COBDATE= '{0:yyMMdd}' -f $COBDATE
#$COBDATE = ([datetime]::ParseExact($COBDATE, "yyMMdd", [System.Globalization.CultureInfo]::InvariantCulture )).DayOfWeek
And the last one:
$COBDATE = ("{0:yyMMdd}" -f [datetime]::parse($COBDATE))
The below works, but once I replace "Get-Date" by my date "20160428" it just prints out the yyMMdd string.
$b = (Get-Date).AddDays(-1).ToString("yyMMdd")
So if I try this:
$input = "20160428"
$format = "yyMMdd"
$input_toDate_up = [DateTime]::ParseExact($input, $format, $null).ToString($format)
$input_toDate_up
It just says that the string is not a valid Date Time, which seems to be the root cause.
How can I fix this?
$Input = "20160428"
Get-Date -Year $Input.Substring(0,4) -Month $Input.Substring(4,2) -Day $Input.Substring(6,2) -Format "yyMMdd"
I think for one $input is a reserved variable so you shouldn't use it as nothing will really work in there as you expect. See here about_Automatic_Variables
I have used this drawn out substring process before with varied abstract date formats.
$dateinput = "20160428"
$dateformat = "yyMMdd"
Get-Date($dateinput.Substring(4,2) + "/" + $dateinput.Substring(2,2) + "/" + $dateinput.substring(0,4)) -Format $dateformat
I'm sure there is a shorter regex method, but that is not under my hat yet.