Snippet - FixedTimeService - working replacement for TimeService with DST

I just posted FixedTimeService - working replacement for TimeService with DST on Codeshare. Feel free to discuss and make suggestions here.

5 Likes

This is good. I have my own time service with DST that is deliberately based on the DAYTIME protocol instead of the NTP Protocol. I have actually tested it through the last few DST changes. I actually think using DAYTIME is better because the service actually tells you the DST status so you don’t need to maintain a region specific rule. My concern with a DST rule is that DST changes rather frequently (a relative term). Not long ago Indiana switched from not participating in DST to being EST/EDT. Now in Indiana many people think it was a mistake and should have been CST. I can easily see this changing; and Congress could change the start and end dates. It’s a complete disaster, but at least the DAYTIME protocol would always work…

According to this tz database - Wikipedia, changes are made several time a year to the timezone database.

Then I saw that NIST was phasing out the DAYTIME protocol over the next 2 years. So it’s back to NTP. My next concern is, what happens if NIST starts to require HTTPS for NTP or passcodes to cut down on the DoS attacks. I am concerned that you could create a device that, a few years after launch, no longer keeps time properly…

Having said all that. I think NTP is the right choice at the moment and this implementation looks good. I’ll give it a try soon.

P.S. In my opinion, the best solution is a NIST run web service that takes a location (GPS or city) and returns current time with an indication of the next time change. But hey, I don’t run these things…

I have to agree with you: If possible, push the location/timezone/DST calculations to the server. That is really the only way to automatically figure out the timezone & DST rules. I didn’t know about DAYTIME, but if it is gone in 2 years - it is not a long-term solution.

If there is a free, long-term solution for fetching that info, I’d gladly add it.

If I ran things, DST would go away.

@ eolson - Unfortunately, the only free service that you can rely on (long-term) for time is NIST (IMHO), but as you now know, they have no facility for DST correction other than the DAYTIME protocol, but even that is only U.S. DST.

What you have implemented is the best that can be done… being NTP protocol and configurable DST calculation.

But, I will point out one thing. You state the you get the time twice because it only works about 5% of the time otherwise. I’m rusty on this, but I think you have to send a byte of something in the message body for the request to work the first time. Calling NTP twice in rapid succession is very bad because NIST has rules for how frequently you can hit their servers. One rule is that you can perform a request no more frequently than one per 5 seconds. If you violate their rules, their servers may start blocking your address! This is to prevent DoS attacks. In my mIP TCP stack, I have a UDP sample that does an NTP call and that works the first time (I think). Here is the code snippit… it may be useful:

// Create a NTP (date/time) Request Message
            var msg = new byte[48];
            msg[0] = 0x1b;

            // Let's get the UTC time from a time zerver using a UDP Message
            UDP.SendUDPMessage(msg, new byte[4] { 0x40, 0x5a, 0xb6, 0x37 }, 123, 123);  // 64.90.182.55 the address of a NIST time server

Thanks. That is the same UDP message that I’m sending in the code. I tried a number of different pieces of code with the same results. I’m not sure why I have the trouble.

  • I don’t have the trouble when checking a NTP server on my local network
  • I have an old router (not my gateway) that can’t seem to sync it’s time with an external NTP server
    So my best guess is that either my router (Cisco/Linksys) or Comcast’s router is not routing the first request back to me (response port not in the routing table yet). On the second request, the response port is now set-up in the routing table - to be able to route it back to the board. Or I’m hitting a firewall algorithm corner-case (I have some buddies at LInksys that I should ask…)

However, because of your comment. I am going to restructure my code so that it does an initial request to the primary then secondary servers (5 second timeout each) . It those fail, it will try again (more than 5 seconds later, so it does not look like a DoS attack.
This worked perfectly for me & will work fine for anyone without router issues. (I always fail on the initial primary and initial secondary queries, but succeed on second primary request) And… since I use 'Poll", this code does not block other threads while it is waiting for the 5 second timeout.

This is brilliant, exactly what I need. But I am not sure how to use this in the most stable way?

Examples or guidelines?

It was meant to be a direct replacement for the TimeService class.
Check the Microsoft documentation for the Method details.
If you can use the TimeService (e.g. have Microsoft.SPOT.Time), then
just replace it with this class. I have this code in my ProgramStarted block:


// setup callbacks
FixedTimeService.SystemTimeChanged += new SystemTimeChangedEventHandler(TimeService_SystemTimeChanged);
FixedTimeService.TimeSyncFailed += new TimeSyncFailedEventHandler(TimeService_TimeSyncFailed);
// New event: called when after we check the time (even if we don't end up changing the time)
FixedTimeService.SystemTimeChecked += new SystemTimeChangedEventHandler(TimeService_SystemTimeChecked); 

// start thread to init TimeService
initTimeserviceThread = new Thread(() => InitTimeservice());
initTimeserviceThread.Start();

Then I define this method that the thread will run


void InitTimeservice()
{
    Thread.Sleep(1);  // let ProgramStarted finish before doing this
    FixedTimeService.Settings = new TimeServiceSettings();

    // wait for internet connection
    while (IPAddress.GetDefaultLocalAddress() == IPAddress.Any)
            Thread.Sleep(500);

    // configure TimeServiceSettings
    tsSettings.AutoDayLightSavings = true;
    tsSettings.ForceSyncAtWakeUp = true;
    // "nist1-sj.ustiming.org,us.pool.ntp.org,clock.tricity.wsu.edu,clock-1.cs.cmu.edu,time-a.nist.gov"
    tsSettings.PrimaryServer = Dns.GetHostEntry("nist1-sj.ustiming.org").AddressList[0].GetAddressBytes(); 
    tsSettings.AlternateServer = Dns.GetHostEntry("pool.ntp.org").AddressList[0].GetAddressBytes();
    tsSettings.RefreshTime = 24 * 60 * 60; // once a day
 
    // configure & start FixedTimeService
    FixedTimeService.Settings = tsSettings;
    FixedTimeService.SetTimeZoneOffset(-8 * 60); /// PST
    FixedTimeService.SetDst("Mar Sun>=8 @ 2", "Nov Sun>=1 @ 2", 60); // US DST
    FixedTimeService.Start();
}

On my Panda II, I don’t have Microsoft.SPOT.Time, so I just made a TimeServiceSettings class to hold the values. Just a few other minor changes that I would have to check for.

I added a new callback “SystemTimeChecked” to version 3 of the code. It is called when the time is checked (RefreshTime), not just when the time gets changed. (In my App, I need to update the time on an external device every day or so.)