Jaa


Setting the Time Zone using Windows PowerShell

23 Nov 2009 Update - TimeZoneLib.ps1 in the download has been updated to allow proper redirection of the script output to a file.

A few year back a customer of mine was setting the time zone on their computers by directly modifying registry entries.  Since the supported way to do this was to use the SetTimeZoneInformation Win32 API, I created a utility in Visual Basic 6 to allow the customer to set the time zone on their Windows computers from the command line.

Later on, Windows XP included a built-in method to do this using control.exe with a command like:

Control.exe TIMEDATE.CPL,,/Z Pacific Standard Time

where the value after /Z is from the Std or Display values under the time zone subkey of the registry key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones.

Unfortunately, the control.exe method no longer works on Windows Vista.  When I tried my old VB6 utility on Vista I discovered it no longer worked either.  After asking about it on an internal discussion group, I was informed that my old utility did not work for several reasons.

First, a new privilege (SeTimeZonePrivilege) was created for Windows Vista so that standard users could now change the time zone.  The Date and Time control panel enables this privilege when you use it to change the time zone.  However, custom programs have to enable this privilege programmatically.  Using the AdjustTokenPrivileges Win32 API is one way to do this.  The second reason was that Windows Vista has a new API to set the time zone – SetDynamicTimeZoneInformation.  This API allows control over whether the system uses Dynamic Daylight Savings Time (daylight savings transition dates that change from year to year).  The old API still works but will set the Dynamic Daylight Savings Time setting incorrectly.  It is also recommended that the time zone name parameters for SetDynamicTimeZoneInformation are set from the MUI_Std and MUI_Dlt values from the time zone Registry subkey. To translate these values into the proper language specific string requires using either the RegLoadMUIString or SHLoadIndirectString API.

So I updated my VB6 utility and it now works fine on Windows Vista and higher.  I thought about sharing this version publically but then I decided it would be better if I could demonstrate doing this with a modern language, preferably a scripting language.  Since the Windows Script Host has no mechanism for using Windows API calls, I could not use that.  Luckily there is a newer Windows scripting language that is capable of doing this (albeit indirectly).  That language is Windows PowerShell.

PowerShell itself cannot call Win32 APIs directly.  However, it can use the power of the .NET Framework to do this.  PowerShell can use .NET objects directly using the new-object cmdlet as show here.  However, to call Win32 APIs you have to write some C# code and either create a cmdlet or compile it on the fly from the script.  Since I did not want to have to compiled assemblies as part of this solution I chose the latter.  In PowerShell v1 this can be done with functions like Compile-Csharp found here.  (Note that in the upcoming v2 release there is an Add-Type cmdlet for this.)  Using PINVOKE (Platform Invoke) to call Win32 APIs in C# can be tricky so I’ve provided a few references in case you want to try this for other Windows APIs:

Using P/Invoke to Call Unmanaged APIs from Your Managed Classes

Calling Win32 DLLs in C# with P/Invoke

PInvoke.net

The attached script files implement two commands.  Set-TimeZone is for setting the current time zone.  Get-TimeZone to get the current time zone information and to list the information for all time zones defined in the Registry.  All five files in the attached Zip need to be in the same folder.  Run Set-TimeZone and Get-TimeZone with the –help switch for usage.

If you want to create similar scripts here are a few tips from what I learn doing this.  First, PowerShell v1 does not like it when you reference the C# classes, structures, etc. in the same script that compiles the code.  I originally had a single library script that contained the here-string with the C# code, the step to compile it and the functions that used it.  This worked fine on v2 but v1 gave me errors that it could not find the C# classes, structures, etc referenced in the functions.  So to get this to work on v1 I had to put the here-string in a separate script (TimeZoneCSharp.ps1) from the functions that use it (TimeZoneLib.ps1).  Then in Get-TimeZone and Set-TimeZone I dot sourced TimeZoneCSharp.ps1 and compiled the C# first, then dot sourced TimeZoneLib.ps1.

Second, if you use the Compile-Csharp function found here you need to comment out the $cpar.OutputAssembly = "custom" line.  This line is not needed since we only want to compile the code in memory.  Also, this line causes the compiler to try to write out an assembly in the Windows\System32 folder.  This makes the script fail when run as as standard user.

Note that Windows 7 now includes a command line utility to set the time zone and get time zone information – tzutil.exe.

Disclaimer: The information on this site is provided "AS IS" with no warranties, confers no rights, and is not supported by the authors or Microsoft Corporation. Use of included script samples are subject to the terms specified in the Terms of Use .

This post was contributed by Michael Murgolo, a Senior Consultant with Microsoft Services - U.S. East Region.

PSTimeZoneScripts.zip

Comments

  • Anonymous
    January 01, 2003
    Introduction I strongly believe that in most application we are building, we would need to store date

  • Anonymous
    January 01, 2003
    A while back the topic of scripting the installation and removal of fonts came up in an internal discussion

  • Anonymous
    January 01, 2003
    The initialization or INI file format has been around a long time. In early versions of Windows (especially

  • Anonymous
    January 01, 2003
    The initialization or INI file format has been around a long time. In early versions of Windows (especially

  • Anonymous
    January 01, 2003
    The initialization or INI file format has been around a long time. In early versions of Windows (especially

  • Anonymous
    January 01, 2003
    A while back the topic of scripting the installation and removal of fonts came up in an internal discussion

  • Anonymous
    January 01, 2003
    Yes, Tim the Enchanter, I am aware of tzutil.exe for Windows 7 and I even mentioned it at the end of post. However, when this post was written (June 2009) Windows 7 hadn't shipped yet. This code was required for Windows Vista as there was no built-in utility to change the time zone on that OS.

  • Anonymous
    January 01, 2003
    Jason, $dstoff is false by default and set to true if the -dstoff switch is used with Set-TimeZone.ps1.      -dstoff     (Optional) On Windows Vista and higher this parameter disables dynamic     daylight savings time, causing the system to use a fixed set of transition     dates. Michael Murgolo

  • Anonymous
    January 01, 2003
    Jason, Yes I did.  However, I'm not sure I'd be able to help you track down what is going wrong with your code.  I'm an Infrastructure Consultant who writes a lot of scripts.  I don't usually work with compiled languages that often. Michael Murgolo

  • Anonymous
    January 01, 2003
    David, Out support folks usually do not appreciate us distibuting new code for a discontinued language.  Someone with VB6 experience using the Windows API should be able to port the .NET/PowerShell code I've provided to VB6 with little difficulty.  I figured it out and I'm not a developer.  :-) Michael Murgolo

  • Anonymous
    June 08, 2009
    Thanks for the article.  Best info I have found on how to set timezone automatically

  • Anonymous
    December 08, 2009
    Thanks for your article. Can I ask you to share your VB6 code anyway?

  • Anonymous
    January 07, 2010
    The comment has been removed

  • Anonymous
    January 07, 2010
    oh. of course...sorry, i didnt think to look there Thanks so much Michael.

  • Anonymous
    February 01, 2010
    Hi mike. Question for you. Did you test this code in Windows 7? I'm having some very strange problems when running this code in C#. I believed i ported your code over correctly. It acts like it runs successfully and does not throw any error, but GetLastWin32Error shows a variety of different errors, at different times.

  • Anonymous
    February 18, 2010
    Thanks DeploymentGuys, this post was very informative and helpful for me. Great post, thanks again. -Cleese

  • Anonymous
    August 31, 2010
    The comment has been removed

  • Anonymous
    January 14, 2011
    @Chris.. nice code, works great on both Server 2003 and 2008.

  • Anonymous
    April 26, 2013
    Nice blog to show how to do simple things in a complex way! :-)

  • Anonymous
    June 18, 2013
    The comment has been removed

  • Anonymous
    October 26, 2014
    This is overly complicated - especially since windows has a built-in utility to handle this. tzutil.exe. I could replace all this nonsense with just 'tzutil /s "Pacific Standard Time"'. Not as fancy as using powershell to compile a C# script to modify the registry, but I feel like it still has it's redeeming qualities.