在做YH维护的时候,偶尔会碰到这样的问题:电脑的非正常关机导致系统时间出错(变为了2002-1-1),从而影响到项目系统的使用。尤其是设计到money的系统,如果时间错误,可能会导致无法想象的后果。所以我们可能需要用系统和网络的双重验证。
通过收集、修改、优化和测试,剔除了一些错误的和速度超慢的,只剩下了4种可行的方案。这些方案中主要有3类:
一、通过向某网站发送请求,获取服务器响应请求的时间
二、获某时间网页的html或xml码,读取其中的时间。
三、通过向某授时服务器发送请求,获取网络时间
我把这些方法封装到了一个类库里了。
下面是具体的类:NetTime类(包含了多种获取网络时间的方法,标有速度),需要添加引用:COM-Microsoft Xml 3.0
using System; using System.Collections.Generic; using System.Linq; using System.Text; using MSXML2; using System.IO; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; using System.IO.Compression; using System.Xml; namespace WebTime { /// <summary> /// 网络时间 /// </summary> public class NetTime { #region 获取标准北京时间1 速度100ms /// <summary> /// [1].获取标准北京时间1,读取http://www.beijing-time.org/time.asp /// </summary> /// <returns></returns> public DateTime GetBeijingTime() { #region 格式 // t0=new Date().getTime(); nyear=2012; nmonth=2; nday=11; // nwday=6; nhrs=0; nmin=23; nsec=0; #endregion DateTime dt; WebRequest wrt = null; WebResponse wrp = null; try { wrt = WebRequest.Create("http://www.beijing-time.org/time.asp"); wrp = wrt.GetResponse(); string html = string.Empty; using (Stream stream = wrp.GetResponseStream()) { using (StreamReader sr = new StreamReader(stream, Encoding.UTF8)) { html = sr.ReadToEnd(); } } string[] tempArray = html.Split(';'); for (int i = 0; i < tempArray.Length; i++) { tempArray[i] = tempArray[i].Replace("\r\n", ""); } string year = tempArray[1].Split('=')[1]; string month = tempArray[2].Split('=')[1]; string day = tempArray[3].Split('=')[1]; string hour = tempArray[5].Split('=')[1]; string minite = tempArray[6].Split('=')[1]; string second = tempArray[7].Split('=')[1]; //for (int i = 0; i < tempArray.Length; i++) //{ // tempArray[i] = tempArray[i].Replace("\r\n", ""); //} //string year = tempArray[1].Substring(tempArray[1].IndexOf("nyear=") + 6); //string month = tempArray[2].Substring(tempArray[2].IndexOf("nmonth=") + 7); //string day = tempArray[3].Substring(tempArray[3].IndexOf("nday=") + 5); //string hour = tempArray[5].Substring(tempArray[5].IndexOf("nhrs=") + 5); //string minite = tempArray[6].Substring(tempArray[6].IndexOf("nmin=") + 5); //string second = tempArray[7].Substring(tempArray[7].IndexOf("nsec=") + 5); dt = DateTime.Parse(year + "-" + month + "-" + day + " " + hour + ":" + minite + ":" + second); } catch (WebException) { return DateTime.Parse("2011-1-1"); } catch (Exception) { return DateTime.Parse("2011-1-1"); } finally { if (wrp != null) wrp.Close(); if (wrt != null) wrt.Abort(); } return dt; } #endregion #region 获取网站响应请求的时间,速度200ms /// <summary> /// [2]获取网站响应请求的时间,速度200ms /// </summary> /// <param name="hUrl">网址</param> /// <returns>DateTime</returns> /// <remarks></remarks> public DateTime GetNetTime( string hUrl) { string datetxt = null; //请求回应的时间 string[] date1 = null; //分割后的星期和日期 string date2 = ""; //分割后的日期和GMT string[] date3 = null; //最终成型的日期 DateTime nTime =DateTime.Today ; string localtime = ""; string mon = ""; XMLHTTP objHttp = new XMLHTTP(); objHttp.open("GET", hUrl, false); try { objHttp.send(); //获取网站回应请求的日期时间。如: Wed, 08 Feb 2012 06:34:58 GMT datetxt = objHttp.getResponseHeader("Date"); //分割时间 date1 = datetxt.Split(','); } catch (Exception ex) { throw ex; } // if (date1 ==null ) { localtime = "网络验证失败,请重新启动或检查网络设置"; } else if (date1.Length < 1) { localtime = "网络验证失败,请重新启动或检查网络设置"; } else { //将时间中的GMT去掉 date2 = date1[1].Replace("GMT", ""); //如: 08 Feb 2012 06:34:58 GMT date3 = date2.Split(' '); //如: 08 Feb 2012 06:34:58 switch (date3[2]) { case "Jan": mon = "01"; break; case "Feb": mon = "02"; break; case "Mar": mon = "03"; break; case "Apr": mon = "04"; break; case "May": mon = "05"; break; case "Jun": mon = "06"; break; case "Jul": mon = "07"; break; case "Aug": mon = "08"; break; case "Sep": mon = "09"; break; case "Oct": mon = "10"; break; case "Nov": mon = "11"; break; case "Dec": mon = "12"; break; } //最终反馈是日期和时间 localtime = date3[3] + "/" + mon + "/" + date3[1] + " " + date3[4]; //获取的协调世界时 DateTime sTime = Convert.ToDateTime(localtime); //转换为当前计算机所处时区的时间,即东八区时间 nTime = TimeZone.CurrentTimeZone.ToLocalTime(sTime); } objHttp = null; return nTime; } #endregion #region 获取标准北京时间3,速度500ms-1500ms /// <summary> /// [3]获取标准北京时间2 ,读取(xml)http://www.time.ac.cn/timeflash.asp?user=flash /// </summary> /// <returns></returns> public DateTime GetStandardTime() { #region 文件格式 /// //<?xml version="1.0" encoding="GB2312" ?> //- <ntsc> //- <time> // <year>2011</year> // <month>7</month> // <day>10</day> // <Weekday /> // <hour>19</hour> // <minite>45</minite> // <second>37</second> // <Millisecond /> // </time> // </ntsc> #endregion DateTime dt; WebRequest wrt = null; WebResponse wrp = null; try { wrt = WebRequest.Create("http://www.time.ac.cn/timeflash.asp?user=flash"); wrt.Credentials = CredentialCache.DefaultCredentials; wrp = wrt.GetResponse(); StreamReader sr = new StreamReader(wrp.GetResponseStream(), Encoding.UTF8); string html = sr.ReadToEnd(); sr.Close(); wrp.Close(); //int yearIndex = html.IndexOf("<year>") ; //int secondIndex = html.IndexOf("</second>"); //html = html.Substring(yearIndex, secondIndex - yearIndex); html = html.Substring(51, 109); string[] s1 = html.Split(new char[2] { '<', '>' }); string year = s1[2]; string month = s1[6]; string day = s1[10]; string hour = s1[18]; string minite = s1[22]; string second = s1[26]; dt = DateTime.Parse(year + "-" + month + "-" + day + " " + hour + ":" + minite + ":" + second); } catch (WebException) { return DateTime.Parse("0001-1-1"); } catch (Exception) { return DateTime.Parse("0001-1-1"); } finally { if (wrp != null) wrp.Close(); if (wrt != null) wrt.Abort(); } return dt; } #endregion #region 访问标准校时服务器端口获取网络时间 速度:1000-2000ms /// <summary> /// 获取网络时间,通过标准校时服务器 /// </summary> /// <param name="HostName">主机名</param> /// <param name="PortNum">端口号</param> /// <returns>DateTime</returns> public DateTime GetInternetTime(string HostName, int PortNum) { DateTime official, localtime; string returndata = null; string[] dates = new string[4]; string[] times = new string[4]; string[] tokens = new string[11]; TcpClient tcpclient = new TcpClient(); try { tcpclient.Connect(HostName, PortNum); NetworkStream networkStream = tcpclient.GetStream(); if (networkStream.CanWrite && networkStream.CanRead) { Byte[] sendBytes = Encoding.ASCII.GetBytes("Hello"); networkStream.Write(sendBytes, 0, sendBytes.Length); byte[] bytes = new byte[tcpclient.ReceiveBufferSize]; networkStream.Read(bytes, 0, (int)tcpclient.ReceiveBufferSize); returndata = Encoding.ASCII.GetString(bytes); } tcpclient.Close(); } catch (Exception excep) { throw excep; } tokens = returndata.Split(' '); dates = tokens[1].Split('-'); times = tokens[2].Split(':'); official = new DateTime(Int32.Parse(dates[0]) + 2000, Int32.Parse(dates[1]), Int32.Parse(dates[2]), Int32.Parse(times[0]), Int32.Parse(times[1]), Int32.Parse(times[2])); localtime = TimeZone.CurrentTimeZone.ToLocalTime(official); return localtime; } #endregion } }
先说一下NetTime类,4个方法,
第一个方法GetBeijingTime()速度最快,100ms的反应时间,快得没话说,不过在跟其他校时网比较时间时,它的时间比别的校时网站时间要提前10s。
第二个方法GetNetTime(string Url),反应时间取决于你访问的网页,我这里用的是百度。通过查资料,百度的平均加载速度为:0.48s(2007年),而Google的加载速度为:0.48s(2007年0.87s)。这2个都可以,当然也可以用网易163或者其他的。推荐用这个。我用的Google,反应时间为200ms左右。完全可以满足你的一般需求。
第三个方法GetStandardTime(),网站的加载速度为0.55s。但是处理起来有点费时,反应时间在500-1500ms。
第四个方法GetInternetTime(string HostName,int PortNum),同样取决于授时服务器,我这里用的是time-a.timefreq.bldrdoc.gov,端口号为13,反应时间在1000-2000ms。
网络时间应用举例:
private void timer2_Tick(object sender, EventArgs e) { //速度100ms try { NetTime t = new NetTime(); string time = t.GetBeijingTime().ToString(); lblNetTime.Text = time; } catch (Exception ex) { lblNetTime.Text = ex.Message; } } private void timer3_Tick(object sender, EventArgs e) { //速度200ms try { NetTime t = new NetTime(); string time = t.GetNetTime("http://www.google.com.hk/").ToString(); label3.Text = time; } catch (Exception ex) { label3.Text = ex.Message ; } } private void timer4_Tick(object sender, EventArgs e) { //速度500-1500ms try { NetTime t = new NetTime(); string time = t.GetStandardTime().ToString(); label4.Text = time; } catch (Exception ex) { label4.Text = ex.Message; } } private void timer5_Tick(object sender, EventArgs e) { try { NetTime t = new NetTime(); string time = t.GetInternetTime("time-a.timefreq.bldrdoc.gov",13).ToString(); label5.Text = time; } catch (Exception ex) { label5.Text = ex.Message; } }
前面说系统和网络的双重验证,其实只要判断一下2者 在日期上不同,就用网络时间来纠正一下本地时间即可。
下面是系统时间类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace WebTime { /// <summary> /// 系统时间结构 /// </summary> public struct SystemTime { public ushort wyear; //年 public ushort wmonth; //月 public ushort wdayofweek; //星期 public ushort wday; //日 public ushort whour; //时 public ushort wminute; //分 public ushort wsecond; //秒 public ushort wmilliseconds; //毫秒 /// <summary> /// 从System.DateTime转换。 /// </summary> /// <param name="time">System.DateTime类型的时间。</param> public void fromDateTime(DateTime time) { wyear = (ushort)time.Year; wmonth = (ushort)time.Month; wdayofweek = (ushort)time.DayOfWeek; wday = (ushort)time.Day; whour = (ushort)time.Hour; wminute = (ushort)time.Minute; wsecond = (ushort)time.Second; wmilliseconds = (ushort)time.Millisecond; } /// <summary> /// 转换为system.DateTime类型。 /// </summary> /// <returns></returns> public DateTime toDateTime() { return new DateTime(wyear, wmonth, wday, whour, wminute, wsecond, wmilliseconds); } /// <summary> /// 静态方法。转换为system.DateTime类型。 /// </summary> /// <param name="time">systemtime类型的时间。</param> /// <returns></returns> public static DateTime toDateTime(SystemTime time) { return time.toDateTime(); } } /// <summary> /// 系统时间类 /// </summary> public class SysTime { [DllImport("Kernel32.dll ")] public static extern bool SetSystemTime(ref SystemTime SystemTime); [DllImport("Kernel32.dll ")] public static extern void GetSystemTime(ref SystemTime SystemTime); } }
其中定义一个系统时间的结构体类型和一个类。SetSystemTime是来设置系统时间,GetSystemTime用来获取系统时间。
下面是用网络时间来校对系统时间的源码:
private void btnCorretTime_Click(object sender, EventArgs e) { NetTime nt = new NetTime(); //获取网络时间 string time = nt.GetNetTime("http://www.google.com.hk/").ToString(); DateTime t=Convert.ToDateTime(time); //实例化一个系统时间结构体对象 SystemTime st = new SystemTime(); //将当前计算机所在时区时间转化为协调世界时 t = TimeZone.CurrentTimeZone.ToUniversalTime(t); st.fromDateTime(t); //修改系统时间 SysTime.SetSystemTime(ref st); MessageBox.Show("时间改为"+ st.toDateTime().ToString()); }
所以只需要给项目系统添加一个通过网络时间来校对系统时间功能,而其他代码不用改,还是用系统时间,这样也符合了开闭原则。
转载的朋友请说明出处:http://blog.csdn.net/xiaoxian8023/article/details/7250385