Issue using scientific notation as a parameter for Get-Date - powershell

Per this Code-Golf tip, in PowerShell you can use scientific notation to easily generate numbers which are powers of 10: https://codegolf.stackexchange.com/a/193/6776
i.e. 1e7 produces the number 10,000,000.
If I pass this value to get-date (or alias date, for the purposes of code golf) I get a single second: i.e. date 10000000 => 01 January 0001 00:00:01.
Yet if I use the scientific notation, even with brackets (i.e. date (1e7)) I get an error:
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "10000000" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
At line:1 char:6
+ date (1e7)
+ ~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Date], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCommand
Question
Is there a way to use scientific notation with the Get-Date's default (date) parameter?

This is because 1e7 gets outputed as a double, so you just have to cast it to an integer:
date ([int]1e7)
You can check that if you call the GetType method on the output:
(1e7).GetType() | Format-Table -AutoSize
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Double System.ValueType
Edit:
Shortest script probably is:
1e7l|date
This is taken from PetSerAls comment - just removed another character by using pipe instead of brackets.

Related

How to convert a string value to type "System.Int32" in PowerShell

I was trying to get the count of days between two dates, but getting the below error
Any suggestions would be greatly appreciated
$Days = $expiration - $currentDate
Cannot convert value "12/28/2020" to type "System.Int32". Error: "Input string was not in a correct format."
At line:1 char:1
+ $Days = $expiration - $currentDate
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
below is a snippet of code for reference
$expiration = $QueryResults.Results.ExpiryDate_s
$expiration
12/28/2020
$currentDate = Get-Date -Format 'MM/dd/yyyy'
$currentDate
09/30/2020
$expiration.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
$currentDate.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PowerShell directly supports subtracting [datetime] (System.DateTime) instances, the result of which is reported as a span of time, expressed as a [timespan] (System.TimeSpan) instance.
For this to work, both operands passed to the - (subtraction) operator must be of type [datetime], which in your case you can simply be achieved by casting[1] your string operands to that type (instead of calling Get-Date -Format 'MM/dd/yyyy' you should just call (Get-Date).Date in order to return a [datetime] instance directly):
PS> [datetime] '12/28/2020' - [datetime] '09/30/2020'
Days : 89
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 0
Ticks : 76896000000000
TotalDays : 89
TotalHours : 2136
TotalMinutes : 128160
TotalSeconds : 7689600
TotalMilliseconds : 7689600000
To get just the count of days between the two dates, using the current date (without a time-of-day component) as the RHS:
$days = ([datetime] '12/28/2020' - (Get-Date).Date).Days
As for what you tried:
If you pass a string-typed LHS to the - operator, PowerShell tries to interpret it as a number.
Since a string such as '12/28/2020' cannot be parsed as a numeric type, the operation fails. To provide a more obvious example:
PS> 'I am not a number' - 1
Cannot convert value "I am not a number" to type "System.Int32"
...
[int] (System.Int32) just happens to be the one numeric type chosen for the error message.
[1] Note that PowerShell casts are always based on the invariant culture, which is based on the US English culture, irrespective of what culture is currently in effect. This means that a [datetime] cast accepts whatever string [datetime]::Parse($string, $null) accepts, which includes month-first date strings such as '1/13/2020' as well as less ambiguous formats such as '2020-1-13'
You are trying to do math against datetime data type values which is not correct.
I belive you could use New-TimeSpan cmdlet to achieve your needs. Like so:
PS C:\WINDOWS\system32> New-TimeSpan -Start '01.01.2020' -End $(Get-Date)
Days : 274
Hours : 3
Minutes : 38
Seconds : 17
Milliseconds : 295
Ticks : 236866972950824
TotalDays : 274,151589063454
TotalHours : 6579,63813752289
TotalMinutes : 394778,288251373
TotalSeconds : 23686697,2950824
TotalMilliseconds : 23686697295,0824
You could also invoke datetime data type variable's methods such as AddDays(). Like so:
PS C:\WINDOWS\system32> $CurrentDate = Get-Date
PS C:\WINDOWS\system32> $CurrentDate.AddDays(-274)
1 января 2020 г. 3:40:59

Convert string to DateTime in Powershell from CSV

I'm having the weirdest and most annoying problem with dealing with a string here that I need to convert to DateTime.
I'm doing the exact same thing from 2 different CSV files - it works perfectly on the first one, keeps returning an error on the second one.
$userDateOut = Get-Date $sourceLine.Date_OUT -Format "dd/MM/yyyy"
$userDateOut = ($userDateOut -as [datetime]).AddDays(+1)
$userDateOut = Get-Date $userDateOut -Format "dd/MM/yyyy"
In the first CSV, Date_OUT is just 31/12/2021 for example, and in the second one it's 31/12/2021 0:00:00.
So before the 3 lines to create $userDateOut, I do
$userDateOut = $sourceLine.Date_OUT.SubString(0,10)
Which makes me end up with the same type of variable as with the first CSV
PS C:\Windows\system32> $userDateOut = $sourceLine.Date_Out.Substring(0,10)
PS C:\Windows\system32> $userDateOut
31/12/2021
PS C:\Windows\system32> $userDateOut.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
However, with this variable, I'm getting
PS C:\Windows\system32> $userDateOut = Get-Date $userDateOut -Format "dd/MM/yyyy"
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "31/12/2021" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
At line:1 char:25
+ $userDateOut = Get-Date $userDateOut -Format "dd/MM/yyyy"
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Date], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCommand
And I don't know why... Can someone help ?
-Format just converts [datetime] to a [string] - it doesn't influence parsing of input strings in any way.
For that, you need [datetime]::ParseExact():
$dateString = '31/12/2021'
# You can pass multiple accepted formats to ParseExact, this should cover both CSV files
$inputFormats = #(
'dd/MM/yyyy H:mm:ss'
'dd/MM/yyyy'
)
$parsedDatetime = [datetime]::ParseExact($dateString, $inputFormat, $null)
You can then use Get-Date -Format to convert it back to an intended output format if needed:

What is the difference between char[] and char?

If I use the [char] datatype, it requires the value to be one character, e.x.
[char]"a"
a
Since if I use it with more than one character it will give me an error:
[char]"ab"
Cannot convert value "ab" to type "System.Char". Error: "String must be exactly one
character long."
At line:1 char:1
+ [char]"ab"
+ ~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastParseTargetInvocation
However if I use
[char[]]"ab"
I get the output
a
b
If I compare Get-Member on both, I get no result:
PS C:\Users\nijoh> Compare-Object -ReferenceObject $([char] | gm) -DifferenceObject $([char[]] | gm) -PassThru
PS C:\Users\nijoh>
But I can see that they are two distinct types since they show up differently:
PS C:\Users\nijoh> ([char[]]"a").GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Char[] System.Array
PS C:\Users\nijoh> ([char]"a").GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Char System.ValueType
So what is the difference between the [char] and [char[]] datatypes in Powershell?
In PowerShell, a type name enclosed in [...] is a type literal, i.e. an object that represents the .NET type named.
Independently of that, inside [...], [] immediately after a type name represents an array containing elements of that type; for an overview of the notation used inside [...], see this answer.
Therefore:
[char] refers to type System.Char (the System. prefix is generally optional in PowerShell; additionally - as is the case here - PowerShell has a fixed set of type accelerators that allow you to use simple names for types located in any namespace - see this answer).
To get a given type literal's full (namespace-qualified) .NET type name, use [...].FullName; e.g., [char].FullName yields System.Char
[char[]] refers to an array type whose elements are of type char ( System.Char); [char[]].FullName yields System.Char[].
Since if I use it with more than one character it will give me an error ([char]"ab")
PowerShell has no [char] literals, only string ([string]) literals. When you cast a string to [char] - which represents a single character - only a single-character string is accepted; e.g. [char] 'a' works, but [char] 'ab' or [char] "ab" doesn't.
However if I use [char[]]"ab" ...
Casting a string to an array of characters returns a character array comprised of the string's individual characters.
In other words: [char[]] "ab" is equivalent to "ab".ToCharArray().
If I compare Get-Member on both, I get no result:
The reason is that Get-Member operates on the types of the input objects, and if the input objects are type literals - such as [char] and [char[]] - their type is examined, which is System.RuntimeType, a non-public PowerShell type that derives from System.Reflection.TypeInfo, which describes a .NET type.
In other words: all type literals piped to Get-Member will result in the same output, irrespective of what specific type they refer to, because it is the one and only type-describing type whose members are being reported.
Therefore, using Compare-Object on Get-Member calls with different type literals predictably produces no output, because the Get-Member calls result in the same output[1]. (Not producing output is Compare-Object's way of indicating that no differences were detected.)
[1] Get-Member outputs an array of Microsoft.PowerShell.Commands.MemberDefinition instances, one for each member of the input object's type. In the absence of a -Property argument passed to Compare-Object, these instances are compared as a whole, by their .ToString() value, which yields a meaningful representation of each member.

converting pom.xml version to number format

I have a requirement to increment to read and increment the pom.xml version by 1 using powershell script.
I was able to fetch the version value as for example: 1.0.123, but the type given here is string, when I try to convert it into Decimal or Double I am getting below error:
Code:
PS C:\Users\XXXX\Downloads> $finale
1.0.153
PS C:\Users\XXXX\Downloads> $finale.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Error:
PS C:\Users\XXXX\Downloads> $finale1 = [Double]::Parse($finale)
Exception calling "Parse" with "1" argument(s): "Input string was not in a correct format."
At line:1 char:1
+ $finale1 = [Double]::Parse($finale)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : FormatException
The reason is 1.0.123 is not math. It is nether an integer, nor a double. It is simply a string that contains numbers and symbols. This is why you are getting the error.
See the following Help files:
About_Arithmetic_Operators
.NET Math Class
Using a [version] type is nice, but it is immutable. This code splits it into an array, increments the third (Build) number, and produces a string in $newfinale.
Note that this does not check to see if there is a third (Build) value. It will produce an exception if the $finale is '1.2'.
PS C:\> $finale = '2.3.4.5'
PS C:\> $a = $finale.split('.')
PS C:\> $a[2] = [int]$a[2] + 1
PS C:\> $newfinale = $a -join '.'
PS C:\> $newfinale
2.3.5.5

How do I access values in an ordered PowerShell hash table using integer keys?

My requirement is to store integer keys and access hash table values using those integer keys in an ordered hash table.
What works
When I use string keys, no problem:
cls
$foo=[ordered]#{}
$foo.add("12",1)
$foo.add("24",2)
write-host ("first item=" + $foo.Item("12"))
write-host ("second item=" + $foo.Item("24"))
Output:
first item=1
second item=2
Using Brackets Fails
When I use brackets, the program doesn't throw an exception, but it returns nothing:
$fooInt=[ordered]#{}
$fooInt.add(12,1)
$fooInt.add(24,2)
write-host ("first item=" + $fooInt[12])
write-host ("second item=" + $fooInt[24])
Output:
first item=
second item=
Using the Item method Fails
When I use the Item method and integer keys, PowerShell interprets the integer key as an index and not a key:
$fooInt=[ordered]#{}
$fooInt.add(12,1)
$fooInt.add(24,2)
write-host ("first item=" + $fooInt.Item(12))
write-host ("second item=" + $fooInt.Item(24))
Output:
Exception getting "Item": "Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index"
At line:8 char:1
+ write-host ("first item=" + $fooInt.Item(12))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], GetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenGetting
Exception getting "Item": "Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index"
At line:9 char:1
+ write-host ("second item=" + $fooInt.Item(24))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], GetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenGetting
How do I access values in a PowerShell hashtable using an integer key?
They keys in a hashtable are objects, not strings. When you're attempting to access the key "12" with the integer 12, it cannot find that entry because the keys don't match.
HOWEVER, you're not using a standard hashtable, you're using an ordered hashtable which has a different Item method on it since it can work by key or index. If you want to access the integer key with an ordered hashtable, you need to use a different syntax:
$hash.12
If you use the array accessor syntax:
$hash[12]
it will try to return the 13th item in the list.
You can observe the difference between these objects by using Get-Member:
$orderedHash | Get-Member Item
TypeName: System.Collections.Specialized.OrderedDictionary
Name MemberType Definition
---- ---------- ----------
Item ParameterizedProperty System.Object Item(int index) {get;set;}, System.Object Item(System.Object key) {get;set;}
$hash | Get-Member Item
TypeName: System.Collections.Hashtable
Name MemberType Definition
---- ---------- ----------
Item ParameterizedProperty System.Object Item(System.Object key) {get;set;}
After some more experimentation, this is only the case on the int32 type. If you define and access it with a different type, it will work since it's no longer matching the overloaded int signature:
$hash = [ordered]#{
([uint32]12) = 24
}
$hash[[uint32]12]
> 24
Summary
$fooInt.Item([object]12) or $fooInt[[object]12]
Reasoning
As seen in TheIncorrigible1's answer, .Item has overloads; it is backed by the method get_Item:
PS C:\> $fooInt.get_Item
OverloadDefinitions
-------------------
System.Object get_Item(int index)
System.Object get_Item(System.Object key)
System.Object IOrderedDictionary.get_Item(int index)
System.Object IDictionary.get_Item(System.Object key)
The version which takes an integer and does indexing comes from the IOrderedDictionary interface, the normal IDictionary key lookup takes a [System.Object]. When you try to use it with an integer argument, PowerShell binds the version which takes an [int] because it's a better match, and runs that one.
Earlier, I made a comment of how you could use reflection to pick out the overload you want, and invoke that, but it's ugly:
$fooInt.GetType().GetMethods().where{
$_.Name -eq 'get_Item' -and $_.GetParameters().Name -eq 'Key'
}.Invoke($fooInt, 'Public', $null, 12, $null)
^ your parameter
Thinking on it, [int] is a value type, not a reference type, and that means .Net has to box it into an object to put it in a Hashtable. So maybe if you also box your integer parameter into an object when doing a lookup, PowerShell might bind to the overload you want and do a key lookup and still match the correct key .. what do you know, it works:
PS C:\> $fooInt=[ordered]#{}
PS C:\> $fooInt.add(12,1)
PS C:\> $fooInt.add(24,2)
PS C:\> write-host ("first item=" + $fooInt.Item([object]12))
first item=1
And it works for indexing, too:
PS C:\> write-host ("first item=" + $fooInt[[object]12])
first item=1
Which is very close to TheIncorrigible1's experiment, except you don't need to define the dictionary with the key typed as something else and then cast your lookups to a matching type, you only need to access it with casting to object, because that's happening internally already for the keys you define.