Hey, Scripting Guy! Quick-Hits Friday: The Scripting Guys Respond to a Bunch of Questions (6/25/10)

Hey, Scripting Guy! Quick-Hits Friday: The Scripting Guys Respond to a Bunch of Questions (6/25/10)

  • Comments 5
  • Likes

 

Bookmark and Share

 

In this post:

 

What in the World Is Wrong with This Windows PowerShell Script I Wrote?

Hey, Scripting Guy! Question

Hey, Scripting Guy!  On trying to parse a datetime string in Windows PowerShell, I get a parse error, like the culture is set to en-US. But when I try it using the Get-Date method, it works. Please see the script output below. Now I can do this using the [datetime]::ParseExact method, but I do not understand why this is happening because the CurrentCulture is en-GB and the short date pattern clearly says dd/mm/yyyy. My question is why is the .NET Framework DateTime class using en-US culture or CurrentUICulture, and can I change this for my shell permanently?


39 >  [datetime] "21/05/2010"
NotSpecified: (:) [], RuntimeException

41 >  $error| fl * -force

PSMessageDetails      :
Exception             : System.Management.Automation.RuntimeException: Cannot convert value "21/05/2010" to type "System.DateTime". Error: "String was not recognized as a valid DateT
ime." ---> System.Management.Automation.PSInvalidCastException: Cannot convert value "21/05/2010" to type "System.DateTime". Error: "String was not recognized
as a valid DateTime." ---> System.FormatException: String was not recognized as a valid DateTime.
at System.DateTimeParse.Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
at System.DateTime.Parse(String s, IFormatProvider provider)
--- End of inner exception stack trace ---
at System.Management.Automation.LanguagePrimitives.IsParseTypeConversion(Object valueToConvert, Type resultType, IFormatProvider formatProvider, Object& re
sult)
at System.Management.Automation.LanguagePrimitives.ConvertTo(Object valueToConvert, Type resultType, Boolean recursion, IFormatProvider formatProvider, Typ
eTable backupTypeTable)
at System.Management.Automation.Parser.ConvertTo(Object obj, Type type, Token token)
--- End of inner exception stack trace ---
at System.Management.Automation.Parser.ConvertTo(Object obj, Type type, Token token)
at System.Management.Automation.UnaryPrefixPostFixNode.ExecuteConversion(Object operand, Token op)
at System.Management.Automation.UnaryPrefixPostFixNode.ExecuteUnary(Object operand, Token op, ExecutionContext context)
at System.Management.Automation.UnaryPrefixPostFixNode.Execute(Array input, Pipe outputPipe, ExecutionContext context)
at System.Management.Automation.AssignmentStatementNode.Execute(Array input, Pipe outputPipe, ExecutionContext context)
at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionC
ontext context)
TargetObject          :
CategoryInfo          : NotSpecified: (:) [], RuntimeException
FullyQualifiedErrorId : RuntimeException
ErrorDetails          :
InvocationInfo        : System.Management.Automation.InvocationInfo
PipelineIterationInfo : {}

38 >  get-date "21/05/2010"

21 May 2010 00:00:00

33 >  $host


Name             : Windows PowerShell ISE Host
Version          : 2.0
InstanceId       : 22476f38-943e-4255-a7a9-a2281730325a
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-GB
CurrentUICulture : en-US
PrivateData      :
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

35 >  (get-culture).get_datetimeformat().ShortDatePattern
dd/MM/yyyy

37 >  [System.Threading.Thread]::CurrentThread.CurrentCulture.datetimeformat.shortdatepattern
dd/MM/yyyy

-- JT

 

 

Hey, Scripting Guy! AnswerHello JT,

 

 

I am going to make a guess here. The first is that if you try your cast in the Windows PowerShell console (the blue one), it will work. If this is so, it is because of the difference between the Windows PowerShell console and the Windows PowerShell ISE. Note that here are your settings:
CurrentCulture: en-GB
CurrentUICulture: en-US


These variables contain similar information to what you can get via the Get-Culture cmdlet and the Get-UICulture. The reason there are two variables and two cmdlets is that Windows has two language settings. The first one—in Control Panel\Clock, Language, and Region on the Location tab—is the same as the Get-UICulture. Note that the tab description says that some software, including Windows, may provide you with additional information. The Windows PowerShell ISE uses Unicode and is therefore controlled by this setting.

On the Administrative tab, there is the Language for non-Unicode programs. The Windows PowerShell console is non-Unicode and therefore is controlled by this setting.

Please do three things for me:

  1. Try your cast inside the Windows PowerShell blue console, and see if it works -- I suspect it will.
  2. Check your Control Panel\Clock, Language, and Region settings and see if both of the ones (Location and Administrative) are set to the same thing.
  3. Let me know if I guessed correctly.

JT follows up:

Your first guess is correct and I tried it in the blue console, but I am getting the same behavior. Now you refer to two setting locations which I understand as being the Regional Options tab and the Languages tab in the Region and Language Options on the Control Panel. But both appear as shown in the following images. Maybe there is another location where Windows sets the language? My computer is part of a corporate network, and I am trying to understand Windows PowerShell .

Image of one setting

Image of additional settings

 

Hello again, JT.

Okay, I did some research, and have found out that this behavior is actually a special case and it is by design. If this sort of type conversion used the current locale by default, scripts written in one locale could break on another with the very same input. The problem occurs with data that is not represented in the appropriate data type yet.

In your example, you are using raw input to convert a string into a system.DateTime object. The System.DateTime structure casts it into a culture-independent format.

If you need to use a different date format, such as the UK uses of dd/mm/yyyy, you can use Parse(). By default, the parse method will use your current culture settings. Therefore, on a system that had UK English (en-GB), the following command would work:

[datetime]::Parse(“21/05/2010”)

You can also supply a cultureinfo object to parse in the second position. This is shown here:

$culture = Get-Culture
[datetime]::Parse(“21/05/2010”, $culture)

On your system with en-GB cultural settings, this command would work. On my system with en-US cultural settings, the command fails. However, if you use the static GetCulturalInfo method from the system.globalization.cultureinfo class to create the required cultural settings, it does not matter what type of settings your computer uses. This is shown here, where on my en-US computer I parse your command:

PS C:\> $ukCulture = [Globalization.cultureinfo]::GetCultureInfo("en-GB")
PS C:\> [datetime]::Parse("21/05/2010", $ukculture)

Friday, May 21, 2010 12:00:00 AM

 

By the way, you can avoid all this by using the Get-Date cmdlet to create your datetime strings. This is shown here:

PS C:\> Get-Date -Month 5 -Day 21 -Year 2010

Friday, May 21, 2010 9:39:15 AM

Or by using the Get-Date cmdlet and passing a string:

PS C:\> Get-Date "5/21/2010"


Friday, May 21, 2010 12:00:00 AM

The Get-Date cmdlet will use your culture settings automatically, in the same way as the parse method from the datetime class. 

 

 

How Can I Make a Script Run When a User Logs On?

Hey, Scripting Guy! Question

Hey, Scripting Guy! Is it possible to make a script that starts when a certain user logs on? This script then needs to start a certain program, wait a minute, start another program, wait a few minutes, and then log off. Just curious if it is possible and how to do it or at least find information about how to do it in this wilderness of help sites.

-- MK

 

 

Hey, Scripting Guy! AnswerHello MK,

Yes, it is possible. Using Group Policy, you can specify a startup script on a per-user basis. You can also simply add the script to the user’s startup folder or the run key in their registry.

Then you need to decide if you want to write the script using VBScript or Windows PowerShell. There are examples of starting programs in the Script Repository as well as the Hey, Scripting Guy! Blog. As a matter of fact, one of the events in the 2010 Scripting Games involved logging off a user when a specific program launched. The expert commentators wrote solutions in both VBScript and Windows PowerShell .

 

Well, this concludes another edition of Quick-Hits Friday. Join us tomorrow for the Weekend Scripter as we delve into the mysteries of…well, we will let that remain a mystery for now.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys


 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • As for the date conversion: Reasons why I always use ISO 8601 date format in strings representing dates in my scripts. Those are parsed by PowerShell even if the locale tells a different story.

  • "Okay, I did some research, and have found out that this behavior is actually a special case and it is by design. If this sort of type conversion used the current locale by default, scripts written in one locale could break on another with the very same input. "

    That's fair enough, but why would/should the "by design" use en-US as the culture. If it needs to be locale independant, should it use ISO 8601 instead?

  • I've got a script where I want to receive the report start date as a param. Since we are UK based the user is probably going to enter the date as DD/MM/YYYY how do I get [datetime]::ParseExact to work on data being received as a parameter? I've tried the below but its not valid syntax:

    Param(

       [parameter(Mandatory=$false, HelpMessage="Date to start report from in DD/MM/YYYY format. Defaults to previous day if not provided")]

       [alias("from")]

       [datetime]::ParseExact)

  • Oh man, thanks for this tip:

    Or by using the Get-Date cmdlet and passing a string:

    PS C:\> Get-Date "5/21/2010"

    I was KILLING myself reading through forums and trying to use [DateTime]::parse to convert 1-9-2013 to 01/09/2013 for use in a 'where-object' filter. This solved the problem so clean!

    I never would have thought of passing a date string to the 'Get-Date' method. Just seems strange somehow...

  • @Chris - try it this way

    PS>([datetime]'1-9-2013').ToString('MM/dd/yyyy')

    01/09/2013

    PS>([datetime]'1-9-2013').ToShortDateString()

    1/9/2013