CF 1 Setting time across Daylight Saving Boundary Issue

G

Guest

Hi all

I have a CF 1.0 application that needs to keep its time coordinated with
that of a server. I am using a P/Invoke of SetSystemTime on the device to set
the device time to that received from the server. However I have noted a
peculiarity when the new time is in a different daylight savings period from
the current time on the device. While, in general, the time drift between the
handheld device and the server will not show this issue, it will occur about
half the time after a hard reset of the handheld device. In addition, the
server may not be in the same time zone as the handheld device, so I must
deal with UTC time. (In fact, in the real system I must convert from the
server-reported time to UTC before using it.)

After calling SetSystemTime (with a UTC time), the clock in the title bar
becomes set to a date/time that seems to use the old value of a daylight
savings flag to convert from UTC to local rather than a flag based on the
date/time being set. However, if I look at the return of a call to
System.DateTime.Now, the local time is correct.

So as an example, say the current date time is 25 May 2006 17:30 in "GMT-5
Eastern US" (which is currently UTC-4 hours due to daylight savings). If the
time is set to 25 Jan 2006 22:30, the clock in the title bar displays 6:30
(pm). This is incorrect, it should be 5:30pm because in January it is
eastern standard time (UTC-5 hours). Even more interesting is that the time
returned by a System.DateTime.Now is correct. It appears that the clock on
the title bar uses a different mechanism to decide whether to apply the
daylight savings adjustment than the System.DateTime.

I poked around the newsgroups and Kbases and did find out some interesting
info.
http://support.microsoft.com/kb/821231/en-us

Since this article mentions that "The GetTimeZoneInformation API uses a
great deal of system resources and time",
I decided to check the value reported by the P/Invoke of
GetTimeZoneInformation. Indeed, it appears that this flag is not updated for
some time after a call to SetSystemTime that would cause an adjustment in the
flag. I have observed this delay to be something less than 200ms in the best
case and longer than 2 seconds in other cases. I have run a test app on an
array of different machines running both WM2003 and WM2005 from the
manufacturers HP, Symbol, and Intermec.

The test subtracts 6 months from the current time, converts to UTC and calls
SetSystemTime. Then it checks the time using DateTime.Now. It then goes into
a 10 iteration loop with a check of the result of GetTimeZoneInformation
followed by a sleep of 200ms. The times recoreded and set are displayed on a
form as well as the results of the loop.

I thought that, perhaps, I could avoid this by using a P/Invoke to
SetLocalTime, so I added the ability to test that to the test application.
Unfortunately, while that seems to make the title bar clock show the correct
time, the System.DateTime.Now returns an incorrect time. It appears that I
cannot win.

In the real system, after the initial call to SetSystemTime, I spawn a
thread that will sleep for 5 seconds and call SetSystemTime again with the
retrieved time (after adjusting for the sleep). When this second call to
SetSystemTime is made, there is no change to the Daylight Savings flag, so
both the title bar clock and the System.DateTime.Now end up in agreement.
So, I have a workaround, but I'm curious if there is another solution to this
problem or perhaps another approach to take.

Thanks for any ideas.

Kevin

Following is the code for Form1.cs.

Create a new C# Smart Device Application, replace the generated Form1.cs
with this code, compile and give it a try.

----------
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace TimeZoneOddity
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.TextBox textBox4;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox textBox5;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox6;
private System.Windows.Forms.CheckBox cbUseSetLocal;
private System.Windows.Forms.Label label9;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.textBox3 = new System.Windows.Forms.TextBox();
this.textBox4 = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.textBox5 = new System.Windows.Forms.TextBox();
this.label7 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.textBox6 = new System.Windows.Forms.TextBox();
this.label9 = new System.Windows.Forms.Label();
this.cbUseSetLocal = new System.Windows.Forms.CheckBox();
//
// label1
//
this.label1.Size = new System.Drawing.Size(144, 16);
this.label1.Text = "DateTime value at start";
//
// label2
//
this.label2.Location = new System.Drawing.Point(0, 16);
this.label2.Size = new System.Drawing.Size(144, 16);
this.label2.Text = "(local)";
//
// label3
//
this.label3.Location = new System.Drawing.Point(0, 40);
this.label3.Size = new System.Drawing.Size(144, 16);
this.label3.Text = "(UTC)";
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(40, 16);
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(192, 22);
this.textBox1.Text = "";
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(40, 40);
this.textBox2.ReadOnly = true;
this.textBox2.Size = new System.Drawing.Size(192, 22);
this.textBox2.Text = "";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(40, 80);
this.textBox3.ReadOnly = true;
this.textBox3.Size = new System.Drawing.Size(192, 22);
this.textBox3.Text = "";
//
// textBox4
//
this.textBox4.Location = new System.Drawing.Point(40, 104);
this.textBox4.ReadOnly = true;
this.textBox4.Size = new System.Drawing.Size(192, 22);
this.textBox4.Text = "";
//
// label4
//
this.label4.Location = new System.Drawing.Point(0, 104);
this.label4.Size = new System.Drawing.Size(144, 16);
this.label4.Text = "(UTC)";
//
// label5
//
this.label5.Location = new System.Drawing.Point(0, 80);
this.label5.Size = new System.Drawing.Size(144, 16);
this.label5.Text = "(local)";
//
// label6
//
this.label6.Location = new System.Drawing.Point(0, 64);
this.label6.Size = new System.Drawing.Size(168, 16);
this.label6.Text = "DateTime value - 6 months";
//
// textBox5
//
this.textBox5.Location = new System.Drawing.Point(40, 144);
this.textBox5.ReadOnly = true;
this.textBox5.Size = new System.Drawing.Size(192, 22);
this.textBox5.Text = "";
//
// label7
//
this.label7.Location = new System.Drawing.Point(0, 144);
this.label7.Size = new System.Drawing.Size(144, 16);
this.label7.Text = "(local)";
//
// label8
//
this.label8.Location = new System.Drawing.Point(0, 128);
this.label8.Size = new System.Drawing.Size(216, 16);
this.label8.Text = "DateTime value read just after set";
//
// button1
//
this.button1.Location = new System.Drawing.Point(112, 248);
this.button1.Size = new System.Drawing.Size(120, 20);
this.button1.Text = "set back 6 months";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// textBox6
//
this.textBox6.Location = new System.Drawing.Point(8, 184);
this.textBox6.Multiline = true;
this.textBox6.ReadOnly = true;
this.textBox6.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.textBox6.Size = new System.Drawing.Size(224, 56);
this.textBox6.Text = "";
//
// label9
//
this.label9.Location = new System.Drawing.Point(0, 168);
this.label9.Size = new System.Drawing.Size(208, 16);
this.label9.Text = "Daylight/Standard after set";
//
// cbUseSetLocal
//
this.cbUseSetLocal.Location = new System.Drawing.Point(8, 248);
this.cbUseSetLocal.Size = new System.Drawing.Size(96, 20);
this.cbUseSetLocal.Text = "use SetLocal";
//
// Form1
//
this.Controls.Add(this.cbUseSetLocal);
this.Controls.Add(this.label9);
this.Controls.Add(this.textBox6);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox5);
this.Controls.Add(this.label7);
this.Controls.Add(this.label8);
this.Controls.Add(this.textBox3);
this.Controls.Add(this.textBox4);
this.Controls.Add(this.label4);
this.Controls.Add(this.label5);
this.Controls.Add(this.label6);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.MinimizeBox = false;
this.Text = "Form1";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

[DllImport("coredll", SetLastError=true)]
internal static extern bool SetSystemTime( byte[] st );

[DllImport("coredll", SetLastError=true)]
internal static extern bool SetLocalTime( byte[] st );

[DllImport("coredll", EntryPoint="GetTimeZoneInformation",
SetLastError=true)]
internal static extern int GetTimeZoneInformation( byte[] timezoneinfo );

private void button1_Click(object sender, System.EventArgs e)
{
//Clear all
this.textBox1.Text="";
this.textBox2.Text="";
this.textBox3.Text="";
this.textBox4.Text="";
this.textBox5.Text="";
this.textBox6.Text="";

//initialize a string builder for our message
System.Text.StringBuilder sb = new System.Text.StringBuilder();

//save our current time so we can reset
DateTime savedDTLocal = DateTime.Now;

//move the date 6 months back to change daylight savings
// time should change clock on title bar is reset
DateTime newLocalDt = savedDTLocal.AddMonths( -6 );
bool bSetResult;


if (this.cbUseSetLocal.Checked)
{
byte[] bytes = ToSystemTimeStructure(newLocalDt);
bSetResult = SetLocalTime( bytes );
}
else
{
byte[] bytes = ToSystemTimeStructure(newLocalDt.ToUniversalTime( ));
bSetResult = SetSystemTime( bytes );
}

//set the system time
if (!bSetResult)
{
System.Windows.Forms.MessageBox.Show("Could not set system time: " +
Marshal.GetLastWin32Error().ToString());
}
else
{
byte[] timezoneinfo = new byte[ 172 ];
this.textBox1.Text = savedDTLocal.ToString("yyyy/MM/ddTHH:mm:sszzz");
this.textBox2.Text =
savedDTLocal.ToUniversalTime().ToString("yyyy/MM/ddTHH:mm:ssZ");
this.textBox3.Text = newLocalDt.ToString("yyyy/MM/ddTHH:mm:sszzz");
this.textBox4.Text =
newLocalDt.ToUniversalTime().ToString("yyyy/MM/ddTHH:mm:ssZ");
this.textBox5.Text = DateTime.Now.ToString("yyyy/MM/ddTHH:mm:sszzz");

// start a loop that will run for about 2 seconds, checking the state of
the DST flag at 200 ms intervals
for ( int i = 0; i < 10; i++ )
{
sb.Append("Trial: " + i.ToString());
sb.Append(" " + (i*200).ToString() + "ms");
switch ( GetTimeZoneInformation( timezoneinfo ) )
{
case 0:
sb.Append(" GetTimeZoneInfo failed.\r\n");
break;
case 1:
sb.Append(" Standard.\r\n");
break;
case 2:
sb.Append(" Daylight Savings.\r\n");
break;
}
System.Threading.Thread.Sleep(200);
}
//Show the results
this.textBox6.Text = sb.ToString();
}
}

private byte [] ToSystemTimeStructure(DateTime dt)
{
//create the SYSTEMTIME-like byte array that SetSystemTime expects
byte[] bytes = new byte[16];
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Year ), 0,
bytes, 0, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Month ), 0,
bytes, 2, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Day ), 0,
bytes, 6, 2 );
//not setting day of week, it is ignored
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Hour ), 0,
bytes, 8, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Minute ), 0,
bytes, 10, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Second ), 0,
bytes, 12, 2 );
//no real need to set milliseconds for our test

return bytes;
}
}
}
 
G

Guest

Given:

- CF has no way to set the system time
- CF caches TZ info on load
- DST changes are handles by a service on the platform
- Calls to SetSystemTime call into SetLocalTime at the OS level

The rules are:
- If you P/invoke to set time or TZ info, from then on use P/Invokes to read
any time info back
- Try to do all work in one zone, preferrably GMT
- Always use the same System or Local calls, never mix them

-Chris



K G O''Connor said:
Hi all

I have a CF 1.0 application that needs to keep its time coordinated with
that of a server. I am using a P/Invoke of SetSystemTime on the device to
set
the device time to that received from the server. However I have noted a
peculiarity when the new time is in a different daylight savings period
from
the current time on the device. While, in general, the time drift between
the
handheld device and the server will not show this issue, it will occur
about
half the time after a hard reset of the handheld device. In addition, the
server may not be in the same time zone as the handheld device, so I must
deal with UTC time. (In fact, in the real system I must convert from the
server-reported time to UTC before using it.)

After calling SetSystemTime (with a UTC time), the clock in the title bar
becomes set to a date/time that seems to use the old value of a daylight
savings flag to convert from UTC to local rather than a flag based on the
date/time being set. However, if I look at the return of a call to
System.DateTime.Now, the local time is correct.

So as an example, say the current date time is 25 May 2006 17:30 in "GMT-5
Eastern US" (which is currently UTC-4 hours due to daylight savings). If
the
time is set to 25 Jan 2006 22:30, the clock in the title bar displays
6:30
(pm). This is incorrect, it should be 5:30pm because in January it is
eastern standard time (UTC-5 hours). Even more interesting is that the
time
returned by a System.DateTime.Now is correct. It appears that the clock
on
the title bar uses a different mechanism to decide whether to apply the
daylight savings adjustment than the System.DateTime.

I poked around the newsgroups and Kbases and did find out some interesting
info.
http://support.microsoft.com/kb/821231/en-us

Since this article mentions that "The GetTimeZoneInformation API uses a
great deal of system resources and time",
I decided to check the value reported by the P/Invoke of
GetTimeZoneInformation. Indeed, it appears that this flag is not updated
for
some time after a call to SetSystemTime that would cause an adjustment in
the
flag. I have observed this delay to be something less than 200ms in the
best
case and longer than 2 seconds in other cases. I have run a test app on
an
array of different machines running both WM2003 and WM2005 from the
manufacturers HP, Symbol, and Intermec.

The test subtracts 6 months from the current time, converts to UTC and
calls
SetSystemTime. Then it checks the time using DateTime.Now. It then goes
into
a 10 iteration loop with a check of the result of GetTimeZoneInformation
followed by a sleep of 200ms. The times recoreded and set are displayed
on a
form as well as the results of the loop.

I thought that, perhaps, I could avoid this by using a P/Invoke to
SetLocalTime, so I added the ability to test that to the test application.
Unfortunately, while that seems to make the title bar clock show the
correct
time, the System.DateTime.Now returns an incorrect time. It appears that
I
cannot win.

In the real system, after the initial call to SetSystemTime, I spawn a
thread that will sleep for 5 seconds and call SetSystemTime again with the
retrieved time (after adjusting for the sleep). When this second call to
SetSystemTime is made, there is no change to the Daylight Savings flag, so
both the title bar clock and the System.DateTime.Now end up in agreement.
So, I have a workaround, but I'm curious if there is another solution to
this
problem or perhaps another approach to take.

Thanks for any ideas.

Kevin

Following is the code for Form1.cs.

Create a new C# Smart Device Application, replace the generated Form1.cs
with this code, compile and give it a try.

----------
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace TimeZoneOddity
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.TextBox textBox4;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox textBox5;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox6;
private System.Windows.Forms.CheckBox cbUseSetLocal;
private System.Windows.Forms.Label label9;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.textBox3 = new System.Windows.Forms.TextBox();
this.textBox4 = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.textBox5 = new System.Windows.Forms.TextBox();
this.label7 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.textBox6 = new System.Windows.Forms.TextBox();
this.label9 = new System.Windows.Forms.Label();
this.cbUseSetLocal = new System.Windows.Forms.CheckBox();
//
// label1
//
this.label1.Size = new System.Drawing.Size(144, 16);
this.label1.Text = "DateTime value at start";
//
// label2
//
this.label2.Location = new System.Drawing.Point(0, 16);
this.label2.Size = new System.Drawing.Size(144, 16);
this.label2.Text = "(local)";
//
// label3
//
this.label3.Location = new System.Drawing.Point(0, 40);
this.label3.Size = new System.Drawing.Size(144, 16);
this.label3.Text = "(UTC)";
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(40, 16);
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(192, 22);
this.textBox1.Text = "";
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(40, 40);
this.textBox2.ReadOnly = true;
this.textBox2.Size = new System.Drawing.Size(192, 22);
this.textBox2.Text = "";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(40, 80);
this.textBox3.ReadOnly = true;
this.textBox3.Size = new System.Drawing.Size(192, 22);
this.textBox3.Text = "";
//
// textBox4
//
this.textBox4.Location = new System.Drawing.Point(40, 104);
this.textBox4.ReadOnly = true;
this.textBox4.Size = new System.Drawing.Size(192, 22);
this.textBox4.Text = "";
//
// label4
//
this.label4.Location = new System.Drawing.Point(0, 104);
this.label4.Size = new System.Drawing.Size(144, 16);
this.label4.Text = "(UTC)";
//
// label5
//
this.label5.Location = new System.Drawing.Point(0, 80);
this.label5.Size = new System.Drawing.Size(144, 16);
this.label5.Text = "(local)";
//
// label6
//
this.label6.Location = new System.Drawing.Point(0, 64);
this.label6.Size = new System.Drawing.Size(168, 16);
this.label6.Text = "DateTime value - 6 months";
//
// textBox5
//
this.textBox5.Location = new System.Drawing.Point(40, 144);
this.textBox5.ReadOnly = true;
this.textBox5.Size = new System.Drawing.Size(192, 22);
this.textBox5.Text = "";
//
// label7
//
this.label7.Location = new System.Drawing.Point(0, 144);
this.label7.Size = new System.Drawing.Size(144, 16);
this.label7.Text = "(local)";
//
// label8
//
this.label8.Location = new System.Drawing.Point(0, 128);
this.label8.Size = new System.Drawing.Size(216, 16);
this.label8.Text = "DateTime value read just after set";
//
// button1
//
this.button1.Location = new System.Drawing.Point(112, 248);
this.button1.Size = new System.Drawing.Size(120, 20);
this.button1.Text = "set back 6 months";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// textBox6
//
this.textBox6.Location = new System.Drawing.Point(8, 184);
this.textBox6.Multiline = true;
this.textBox6.ReadOnly = true;
this.textBox6.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.textBox6.Size = new System.Drawing.Size(224, 56);
this.textBox6.Text = "";
//
// label9
//
this.label9.Location = new System.Drawing.Point(0, 168);
this.label9.Size = new System.Drawing.Size(208, 16);
this.label9.Text = "Daylight/Standard after set";
//
// cbUseSetLocal
//
this.cbUseSetLocal.Location = new System.Drawing.Point(8, 248);
this.cbUseSetLocal.Size = new System.Drawing.Size(96, 20);
this.cbUseSetLocal.Text = "use SetLocal";
//
// Form1
//
this.Controls.Add(this.cbUseSetLocal);
this.Controls.Add(this.label9);
this.Controls.Add(this.textBox6);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox5);
this.Controls.Add(this.label7);
this.Controls.Add(this.label8);
this.Controls.Add(this.textBox3);
this.Controls.Add(this.textBox4);
this.Controls.Add(this.label4);
this.Controls.Add(this.label5);
this.Controls.Add(this.label6);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.MinimizeBox = false;
this.Text = "Form1";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

[DllImport("coredll", SetLastError=true)]
internal static extern bool SetSystemTime( byte[] st );

[DllImport("coredll", SetLastError=true)]
internal static extern bool SetLocalTime( byte[] st );

[DllImport("coredll", EntryPoint="GetTimeZoneInformation",
SetLastError=true)]
internal static extern int GetTimeZoneInformation( byte[] timezoneinfo );

private void button1_Click(object sender, System.EventArgs e)
{
//Clear all
this.textBox1.Text="";
this.textBox2.Text="";
this.textBox3.Text="";
this.textBox4.Text="";
this.textBox5.Text="";
this.textBox6.Text="";

//initialize a string builder for our message
System.Text.StringBuilder sb = new System.Text.StringBuilder();

//save our current time so we can reset
DateTime savedDTLocal = DateTime.Now;

//move the date 6 months back to change daylight savings
// time should change clock on title bar is reset
DateTime newLocalDt = savedDTLocal.AddMonths( -6 );
bool bSetResult;


if (this.cbUseSetLocal.Checked)
{
byte[] bytes = ToSystemTimeStructure(newLocalDt);
bSetResult = SetLocalTime( bytes );
}
else
{
byte[] bytes = ToSystemTimeStructure(newLocalDt.ToUniversalTime( ));
bSetResult = SetSystemTime( bytes );
}

//set the system time
if (!bSetResult)
{
System.Windows.Forms.MessageBox.Show("Could not set system time: " +
Marshal.GetLastWin32Error().ToString());
}
else
{
byte[] timezoneinfo = new byte[ 172 ];
this.textBox1.Text = savedDTLocal.ToString("yyyy/MM/ddTHH:mm:sszzz");
this.textBox2.Text =
savedDTLocal.ToUniversalTime().ToString("yyyy/MM/ddTHH:mm:ssZ");
this.textBox3.Text = newLocalDt.ToString("yyyy/MM/ddTHH:mm:sszzz");
this.textBox4.Text =
newLocalDt.ToUniversalTime().ToString("yyyy/MM/ddTHH:mm:ssZ");
this.textBox5.Text = DateTime.Now.ToString("yyyy/MM/ddTHH:mm:sszzz");

// start a loop that will run for about 2 seconds, checking the state of
the DST flag at 200 ms intervals
for ( int i = 0; i < 10; i++ )
{
sb.Append("Trial: " + i.ToString());
sb.Append(" " + (i*200).ToString() + "ms");
switch ( GetTimeZoneInformation( timezoneinfo ) )
{
case 0:
sb.Append(" GetTimeZoneInfo failed.\r\n");
break;
case 1:
sb.Append(" Standard.\r\n");
break;
case 2:
sb.Append(" Daylight Savings.\r\n");
break;
}
System.Threading.Thread.Sleep(200);
}
//Show the results
this.textBox6.Text = sb.ToString();
}
}

private byte [] ToSystemTimeStructure(DateTime dt)
{
//create the SYSTEMTIME-like byte array that SetSystemTime expects
byte[] bytes = new byte[16];
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Year ), 0,
bytes, 0, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Month ), 0,
bytes, 2, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Day ), 0,
bytes, 6, 2 );
//not setting day of week, it is ignored
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Hour ), 0,
bytes, 8, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Minute ), 0,
bytes, 10, 2 );
Buffer.BlockCopy(BitConverter.GetBytes( (short)dt.Second ), 0,
bytes, 12, 2 );
//no real need to set milliseconds for our test

return bytes;
}
}
}
 
G

Guest

Heh heh, not the most comforting answer as you might expect :)

Besides the difference between the time reported by CF System.Date and the
title bar clock, the fact that using SetLocalTime and SetSystemTime with what
seems to be the appropriate inputs yield different answers is a problem that
disturbs me.

In addition, the title bar clock does not seem to check the daylight savings
flag after the initial call to SetSystemTime even though that does get
updated to the appropriate value eventually.

BTW, I am using OpenNETCF in the app. The calls (in the real app, not the
sample posted) are done with DateTimeEx. Handy stuff all around. Thanks.

Kevin


Given:

- CF has no way to set the system time
- CF caches TZ info on load
- DST changes are handles by a service on the platform
- Calls to SetSystemTime call into SetLocalTime at the OS level

The rules are:
- If you P/invoke to set time or TZ info, from then on use P/Invokes to read
any time info back
- Try to do all work in one zone, preferrably GMT
- Always use the same System or Local calls, never mix them

-Chris
<snip>
 
P

Paul G. Tobey [eMVP]

My brain isn't entirely up to DST thoughts yet today, but you should be OK
if the server returns GMT and you use SetSystemTime() to transfer that to
the local clock. It should be clear that setting the system time near a DST
transition time is a significant edge problem and you really, really don't
want to do that. We do this to sync with a time server and everything works
pretty much as expected, allowing for some delay in updating the UI. The
title bar clock is only updated periodically. You shouldn't see an instant
effect of changing the time.

If you're getting wrong results, post what times were used, what time zones,
and what you observed and we might be able to formulate an explanation and a
fix.

Paul T.
 
G

Guest

Hi Paul,

The code that is in the post that originated the thread should demonstrate
the issue. Try it with the "use SetLocal" box unchecked to employ
SetSystemTime then try it again with the "use SetLocal" box checked to employ
SetLocalTime. You will see that in each case, after setting the time, the
title bar clock will not agree with the value returned by System.DateTime.Now
which is displayed in the text box below the label "DateTime value read just
after set".

I'm very interested to get your thoughts on this, thanks for taking a look.

Kevin
 
P

Paul G. Tobey [eMVP]

Yes, that's expected, I think, although not necessarily right. DateTime.Now
is the problem, not the OS itself. You can easily confirm that with a
native-code application, of course. As to why, I don't know. You can
always P/Invoke GetLocalTime/GetSystemTime, just to verify that...

Paul T.
 
G

Guest

Thanks again, Paul,

Since you indicate that you think it is expected, do you know if there is a
bit of documentation or KBase on this that I might have missed?

I'm also curious as to why you think it is DateTime.Now that has the problem
and not the OS. I think that I was calling SetSystemTime and SetLocalTime
with structures that ought to yield the same result, but as you can see in
the little program, that is not the case. If I didn't mess up those calls,
then it indicates to me that there is an OS problem rather than a
DateTime.Now issue. Am I missing something?

Also there seems to be another problem demonstrated by the little app. The
title bar clock seems to check for the daylight savings flag only when the
time is updated by Set(System|Local)Time, but after a little while the
daylight savings flag changes (to the appropriate value based on the date and
time) however the clock never looks at that flag again (or never gets some
signal that it changes) and stays out of whack. Well :) at least that is
what it seems from the outside looking in.

Kevin

Paul G. Tobey said:
Yes, that's expected, I think, although not necessarily right. DateTime.Now
is the problem, not the OS itself. You can easily confirm that with a
native-code application, of course. As to why, I don't know. You can
always P/Invoke GetLocalTime/GetSystemTime, just to verify that...

Paul T.
<snip>
 
P

Paul G. Tobey [eMVP]

Maybe expected is too strong a word. Known, maybe.

I do SetSystemTime() all over the place and the clock is always fine. I
have a number of utilities which conspire to set the time under user control
and allow it to be read back, all with SetLocalTime/GetLocalTime and there's
never been a test failure that I can remember.

When there's a change in the DST flag performed by the shell, at least the
standard, non-Pocket PC shell, in Windows CE, SetLocalTime() is called to
make that change and a check for DST is done on every time zone change and
manual date/time change, as well as when the next transition time rolls
around. That is, there are three events detected: user changes time zone,
user changes time, and clock reached time to go from DST to ST or vice
versa. The shell always just uses GetLocalTime() to retrieve the time, as
far as I can tell and never does anything as far as display with the time
zone, calculating anything, or even knowing whether DST is active or not.

Paul T.
 
P

Paul G. Tobey [eMVP]

I've just run through a simple test where I send a new local time (local
time is what's stored in the system clock), to one of our devices, then read
it back, via a Telnet server that I wrote. It works precisely as expected.
I change the time and read it back as fast as I can type "get datetime" and
it matches what I set plus a second or two. Changing to a time that is
after the next DST transition does what I'd expect (although I doubt that
it's documented anywhere), changing the time forward or back an hour to
account for the DST change and changing the DST flag returned by
GetTimeZoneInformation().

My guess as to the source of this problem with the DateTime class is that
it's probably caching the DST flag and, therefore, using the wrong offset to
calculate the current time, somehow. I don't know why it would be using the
system time and offsetting it, rather than just reading the local time
directly, but that's what it seems to do, resulting in inconsistent
behavior. If it just returned the result of GetLocalTime(), everything
would match up perfectly, I think.

Paul T.

"Paul G. Tobey [eMVP]" <p space tobey no spam AT no instrument no spam DOT
com> wrote in message news:[email protected]...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top