Author Topic: Change System Time & Day  (Read 4688 times)

Exergist

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 405
  • Ride the lightning
Change System Time & Day
« on: March 26, 2018, 01:10:22 PM »
Short Summary: This is a profile containing C# inline functions that allow you to change the current system time or day, as well as reset the system time.



I recently had a need to repeatedly make changes to the system time or date, so I decided to make a VoiceAttack profile that would handle this as well as reset the system time as needed. More specifically this allows commands like "Change system time to 3:15 PM" or "Change day to Monday." I didn't want to mess with changing the actual date due to the number of command combinations this would create (and I don't want to mess with dictation). When the day or time is changed VoiceAttack will stop Windows from automatically synchronizing the system time, otherwise your modified time would eventually be reset when the system time resynchronizes. More specifically, the Windows Time Service is stopped and a registry edit is made to disable automatic time synchronization. Reseting the time will be explained in more detail later. The profile is attached and the C# inline functions are outlined below.

Please note:
  • I originally created this for temporary time and day changes. All my testing shows that the time and day changes will be maintained, but longer term evaluation is needed. To be honest though, if you really need to change the time or date and keep it that way for a while you should probably just do it manually.
  • The commands and inline functions do not affect the system's time zone (more on this later).
Change System Day
Basically changes the day to the specified value. So if today is Monday (3/26/18) and you invoke the command "Change day to Thursday" the system day will be pushed ahead to the next future Thursday (3/29/18). If you requested the day be changed to Monday then nothing will happen since the requested day is equivalent to the current day. As you can see changing the day also changes the date, and advances in the month or year will be made when needed (i.e., "Change Day to Sunday" would change the date to 4/1/18).
Referenced Assemblies:: System.dll; System.ServiceProcess.dll
Code: [Select]
using Microsoft.Win32;
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading;

public class VAInline
{
public void main()
{
// Retrieve data from VoiceAttack
string NewDayName = VA.GetText("~~NewDayOfWeek"); // Retrieve requested day of week from VoiceAttack
string TimeServiceType = VA.GetText("~~TimeServiceType"); // Retrieve the desired time service for automatically synchronizing the system time

ModifyTimeService("stop", TimeServiceType); // Stop the Windows time service so that the subsequently modified date doesn't automatcially get reset

// Get current system date, time, and day of week
string DateTimeNow = DateTime.Now.ToString("{dddd M/d/yyyy h:mm:ss tt}"); // Get current local time from system and store as a string
string CurrentDayName = DateTime.Now.DayOfWeek.ToString(); // Get the name of the current day based on local time from system
int CurrentDayNumber = (int)DateTime.Now.DayOfWeek; // Get the day number of the current day based on local time from system
VA.SetText("~~CurrentDateTime", DateTimeNow); // Send current date and time information back as VoiceAttack variable

// Perform calculations to determine number of days that need to be added to current date to obtain requested day of the week
CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture; // Create instance of cultureInfo
TextInfo textInfo = cultureInfo.TextInfo; // Create instance of textInfo
NewDayName = textInfo.ToTitleCase(textInfo.ToLower(NewDayName)); // Convert string stored in day to TitleCase (proper case) based on the culture info
int NewDayNumber = GetDayNumber(NewDayName); // Get the day number of the requested new day
if (NewDayNumber == 10) // Check if NewDayNumber equals 10
{
VA.WriteToLog("Data entry error. Check name of new day of week."); // Output info to event log
ModifyTimeService("start", TimeServiceType); // Restart the Windows time service
return; // Stop processing
}
int NewDayOffset = 0; // Initialize NewDayOffset
if (NewDayName != CurrentDayName) // Check if NewDayName does NOT equal CurrentDayName
{
NewDayOffset = (NewDayNumber - CurrentDayNumber) + ((NewDayNumber - CurrentDayNumber) >= 0 ? 0 : 7); // Calculate the NewDayOffset that will be added to the current date

// Change current system date to the next earliest requested day (in the future)
SystemTime ChangeDay = new SystemTime(); // Create SystemTime instance
ChangeDay.wYear = (short)DateTime.Now.Year; // Set year based on local date from system
ChangeDay.wMonth = (short)DateTime.Now.Month; // Set month based on local date from system
ChangeDay.wDay = (short)(DateTime.Now.Day + NewDayOffset); // Set day based on local date from system plus NewDayOffset (to advance the day)
int DaysInMonth = System.DateTime.DaysInMonth(ChangeDay.wYear, ChangeDay.wMonth); // Get the number of days in the current month
if (ChangeDay.wDay > DaysInMonth) // Check if the new day of the month is greater than the number of days in the current month
{
ChangeDay.wDay = (short)(ChangeDay.wDay - DaysInMonth); // Revise the day of the month because the month also needs to be revised
ChangeDay.wMonth++; // Increment the month by 1
if (ChangeDay.wMonth > 12) // Check if the current month is greater than 12
{
ChangeDay.wMonth = 1; // Revise the month because the year also needs to be revised
ChangeDay.wYear++; // Increment the year by 1
}
}
ChangeDay.wHour = (short)DateTime.Now.Hour; // Set hour based on local time from system
ChangeDay.wMinute = (short)DateTime.Now.Minute; // Set minutes based on local time from system
ChangeDay.wSecond = (short)DateTime.Now.Second; // Set seconds based on local time from system
ChangeDay.wMilliseconds = (short)DateTime.Now.Millisecond; // Set milliseconds based on local time from system
SetLocalTime(ref ChangeDay); // Change the local system date and time
DateTimeNow = DateTime.Now.ToString("{dddd M/d/yyyy h:mm:ss tt}"); // Get (modified) current local time from system and store as a string
VA.SetText("~~ModifiedDateTime", DateTimeNow); // Send current date and time information back as VoiceAttack variable
}
else
{
VA.WriteToLog("Requested day is equivalent to current day. No change made.", "yellow"); // Output info to event log
ModifyTimeService("start", TimeServiceType); // Restart the Windows time service
}
}

// Modify the Windows time service ("start," "stop," or "reset")
public void ModifyTimeService(string action, string TimeServiceType)
{
ServiceController controller = new ServiceController("W32Time"); // Create new instance of ServiceController related to the "W32Time" service
switch (action) // Switch statement based on inputted action
{
case "start": // case for "start" action
if (controller.Status != ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Start(); // Start the Windows time Service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", TimeServiceType, RegistryValueKind.String); // Tell time service to synchronize from the domain hierarchy
}

break; // break out of switch
case "stop": // case for "stop" action
if (controller.Status == ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Stop(); // Stop the Windows time service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", "NoSync", RegistryValueKind.String); // Tell time service to NOT synchronize with other sources
}

break; // break out of switch
case "reset": // case for "reset" action
if (controller.Status == ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Stop(); // Stop the Windows time service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", "NoSync", RegistryValueKind.String); // Tell time service to NOT synchronize with other sources
Thread.Sleep(100); // Pause for 100 ms
}
controller.Start(); // Start the Windows time Service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", TimeServiceType, RegistryValueKind.String); // Tell time service to synchronize from the domain hierarchy
break; // break out of switch
default: // default case
VA.WriteToLog("Invalid input for ModifyTimeService()", "red"); // Output info to event log
break; // break out of switch
}
}

// Function for obtaining inputted day name's number in format corresponding to that for C# DateTime.Now.DayOfWeek (Sunday = 0, Saturday = 6)
public static int GetDayNumber(string day)
{
int DayNum; // Initialize DayNum

switch (day) // Switch statement based on inputted action
{
case "Sunday": // case for "start" action
DayNum = 0; // Change value of DayNum
break; // break out of switch
case "Monday": // case for "start" action
DayNum = 1; // Change value of DayNum
break; // break out of switch
case "Tuesday": // case for "start" action
DayNum = 2; // Change value of DayNum
break; // break out of switch
case "Wednesday": // case for "start" action
DayNum = 3; // Change value of DayNum
break; // break out of switch
case "Thursday": // case for "start" action
DayNum = 4; // Change value of DayNum
break; // break out of switch
case "Friday": // case for "start" action
DayNum = 5; // Change value of DayNum
break; // break out of switch
case "Saturday": // case for "start" action
DayNum = 6; // Change value of DayNum
break; // break out of switch
default: // case for when day does not match the other cases
DayNum = 10; // Change value of DayNum for later error processing
break; // break out of switch
}
return DayNum; // Return the value of DayNum
}

// Add SetLocalTime method to class
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetLocalTime(ref SystemTime st);
}

// Define SystemTime structure
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}

Change System Time
Basically changes the time of day without modifying the current date. So commands like "Change time to 5:20 AM," "Change time to 11 PM," or "Change time to 8 o'clock AM" would work as described. The VoiceAttack command was set up to handle time increments of 5 minutes (so "Change time to 1:03 AM" would not work), but the base functionality will work with any integer time increment at or above 1 minute (so "Change time to 1:03 AM" is possible with modification to the When I Say). Note that I did not include functionality for setting the time seconds. Instead the seconds will automatically be set to 0 when the time is changed.
Referenced Assemblies: System.dll; System.ServiceProcess.dll
Code: [Select]
using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading;

public class VAInline
{
public void main()
{
// Retrieve data from VoiceAttack
int minute = (int)VA.GetInt("~~minute"); // Get requested minute
int hour = (int)VA.GetInt("~~hour"); // Get requested hour
string meridiem = VA.GetText("~~meridiem"); // Get requested meridiem
string TimeServiceType = VA.GetText("~~TimeServiceType"); // Retrieve the desired time service for automatically synchronizing the system time

ModifyTimeService("stop", TimeServiceType); // Stop the Windows time service so that the subsequently modified date doesn't automatcially get reset

// Get current system date & time
string DateTimeNow = DateTime.Now.ToString("{dddd M/d/yyyy h:mm:ss tt}"); // Get current local time from system and store as a string
VA.SetText("~~CurrentDateTime", DateTimeNow); // Send current date and time information back as VoiceAttack variable

// Determine HourOffset for 24-hour format conversion
int HourOffset; // Declare HourOffset
if (String.Equals(meridiem, "PM", StringComparison.OrdinalIgnoreCase) && hour != 12) // Check if meridiem equals "PM" and the hour is not 12)
HourOffset = 12; // Set HourOffset
else
HourOffset = 0; // Set HourOffset

// Change current system time
SystemTime ChangeTime = new SystemTime(); // Create instance of SystemTime
ChangeTime.wYear = (short)DateTime.Now.Year; // Set year based on local date from system
ChangeTime.wMonth = (short)DateTime.Now.Month; // Set month based on local date from system
ChangeTime.wDay = (short)DateTime.Now.Day; // Set day based on local date from system
ChangeTime.wHour = (short)(hour + HourOffset); // Set hours based on user input
ChangeTime.wMinute = (short)minute; // Set minutes based on user input
ChangeTime.wSecond = 0; // Set seconds to 0
ChangeTime.wMilliseconds = 0; // Set milliseconds to 0
SetLocalTime(ref ChangeTime); // Change the local system date & time
DateTimeNow = DateTime.Now.AddMilliseconds(1).ToString("{dddd M/d/yyyy h:mm:ss tt}"); // Get (modified) current local time from system and store as a string. 1 ms added to ensure that the text display of the time is accurate.
VA.SetText("~~ModifiedDateTime", DateTimeNow); // Send current date and time information back as VoiceAttack variable
}

// Modify the Windows time service ("start," "stop," or "reset")
public void ModifyTimeService(string action, string TimeServiceType)
{
ServiceController controller = new ServiceController("W32Time"); // Create new instance of ServiceController related to the "W32Time" service
switch (action) // Switch statement based on inputted action
{
case "start": // case for "start" action
if (controller.Status != ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Start(); // Start the Windows time Service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", TimeServiceType, RegistryValueKind.String); // Tell time service to synchronize from the domain hierarchy
}

break; // break out of switch
case "stop": // case for "stop" action
if (controller.Status == ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Stop(); // Stop the Windows time service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", "NoSync", RegistryValueKind.String); // Tell time service to NOT synchronize with other sources
}

break; // break out of switch
case "reset": // case for "reset" action
if (controller.Status == ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Stop(); // Stop the Windows time service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", "NoSync", RegistryValueKind.String); // Tell time service to NOT synchronize with other sources
Thread.Sleep(100); // Pause for 100 ms
}
controller.Start(); // Start the Windows time Service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", TimeServiceType, RegistryValueKind.String); // Tell time service to synchronize from the domain hierarchy
break; // break out of switch
default: // default case
VA.WriteToLog("Invalid input for ModifyTimeService()", "red"); // Output info to event log
break; // break out of switch
}
}

// Add SetLocalTime method to class
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetLocalTime(ref SystemTime st);
}

// Define SystemTime structure
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}

Reset System Time
As you probably expect this will reset the system time to your actual local time. I reside in the Central Time Zone (UTC-08:00), and all my testing shows that this works as expected. The Windows Time Service periodically polls some external server or other source from the internet (and perhaps does some additional processing) to obtain your actual time. The above two inline functions stop the time service as well as disable the service's ability to sync from other sources. The Reset System Time command basically does the following:
  • Restart the Windows Time Service
  • Specify a source for synchronizing the time service
  • Obtain the local time from a web query
The time synchronization from the time service could be nearly instantaneous or it may be slightly delayed before the system polls the external source. This is why I included the web query, which will immediately reset the time and close any gap between the reset request and actual synchronization from the external source. By default I've selected the "AllSync" synchronization time for the time service, which will allow the system to use all available synchronization mechanisms to update the time (see this documentation from Microsoft for more info). I choose AllSync because it should (hopefully) work on most (if not all) systems out of the box. I've included notes in the VoiceAttack command if you need to try out the other source types.

It is important to note that the time reset will take your system's time zone into account. For example, let's say my actual local (Central = UTC-06:00) time is 3:15 PM. If I manually set my system's time zone to the Pacific Time Zone (UTC-08:00) then my system time will be 1:15 PM. If I then issue the command "Change time to 5:15 PM" the time will be set as such. If I reset the system time then the time will briefly be changed to my actual local Central time due to the web query that considers my actual time zone (3:15 PM), and once the time automatically synchronizes it will change the time again to 1:15 PM (which reflects the time zone I've set on my system). In other words, the web query for the current time is based on your local time, but the automatic time synchronization (which will override the web query) is based on your system's time zone. So if you really need to use a different time zone than what you currently reside in, the Reset System Time command will be "incorrect" for a short period of time before your system resynchronizes based on your inputted time zone.
Referenced Assemblies: System.dll; System.ServiceProcess.dll
Code: [Select]
using Microsoft.Win32;
using System;
using System.Globalization;
using System.Net;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading;

public class VAInline
{
public void main()
{
// Get current system date & time
string DateTimeNow = DateTime.Now.ToString("{dddd M/d/yyyy h:mm:ss tt}"); // Get current local time from system and store as a string
VA.SetText("~~CurrentDateTime", DateTimeNow); // Send current date and time information back as VoiceAttack variable

string TimeServiceType = VA.GetText("~~TimeServiceType"); // Retrieve the desired time service for automatically synchronizing the system time

// Obtain actual local time via web query
DateTime LocalWebTime = GetWebTime(); // Query Google's home page header for the current local time
if (LocalWebTime != default(DateTime)) // Check if time query from website was successful
{
// Reset system time based on local time from web query
SystemTime ResetDateTime = new SystemTime(); // Create SystemTime instance
ResetDateTime.wYear = (short)LocalWebTime.Year; // Set year based on local web date
ResetDateTime.wMonth = (short)LocalWebTime.Month; // Set month based on local web date
ResetDateTime.wDay = (short)LocalWebTime.Day; // Set day based on local web date
ResetDateTime.wHour = (short)(LocalWebTime.Hour); // Set hour based on local web time
ResetDateTime.wMinute = (short)LocalWebTime.Minute; // Set minutes based on local web time
ResetDateTime.wSecond = (short)LocalWebTime.Second; // Set seconds based on local web time
ResetDateTime.wMilliseconds = (short)LocalWebTime.Millisecond; // Set milliseconds based on local web time
SetLocalTime(ref ResetDateTime); // Change the local system date & time
DateTimeNow = DateTime.Now.ToString("{dddd M/d/yyyy h:mm:ss tt}"); // Get (reset) current local time from system and store as a string
VA.SetText("~~ModifiedDateTime", DateTimeNow); // Send reset date and time information back as VoiceAttack variable
}
else // Time query from website was unsuccessful
{
VA.WriteToLog("Error requesting local date and time from web. Connect to the internet to automatically reset system time.", "red"); // Output info to event log
}
ModifyTimeService("reset", TimeServiceType); // Start the Windows time service, which will reset the system time to the actual local time (takes a moment or two to update). This is done as an extra precaution to ensure the time is accurate.
}

// Modify the Windows time service ("start," "stop," or "reset")
public void ModifyTimeService(string action, string TimeServiceType)
{
ServiceController controller = new ServiceController("W32Time"); // Create new instance of ServiceController related to the "W32Time" service
switch (action) // Switch statement based on inputted action
{
case "start": // case for "start" action
if (controller.Status != ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Start(); // Start the Windows time Service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", TimeServiceType, RegistryValueKind.String); // Tell time service to synchronize from the domain hierarchy
}

break; // break out of switch
case "stop": // case for "stop" action
if (controller.Status == ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Stop(); // Stop the Windows time service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", "NoSync", RegistryValueKind.String); // Tell time service to NOT synchronize with other sources
}
break; // break out of switch
case "reset": // case for "reset" action
if (controller.Status == ServiceControllerStatus.Running) // Check if the Windows time service is running
{
controller.Stop(); // Stop the Windows time service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", "NoSync", RegistryValueKind.String); // Tell time service to NOT synchronize with other sources
Thread.Sleep(100); // Pause for 100 ms
}
controller.Start(); // Start the Windows time Service
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Parameters", "Type", TimeServiceType, RegistryValueKind.String); // Tell time service to synchronize from the domain hierarchy
break; // break out of switch
default: // default case
VA.WriteToLog("Invalid input for ModifyTimeService()", "red"); // Output info to event log
break; // break out of switch
}
}

// Web query to obtain local time from given website's http response header
// From cerberus @ https://stackoverflow.com/questions/6435099/how-to-get-datetime-from-the-internet
public static DateTime GetWebTime()
{
try
{
using (var response = WebRequest.Create("http://www.google.com").GetResponse())
return DateTime.ParseExact(response.Headers["date"], "ddd, dd MMM yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture.DateTimeFormat, DateTimeStyles.AssumeUniversal);
}
catch (WebException)
{
return default(DateTime); // In case something goes wrong return a default timestamp
}
}

// Add SetLocalTime method to class
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetLocalTime(ref SystemTime st);
}

// Define SystemTime structure
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public short wYear;
public short wMonth;
public short wDayOfWeek;
public short wDay;
public short wHour;
public short wMinute;
public short wSecond;
public short wMilliseconds;
}


Support This and Future Efforts
If you find this profile useful, please consider buying me a cup of coffee. Thank you for your support!
 


Cheers! :)
« Last Edit: June 02, 2020, 09:45:24 PM by Exergist »