C# 从 .NET 设置系统时区

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/808736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 02:04:32  来源:igfitidea点击:

Set System Time Zone from .NET

c#.netwinapiinteroptimezone

提问by user19297

Does anyone have some code that will take a TimeZoneInfo field from .NET and execute the interop code to set the system time zone via SetTimeZoneInformation? I realize that it's basically mapping the TimeZoneInfo members to the struct members, but it does not appear obvious to me how the fields will map exactly or what I should care about beyond the bias.

有没有人有一些代码可以从 .NET 获取 TimeZoneInfo 字段并执行互操作代码以通过 SetTimeZoneInformation 设置系统时区?我意识到它基本上是将 TimeZoneInfo 成员映射到结构成员,但对我来说,字段将如何精确映射或除了偏差之外我应该关心什么似乎并不明显。

采纳答案by BigBlackDog

I don't know if this is what you are looking for, but there is some information on how to use the Win32 functions to get and set the timezone information at

我不知道这是否是您要找的,但是有一些关于如何使用 Win32 函数来获取和设置时区信息的信息

http://www.pinvoke.net/default.aspx/kernel32/GetTimeZoneInformation.html

http://www.pinvoke.net/default.aspx/kernel32/GetTimeZoneInformation.html

The main part of the following sample code is taken from this site. I just added a few more lines to actually call the GetTimeZone() method and fixed a small error in the access modifier of the SystemTime struct to make the sample work.

以下示例代码的主要部分取自该站点。我只是添加了更多行来实际调用 GetTimeZone() 方法,并修复了 SystemTime 结构的访问修饰符中的一个小错误,以使示例工作。

Hope this helps...

希望这可以帮助...

using System;
using System.Runtime.InteropServices;

namespace Stackoverflow
{
    class TimeZoneFunctionality
    {
        /// <summary>
        /// [Win32 API call]
        /// The GetTimeZoneInformation function retrieves the current time-zone parameters.
        /// These parameters control the translations between Coordinated Universal Time (UTC)
        /// and local time.
        /// </summary>
        /// <param name="lpTimeZoneInformation">[out] Pointer to a TIME_ZONE_INFORMATION structure to receive the current time-zone parameters.</param>
        /// <returns>
        /// If the function succeeds, the return value is one of the following values.
        /// <list type="table">
        /// <listheader>
        /// <term>Return code/value</term>
        /// <description>Description</description>
        /// </listheader>
        /// <item>
        /// <term>TIME_ZONE_ID_UNKNOWN == 0</term>
        /// <description>
        /// The system cannot determine the current time zone. This error is also returned if you call the SetTimeZoneInformation function and supply the bias values but no transition dates.
        /// This value is returned if daylight saving time is not used in the current time zone, because there are no transition dates.
        /// </description>
        /// </item>
        /// <item>
        /// <term>TIME_ZONE_ID_STANDARD == 1</term>
        /// <description>
        /// The system is operating in the range covered by the StandardDate member of the TIME_ZONE_INFORMATION structure.
        /// </description>
        /// </item>
        /// <item>
        /// <term>TIME_ZONE_ID_DAYLIGHT == 2</term>
        /// <description>
        /// The system is operating in the range covered by the DaylightDate member of the TIME_ZONE_INFORMATION structure.
        /// </description>
        /// </item>
        /// </list>
        /// If the function fails, the return value is TIME_ZONE_ID_INVALID. To get extended error information, call GetLastError.
        /// </returns>
        [DllImport( "kernel32.dll", CharSet = CharSet.Auto )]
        private static extern int GetTimeZoneInformation( out TimeZoneInformation lpTimeZoneInformation );

        /// <summary>
        /// [Win32 API call]
        /// The SetTimeZoneInformation function sets the current time-zone parameters.
        /// These parameters control translations from Coordinated Universal Time (UTC)
        /// to local time.
        /// </summary>
        /// <param name="lpTimeZoneInformation">[in] Pointer to a TIME_ZONE_INFORMATION structure that contains the time-zone parameters to set.</param>
        /// <returns>
        /// If the function succeeds, the return value is nonzero.
        /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
        /// </returns>
        [DllImport( "kernel32.dll", CharSet = CharSet.Auto )]
        private static extern bool SetTimeZoneInformation( [In] ref TimeZoneInformation lpTimeZoneInformation );

        /// <summary>
        /// The SystemTime structure represents a date and time using individual members
        /// for the month, day, year, weekday, hour, minute, second, and millisecond.
        /// </summary>
        [StructLayoutAttribute( LayoutKind.Sequential )]
        public struct SystemTime
        {
            public short year;
            public short month;
            public short dayOfWeek;
            public short day;
            public short hour;
            public short minute;
            public short second;
            public short milliseconds;
        }

        /// <summary>
        /// The TimeZoneInformation structure specifies information specific to the time zone.
        /// </summary>
        [StructLayout( LayoutKind.Sequential, CharSet = CharSet.Unicode )]
        public struct TimeZoneInformation
        {
            /// <summary>
            /// Current bias for local time translation on this computer, in minutes. The bias is the difference, in minutes, between Coordinated Universal Time (UTC) and local time. All translations between UTC and local time are based on the following formula:
            /// <para>UTC = local time + bias</para>
            /// <para>This member is required.</para>
            /// </summary>
            public int bias;
            /// <summary>
            /// Pointer to a null-terminated string associated with standard time. For example, "EST" could indicate Eastern Standard Time. The string will be returned unchanged by the GetTimeZoneInformation function. This string can be empty.
            /// </summary>
            [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 32 )]
            public string standardName;
            /// <summary>
            /// A SystemTime structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the wMonth member in the SystemTime structure must be zero. If this date is specified, the DaylightDate value in the TimeZoneInformation structure must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied.
            /// <para>To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurence of the day of the week within the month (first through fifth).</para>
            /// <para>Using this notation, specify the 2:00a.m. on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, wDay = 1. Specify 2:00a.m. on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, wDay = 5.</para>
            /// </summary>
            public SystemTime standardDate;
            /// <summary>
            /// Bias value to be used during local time translations that occur during standard time. This member is ignored if a value for the StandardDate member is not supplied.
            /// <para>This value is added to the value of the Bias member to form the bias used during standard time. In most time zones, the value of this member is zero.</para>
            /// </summary>
            public int standardBias;
            /// <summary>
            /// Pointer to a null-terminated string associated with daylight saving time. For example, "PDT" could indicate Pacific Daylight Time. The string will be returned unchanged by the GetTimeZoneInformation function. This string can be empty.
            /// </summary>
            [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 32 )]
            public string daylightName;
            /// <summary>
            /// A SystemTime structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the wMonth member in the SystemTime structure must be zero. If this date is specified, the StandardDate value in the TimeZoneInformation structure must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied.
            /// <para>To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurence of the day of the week within the month (first through fifth).</para>
            /// </summary>
            public SystemTime daylightDate;
            /// <summary>
            /// Bias value to be used during local time translations that occur during daylight saving time. This member is ignored if a value for the DaylightDate member is not supplied.
            /// <para>This value is added to the value of the Bias member to form the bias used during daylight saving time. In most time zones, the value of this member is –60.</para>
            /// </summary>
            public int daylightBias;
        }

        /// <summary>
        /// Sets new time-zone information for the local system.
        /// </summary>
        /// <param name="tzi">Struct containing the time-zone parameters to set.</param>
        public static void SetTimeZone( TimeZoneInformation tzi )
        {
            // set local system timezone
            SetTimeZoneInformation( ref tzi );
        }

        /// <summary>
        /// Gets current timezone information for the local system.
        /// </summary>
        /// <returns>Struct containing the current time-zone parameters.</returns>
        public static TimeZoneInformation GetTimeZone()
        {
            // create struct instance
            TimeZoneInformation tzi;

            // retrieve timezone info
            int currentTimeZone = GetTimeZoneInformation( out tzi );

            return tzi;
        }

        /// <summary>
        /// Oversimplyfied method to test  the GetTimeZone functionality
        /// </summary>
        /// <param name="args">the usual stuff</param>
        static void Main( string[] args )
        {
            TimeZoneInformation timeZoneInformation = GetTimeZone();

            return;
        }
    }
}

Before setting the time zone information you will need to ensure that the process has the appropriate privileges. As noted on the MSDN page for SetTimeZoneInformation:

在设置时区信息之前,您需要确保该进程具有适当的权限。如 SetTimeZoneInformation 的 MSDN 页面所述:

An application must have the SE_TIME_ZONE_NAME privilege for this function to succeed. This privilege is disabled by default.

应用程序必须具有 SE_TIME_ZONE_NAME 权限才能成功执行此功能。默认情况下禁用此权限。

Here is some sample code to enable this privilege:

以下是启用此权限的一些示例代码:

public class AdjustTokenPrivilegesFunctionality
{
  [StructLayout(LayoutKind.Sequential)]
  private struct LUID
  {
    public uint LowPart;
    public int HighPart;
  }

  [StructLayout(LayoutKind.Sequential)]
  private struct LUID_AND_ATTRIBUTES
  {
    public LUID Luid;
    public UInt32 Attributes;
  }

  private struct TOKEN_PRIVILEGES
  {
    public UInt32 PrivilegeCount;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public LUID_AND_ATTRIBUTES[] Privileges;
  }

  public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege";
  public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
  public const int TOKEN_QUERY = 0x00000008;
  public const int SE_PRIVILEGE_ENABLED = 0x00000002;

  [DllImport("advapi32.dll", SetLastError = true)]
  [return: MarshalAs(UnmanagedType.Bool)]
  private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 Zero, IntPtr Null1, IntPtr Null2);

  [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern int OpenProcessToken(int ProcessHandle, int DesiredAccess, out IntPtr tokenhandle);

  [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
  private static extern int GetCurrentProcess();

  [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
  private static extern int LookupPrivilegeValue(string lpsystemname, string lpname, [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);

  static void EnableSetTimeZonePrivileges()
  {
    // We must add the set timezone privilege to the process token or SetTimeZoneInformation will fail
    IntPtr token;
    int retval = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out token);
    Assert.IsTrue(retval != 0, String.Format("OpenProcessToken failed. GetLastError: {0}", Marshal.GetLastWin32Error()));

    LUID luid = new LUID();
    retval = LookupPrivilegeValue(null, SE_TIME_ZONE_NAME, ref luid);
    Assert.IsTrue(retval != 0, String.Format("LookupPrivilegeValue failed. GetLastError: {0}", Marshal.GetLastWin32Error()));

    TOKEN_PRIVILEGES tokenPrivs = new TOKEN_PRIVILEGES();
    tokenPrivs.PrivilegeCount = 1;
    tokenPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
    tokenPrivs.Privileges[0].Luid = luid;
    tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    Assert.IsTrue(AdjustTokenPrivileges(token, false, ref tokenPrivs, 0, IntPtr.Zero, IntPtr.Zero), String.Format("AdjustTokenPrivileges failed. GetLastError: {0}", Marshal.GetLastWin32Error()));
  }
}

回答by Simon Gillbee

There is an article for doing this using VB on Microsoft's support site:

在 Microsoft 的支持站点上有一篇使用 VB 执行此操作的文章:

How to change time zone information by using Visual Basic

如何使用 Visual Basic 更改时区信息

回答by Matt Johnson-Pint

There's another way to do this, which admittedly is a bit of a hack, but works quite well in practice:

还有另一种方法可以做到这一点,诚然这有点黑客,但在实践中效果很好:

public void SetSystemTimeZone(string timeZoneId)
{
    var process = Process.Start(new ProcessStartInfo
    {
        FileName = "tzutil.exe",
        Arguments = "/s \"" + timeZoneId + "\"",
        UseShellExecute = false,
        CreateNoWindow = true
    });

    if (process != null)
    {
        process.WaitForExit();
        TimeZoneInfo.ClearCachedData();
    }
}

Simply call this method and pass the TimeZoneInfo.Idthat you wish to set. For example:

只需调用此方法并传递TimeZoneInfo.Id您希望设置的 。例如:

SetSystemTimeZone("Eastern Standard Time");

No special privileges are required to run this code, as tzutil.exealready has the appropriate permissions.

运行此代码不需要特殊权限,因为tzutil.exe已经具有适当的权限。