timezone. To avoid too large assemblies, I did not use any XML or JSON parsing
classes. If your application is using XML or JSON parsing, the use of those here
could reduce code size.
The code refreshes local time with NTP server in a configurable number of hours. I noticed that on my device with 10 hours the clock gets drifted in couple of seconds. Use 5-6 hours for refresh. This is the main reason I wrote this code... I did not implement a refresh when DST turns on or off. If anyone adds that, please share.
How it Works:
By quering geobytes.com and askGeo.com I am finding the actual time-zone of the device:
Through geobytes.com I am finding my location in the world
Through askGeo.com I am finding the timezone data of this location
Limitations:
This auto-detection will work if the device is connected to the internet through
standard NAT configuration, but with remote proxies the auto-detection will be
wrong as it will detect the timezone of the proxy itself. For most applications
however it is good enough.
Don't expect a GPS accuracy for your location. It will probably get you the
name of city/town where your ISP is, and not your actual location, still for
timezone purposes this good enough.
Please read http://www.askgeo.com Terms of Use and legal disclaimer
Special NOTE:
In this code an API Key that is registered to the author of this code.
>>>> Please modify it to your registered key <<<<
Search below for <<<< to location of where you need to put your key
You may use the code, modify or include in any project you have, posted in the hope it will be useful.
Please read http://www.askgeo.com Terms of Use and legal disclaimer
Please read http://www.geobytes.com Terms and Conditions
/* ===================================================================================== * NTP Protocol synchronization code * In this code an implementation of NTP client and auto-detection classes for actual * timezone. To avoid too large assemblies, I did not use any XML or JSON parsing * classes. If your application is using XML or JSON parsing, the use of those here * could reduce code size. * * How it Works: * By quering geobytes.com and askGeo.com I am finding the actual time-zone of the * device: * Through geobytes.com I am finding my location in the world * Through askGeo.com I am finding the timezone data of this location * Limitations: * This auto-detection will work if the device is connected to the internet through * standard NAT configuration, but with remote proxies the auto-detection will be * wrong as it will detect the timezone of the proxy itself. For most applications * however it is good enough. * * Don't expect a GPS accuracy for your location. It will probably get you the * name of city/town where your ISP is, and not your actual location, still for * timezone purposes this good enough. * * Please read http://www.askgeo.com Terms of Use and legal disclaimer * * Special NOTE: * In this code an API Key that is registered to the author of this code. * >>>> Please modify it to your registered key <<<< * Search below for <<<< to location of where you need to put your key * ===================================================================================== * By: Zakie Mashiah * You may use the code, modify or include in any project you have. * Please read http://www.askgeo.com Terms of Use and legal disclaimer * Please read http://www.geobytes.com Terms and Conditions * ===================================================================================== */ using System; using System.Threading; using System.IO; using System.Text; using System.Net; using System.Net.Sockets; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using Microsoft.SPOT.Net; using Microsoft.SPOT.Time; using SecretLabs.NETMF; using SecretLabs.NETMF.Hardware; namespace NTPThread { public class GeoLoactionByIP { public string country; public string region; public string city; public string latitude; public string longitude; public string timezone; public int timeZoneMinutesGMT; // How many minutes from GMT public string externalIPAddress; private string queryString { get { return "http://www.geobytes.com/IpLocator.htm?GetLocation&Template=XML.txt"; } } /// /// Converts the internal string member of timezone to internal member of minutes from GMT /// private void _parseTimeZoneString() { int sign; int hour; int minute; sign = timezone[0] == '+' ? 1 : -1; hour = (timezone[1] - '0') * 10 + (timezone[2] - '0'); minute = (timezone[4] - '0') * 10 + (timezone[5] - '0'); timeZoneMinutesGMT = sign * ((hour * 60) + minute); } /// /// Read an attribute in the XML string until the end of the attribute /// /// String with the reply XML and starting with the value /// The sub-string until end of parameter denotion of XML private string _readAttribute(string parser, bool isString = true) { int start, end; string rv = null; string de = (isString) ? "\"" : ","; // Data ends at string ds = (isString) ? "\":\"" : "\":"; // Data starts at start = parser.IndexOf(ds); if (start > 0) { start += ds.Length; end = parser.IndexOf(de, start); if (end > 0) { rv = parser.Substring(start, end - start); } } return rv; } /// /// Reads the stream of reply from the geobytes and parses it into the variables /// /// the stream of the reply private void _readStream(Stream s) { System.Threading.Thread.Sleep(1000); // Wait a while StreamReader sr = new StreamReader(s); string parser = sr.ReadToEnd(); int location = parser.IndexOf("country\":\""); if (location > 0) { parser = parser.Substring(location); this.country = _readAttribute(parser); location = parser.IndexOf("region\":\""); if (location > 0) { parser = parser.Substring(location); this.region = _readAttribute(parser); location = parser.IndexOf("city\":\""); if (location > 0) { parser = parser.Substring(location); this.city = _readAttribute(parser); location = parser.IndexOf("latitude\":"); if (location > 0) { parser = parser.Substring(location); this.latitude = _readAttribute(parser, false); location = parser.IndexOf("longitude\":"); if (location > 0) { parser = parser.Substring(location); this.longitude = _readAttribute(parser, false); location = parser.IndexOf("timezone\":\""); if (location > 0) { parser = parser.Substring(location); this.timezone = _readAttribute(parser); location = parser.IndexOf("ipaddress\":"); if (location > 0) { parser = parser.Substring(location); this.externalIPAddress = _readAttribute(parser); // Now parse the timezone string to minutes _parseTimeZoneString(); } } } } } } } } /// /// Queries the Geobytes service for location of the IP address. /// /// public void queryLocation(string ip = null) { string q = (ip == null) ? queryString : queryString + ip; HttpWebRequest request = HttpWebRequest.Create(q) as HttpWebRequest; WebResponse resp = null; try { resp = request.GetResponse(); } catch (Exception e) { Debug.Print("ERROR: Exception in HttpWebRequest.GetResponse() for geobytes.com" + e.ToString()); } if (resp != null) { Stream respStream = resp.GetResponseStream(); respStream.ReadTimeout = 5000; _readStream(respStream); resp.Close(); } request.Dispose(); } public override string ToString() { return "Country=\t" + country + "\n\tRegion=\t" + region + "\n\tCity=\t" + city + "\n\tLatitude=\t" + latitude + "\n\tLongitude=\t" + longitude + "\n\tTimezone=\t" + timezone + "\n\tMinutesFromGMT=\t" + timeZoneMinutesGMT + "\n\tExternalIP=\t" + externalIPAddress; } } public class AskGeoService { public long currentOffserMS; private const string accountID = ""; // <<<< Put up your account ID, PLEASE! private const string apiKey = ""; // <<<< Put here your API key, PLEASE! private string queryString(string latitude, string longitude) { return "http://www.askgeo.com/api/" + accountID + "/" + apiKey + "/timezone.json?points=" + latitude + "%2C" + longitude; } public void queryLocation(string latitude, string longitude) { string q = queryString(latitude, longitude); HttpWebRequest request = HttpWebRequest.Create(q) as HttpWebRequest; WebResponse resp = null; try { resp = request.GetResponse(); } catch (Exception e) { Debug.Print("ERROR: Exception in HttpWebRequest.GetResponse() for askgeo.com" + e.ToString()); } if (resp != null) { Stream respStream = resp.GetResponseStream(); respStream.ReadTimeout = 5000; System.Threading.Thread.Sleep(1000); // Wait a while StreamReader sr = new StreamReader(respStream); string parser = sr.ReadToEnd(); int location; location = parser.IndexOf("currentOffsetMs\":"); if (location > 0) { parser = parser.Substring(location); int start = parser.IndexOf(':') + 1; location = parser.IndexOf(','); string currentOffsetMsString = parser.Substring(start, location - start); currentOffserMS = Convert.ToInt32(currentOffsetMsString); } resp.Close(); } request.Dispose(); } } public class NetworkTimeProtocolThread { #region Variables private static int sleepTime = 60 * 10; // Every 10 hours private static string ntpName = "time-a.nist.gov"; private static int minutesFromGMT; #endregion #region PrivateFunctions private static void SleepMinutes() { int times; times = sleepTime == 0 ? 1 : sleepTime; for (int i = 0; i < times; i++) { Debug.Print(DateTime.Now.ToString() + " NTP Thread:: " + i.ToString() + " of " + times.ToString()); Thread.Sleep(60 * 1000); // Minute } } private static void SetSystemTime(DateTime dt) { // Simulate timezone and DST of 3 hours (life is good in Tel-Aviv :-) TimeSpan timezoneOffset = TimeSpan.FromTicks(minutesFromGMT * TimeSpan.TicksPerMinute); dt += timezoneOffset; Debug.Print("\tSetting time to:\t" + dt.ToString()); Utility.SetLocalTime(dt); Debug.Print("\tSystem Time is :\t" + DateTime.Now.ToString()); } #endregion #region PublicFunctions public static void SetRefreshRate(int hoursForRefresh) { sleepTime = hoursForRefresh * 60; } public static void SetNTPHost(string host) { ntpName = host; } public static int GetTimeZoneOffset() { return minutesFromGMT; } public static void SetTimeZoneOffset(int minutes_from_GMT) { minutesFromGMT = minutes_from_GMT; } public static DateTime GetNetworkTime() { DateTime dt = new DateTime(1900, 1, 1, 1, 1, 1); IPHostEntry ntpServer = Dns.GetHostEntry(ntpName); if (ntpServer == null) { Debug.Print("ERROR: Can't find DNS entry for " + ntpName); return dt; } if (ntpServer.AddressList == null) { Debug.Print("ERROR: No addresses found for NTP server " + ntpName); return dt; } IPEndPoint ep = new IPEndPoint(Dns.GetHostEntry(ntpName).AddressList[0], 123); Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); s.Connect(ep); byte[] ntpData = new byte[48]; // RFC 2030 ntpData[0] = 0x1B; for (int i = 1; i < 48; i++) ntpData[i] = 0; s.Send(ntpData); s.Receive(ntpData); byte offsetTransmitTime = 40; ulong intpart = 0; ulong fractpart = 0; for (int i = 0; i <= 3; i++) intpart = 256 * intpart + ntpData[offsetTransmitTime + i]; for (int i = 4; i <= 7; i++) fractpart = 256 * fractpart + ntpData[offsetTransmitTime + i]; ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L); s.Close(); TimeSpan timeSpan = TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond); DateTime dateTime = new DateTime(1900, 1, 1); dateTime += timeSpan; TimeSpan offsetAmount = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime); DateTime networkDateTime = (dateTime + offsetAmount); Debug.Print(" NetworkDateTime: " + networkDateTime.ToString()); return networkDateTime; } #region NTPThreadRunner public static void Run() { Debug.Print("Network Time Protocol Thread starting...\n\tServer\t" + ntpName + "\n\tevery\t" + sleepTime.ToString() + " hours"); while (true) { Debug.Print("NTP Thread:: Checking NTP server time. Time before: " + DateTime.Now.ToString()); DateTime dt = GetNetworkTime(); SetSystemTime(dt); SleepMinutes(); } } #endregion #endregion } // End of NTP Thread class }
Test Code:
using System; using System.IO; using System.Text; using System.Threading; using System.Net; using System.Net.Sockets; using System.Collections; using Microsoft.SPOT; using Microsoft.SPOT.IO; using Microsoft.SPOT.Hardware; using SecretLabs.NETMF; using SecretLabs.NETMF.Hardware.NetduinoPlus; using NTPThread; /// This program demonstrates how to use the .NET Micro Framework HTTP classes /// to create a simple HTTP client that retrieves pages from several different /// websites, including secure sites. namespace NTPTestProgram { public static class NTPTestProgram { #region Variables static DateTime startTime; #endregion // Variables #region Main /// /// Retrieves weather current and forecast information from Google Weather and display /// it on the LCD with time ticking clock /// public static void Main() { DateTime dt; int delay; // Initialization InitNetwork(); InitLocation(); InitRealTimeClock(); // Loop (forever) for (int minute = 0; ; minute += 1000) { if (minute > (60 * 1000)) minute = 0; dt = DateTime.Now; ShowTime(); TimeSpan ts = DateTime.Now - dt; delay = 1000 - ts.Milliseconds; if (delay > 0) Thread.Sleep(delay); else Debug.Print("Total processing time was: " + ts.Milliseconds.ToString()); } } #endregion #region InitFunctions public static void InitNetwork() { } // Would need to populate if using a Ethernet Shield public static void InitRealTimeClock() { NetworkTimeProtocolThread.SetRefreshRate(5); new Thread(NetworkTimeProtocolThread.Run).Start(); Thread.Sleep(1000); // Wait a second for NTP to push system time startTime = DateTime.Now; Debug.Print("Starting at: \t[" + startTime.ToString() + "]"); } public static void InitLocation() { GeoLoactionByIP myLocation = new GeoLoactionByIP(); AskGeoService askGeo = new AskGeoService(); myLocation.queryLocation(); Debug.Print("Location:\n\t" + myLocation.ToString() ); askGeo.queryLocation(myLocation.latitude, myLocation.longitude); int minutes = (int)(askGeo.currentOffserMS / 1000 / 60); Debug.Print("\tCurrent timezone offset=" + minutes + " minutes"); NetworkTimeProtocolThread.SetTimeZoneOffset(minutes); } #endregion #region UtilityFunctions private static string DecimalTwoPos(int d) { if (d < 10) return "0" + d.ToString(); else return d.ToString(); } #endregion #region DisplayFunctions private static void ShowTime() { DateTime dt = DateTime.Now; string s = DecimalTwoPos(dt.Hour) + ":" + DecimalTwoPos(dt.Minute)+ ":" + DecimalTwoPos(dt.Second); Debug.Print(s); } #endregion } }