Well, here's the code in PUB HumanTime in SNTP 2.01:
PUB HumanTime(Offset,TimeStampAddress)|i,Seconds,Days,Years,LYrs,DW,DD,HH,MM,SS,Month,Date,Year
Seconds := long[TimeStampAddress] + Offset * 3600
Days := ((Seconds >>= 7)/675) + 1 '<- Days since Jan 1, 1900 ... divide by 86,400 (sec/day) and add 1
So if we get a seconds-since time from the NTP server, and there are 86,400 seconds per day... The Days := line seems to say in the comments that they are dividing the Seconds by 86,400 - but the actual line is ((Seconds >>= 7)/675) + 1
Are those two operations functionally equivalent?
Seconds >>= 7 should be the same as dividing Seconds by 128, am I correct in that understanding?
Then further dividing that result by 675 is supposed to result in the Days count. Yes?
That's the code to which I refer. Take a look at SNTP 2.01 above which is where this all comes from...
So when I replace the ((Seconds >>= 7)/675) + 1 with (Seconds / 86400) + 1 - I do not get the same results at all.
Seconds >>= 7 should be the same as dividing Seconds by 128, am I correct in that understanding?
Yes.
Then further dividing that result by 675 is supposed to result in the Days count. Yes?
Yes.
So when I replace the ((Seconds >>= 7)/675) + 1 with (Seconds / 86400) + 1 - I do not get the same results at all.
I have a sneaky suspicion that if I answered that, I would be wrong. But, I think it's because the initial value in seconds is too large. That's why Seconds >>=7 is important. It shifts the bits, which clears a few of them on the left. I don't understand it well enough to give you a factual explanation. Maybe someone else will chime in.
As an aside, the maximum date and time NTP supports is:
Date/time: 2038, jan 19, 03:14:07
ntp seconds: 4356472447.
binary: % 1_0000_0011_1010_1010_0111_1110_0111_1111 (33 bits)
The last second of today
Date/time: 2013, dec 9, 23:59:59
ntp seconds: 3595622399
binary: % 1101_0110_0101_0000_1101_0111_1111_1111 (32 bits)
Oddly enough, the Last Second of Today NTP value of ntp seconds: 3595622399, with my zone set to GMT (0) yields a time of 12/09/2013 02:00:22 through my HumanTime method.
How are you arriving at those NTP time values? It would be handy for me to know - unless you're just multiplying out... (years * 31,536,000 secs per year) + (days this year * 86,400 secs per day) + (hours today * 3600) + (mins * 60) + seconds ...???
3,563,568,000 + 29,548,800 + 75,600 + 360 + 45 = 3,593,192,805 is what I calculate for 2013/12/09 21:06:45
Is that right?
Of course, then I need to look at years/4 to get leap years and add that to days... so 113/4 = 28 leap days, x 84,600 secs/day = 2,419,200
Then I get 3,595,612,005
But plugging that into my HumanTime method yields a time of 2013/12/09 02:14/12
I'm utterly confused...
On the bright side, commenting out my "replacement Seconds" value and taking in the value from the NTP server IS yielding the correct time and date... I just don't trust it to remain correct now.......
I cheat. http://www.aelius.com/njh/unixtime/ It hasn't led me wrong yet. The numbers I give you are actually from my humantime method. But I bounce them with this link just to double check.
Then I get 3,595,612,005
This is correct. It's the NTP value when it's 2013/12/09 21:06:45 GMT ... NTP time is always spitting out the GMT time. Always and forever. Or at least until sometime in 2038
But plugging that into my HumanTime method yields a time of 2013/12/09 02:14/12
That's because it doesn't work correctly. (Blah hahaha!!! Sorry, I couldn't resist. that was waaaaaay too easy to pass up.)
P.S. 1900 was not a leap year. I'm not sure how/if that affects your humantime.
LOL... yeah. OK, so my HumanTime doesn't work correctly. Unless I take in the actual NTP value from the server and then it IS giving me the right time.
So.... I replace the NTP TransmitTimestamp with the calculated (and verified) value, and it gives me the wrong time. But I let the NTP value go through without replacing it and it's correct.
I think I've had it for tonight. Maybe a beer will help me figure it out! LOL
Thanks for your help so far, it's been tremendously valuable in helping me to figure out what's going on. I understand a LOT more about the whole thing than I did this morning, so that's success in itself.
My main issue now is that while I understand a good chunk of what's going on, AND I have the time stamp function *seemingly* working - I am getting a good time and date - I don't trust it now. I'm hesitant to deploy what I've found because I am pretty sure it will fail at some future date - due to the fact that there's apparently SOME thing remaining that I just am not seeing/understanding (like why the passed value displays proper time/date, but the calculated value does not).
My main issue now is that while I understand a good chunk of what's going on, AND I have the time stamp function *seemingly* working - I am getting a good time and date - I don't trust it now. I'm hesitant to deploy what I've found because I am pretty sure it will fail at some future date - due to the fact that there's apparently SOME thing remaining that I just am not seeing/understanding (like why the passed value displays proper time/date, but the calculated value does not).
That's why unit tests are a good idea...
I live by unit test as they validate code, check for edge cases like 2/29/2012, and clearly show intent. Anyway, there also an excel workbook attached used to get the math right and validate the SPIN logic.
The idea behind ((Seconds >>= 7)/675) is to shift bit 31 to the right. Bit 31 determine if the 32 bit value is negative or positive.
It's just December. For all dates in the month of December, I need to subtract 1 from "Years" in HumanTime. Once it's January, that -1 yields the wrong result. Removing it then gives the correct date.
This is fascinating, although frustrating. I'm trying to visualize what's happening at the byte level that makes December "Special". It's given me a great level of insight into the workings of SNTP!
PUB HumanTime(Offset,TimeStampAddress)|i,Seconds,Days,Years,LYrs,DW,DD,HH,MM,SS,Month,Date,Year
Seconds := (long[TimeStampAddress] + (Offset * 3600)) 'Offset = Zone. 3600 secs per hr.
'Seconds := (864000000 * 4) + (31536000 * 5) '07/07/2013 20:57:02
'Seconds := 3595622399 ' 2013/12/09, 23:59:59
'Seconds := 3595612005 ' calculated as 2013/12/09 21:06:45 - but reads 2013/12/09 02:14/12 here.......
Seconds := 3597618600
Days := ((Seconds >>= 7)/675) + 1 '<- Days since Jan 1, 1900 ... divide by 86,400 (sec/day) and add 1
' 31,536,000 secs/yr
DW := (Days-1)//7+1 ' DW := (Days-1)//7+1 'DW := (Days-1) // 7
Years := (Days / 365) '- 1 ' Number of Days THIS year and ''*** Added the " - 1 " and date is good... as long as it's December.
....
That seems to have worked. Taking a live NTP stamp, it's yielding the correct time and date. Here's the edited HumanTime method with your observations included (and previous stuff commented):
PUB HumanTime(Offset,TimeStampAddress)|i,Seconds,Days,Years,LYrs,DW,DD,HH,MM,SS,Month,Date,Year
Seconds := (long[TimeStampAddress] + (Offset * 3600)) 'Offset = Zone. 3600 secs per hr.
'Seconds := 3595612005 ' calculated as 2013/12/09 21:06:45 - but reads 2013/12/09 02:14/12 here.......
Days := ((Seconds >>= 7)/675) + 1 '<- Days since Jan 1, 1900 ... divide by 86,400 (sec/day) and add 1
' 31,536,000 secs/yr
LYrs := Days/1460
Days -= LYrs
Years := Days/365
DW := (Days-1)//7+1 ' DW := (Days-1)//7+1 'DW := (Days-1) // 7
'Years := (Days / 365) - 1 ' Number of Days THIS year and ''*** Added the " - 1 " and date is good...
'Days -= (Years * 365) ' number of years since.
'LYrs := Years / 4 '<- Leap years since 1900 2013 (113) and 2014 (114) both yield 28 (decimal truncated)
Year := Years + 1900 '<- Current Year
'Days -= LYrs '<- Leap year Days correction
' for THIS year
' Days is now the Julian Date - actual number of days so far this year.
repeat
repeat i from 1 to 12 '<- Calculate number of days
Month := 30 ' in each month. Stop if
if i&1 <> (i&8)>>3 ' Month has been reached
Month += 1
if i == 2
Month := 28
if Days =< Month '<- When done, Days will contain
quit ' the number of days so far this
if Days > Month ' month. In other words, the Date.
Days -= Month
{
if Days > Month '<- When done, Days will contain
Days -= Month ' the number of days so far this
if Days =< Month ' month. In other words, the Date.
quit
}
until Days =< Month
Month := i '<- Current Month
Date := Days '<- Current Date
SS := long[TimeStampAddress] + Offset * 3600
SS := SS -(((Years*365)*675)<<7) '<- seconds this year
MM := SS / 60 '<- minutes this year
SS := SS - (MM * 60) '<- current seconds
HH := MM / 60 '<- hours this year
MM := MM - (HH * 60) '<- current minutes
DD := HH / 24 '<- days this year
HH := HH - (DD * 24) '<- current hour
DD -= LYrs '<- Leap year Days correction
' for THIS year
long[TimeStampAddress][2] := Month<<24+Date<<16+Year 'Giving bad date
long[TimeStampAddress][3] := DW<<24+HH<<16+MM<<8+SS 'Giving good time
' DD is redundant but I included it for completion...
' If you subtract the number of days so far this year from
' DD and add one, you should get today's date. This is calculated
' from another angle above from Days
Now I just need to test it to see what it does for a date in 2014....
There's two things that I see...
1) ((Seconds >>= 7)/675) is the number of day since 1/1/1900 minus the fractional part of the current day. Another way to say this is, the timestamp is whole days plus a fraction of a day. The binary shift and division truncate the fraction part of the last day.((Seconds >>= 7)/675) + 1 adds a day (86400 secs) to the SNTP timestamp minus the faction of a day.
2) There have been 28 leap years since 1/1/1900. You're probably not accounting for leaps years in the equation Years := (Days / 365) '- 1.
Taking Item 1 and 2 into account and realizing there have been 28 leap years since 1/1/1900, it seems to reason why the date messed up around 12/3/2013 of this year. After all the number in day in December (31)-3 = 28.
Dave- Nope, the code you just posted doesn't work. day of week is wrong and also the day of the month. ntp 3913055999 should be 2023, dec 31, 23:59:59 and it's coming up with dec 30. day of week says 4, but it should be 1.
The critical flaw with this approach is that we're taking a definitive value (the ntp seconds value) starting to manipulate it using numbers that aren't consistent conversions. For instance, 1460 sounds great, because we have 1 leap day every 1460 days. But that day occurs a strange time of the year. Any division by that number is only valid when you do something with the remainder... because it's significant. Worse yet, if calculations are ran before the leap day of a leap year, we do X with the remainder. Otherwise, we have to do Y with it. The problem is we don't know where we are in time until we figure it out. And to figure it out, we need... oh yeah. My head hurts now.
Try to follow through my code. It works because it takes everything 1 step at a time and rolls values up or down as it goes.
I've got my units all on manual time right now, and that should suffice for the next month or so. I'm going to wait until AFTER the 1/1/2014 transition to see how these guys perform then. I've got code here on my dev system that seems to be working even with "manually substituted" NTP time values into 2014 and beyond, but I'm just very distrustful of implementing any of these just yet - and that only because while I understand a great deal of the code now, there are still gray areas I need to work to understand, and so there's still the "magic" factor in there for me :-)
Right now I'm just in a holding pattern and onto another project while I digest what I've learned so far. I think when I return to it in January, fresh, it'll probably seem more obvious.
Thanks again for all your help & observations. And especially for letting me know I wasn't the only one seeing the issue!!!! :-)
Comments
So if we get a seconds-since time from the NTP server, and there are 86,400 seconds per day... The Days := line seems to say in the comments that they are dividing the Seconds by 86,400 - but the actual line is ((Seconds >>= 7)/675) + 1
Are those two operations functionally equivalent?
Seconds >>= 7 should be the same as dividing Seconds by 128, am I correct in that understanding?
Then further dividing that result by 675 is supposed to result in the Days count. Yes?
That's the code to which I refer. Take a look at SNTP 2.01 above which is where this all comes from...
So when I replace the ((Seconds >>= 7)/675) + 1 with (Seconds / 86400) + 1 - I do not get the same results at all.
Dave
Yes.
Yes.
I have a sneaky suspicion that if I answered that, I would be wrong.
As an aside, the maximum date and time NTP supports is:
Date/time: 2038, jan 19, 03:14:07
ntp seconds: 4356472447.
binary: % 1_0000_0011_1010_1010_0111_1110_0111_1111 (33 bits)
The last second of today
Date/time: 2013, dec 9, 23:59:59
ntp seconds: 3595622399
binary: % 1101_0110_0101_0000_1101_0111_1111_1111 (32 bits)
How are you arriving at those NTP time values? It would be handy for me to know - unless you're just multiplying out... (years * 31,536,000 secs per year) + (days this year * 86,400 secs per day) + (hours today * 3600) + (mins * 60) + seconds ...???
3,563,568,000 + 29,548,800 + 75,600 + 360 + 45 = 3,593,192,805 is what I calculate for 2013/12/09 21:06:45
Is that right?
Of course, then I need to look at years/4 to get leap years and add that to days... so 113/4 = 28 leap days, x 84,600 secs/day = 2,419,200
Then I get 3,595,612,005
But plugging that into my HumanTime method yields a time of 2013/12/09 02:14/12
I'm utterly confused...
On the bright side, commenting out my "replacement Seconds" value and taking in the value from the NTP server IS yielding the correct time and date... I just don't trust it to remain correct now.......
This is correct. It's the NTP value when it's 2013/12/09 21:06:45 GMT ... NTP time is always spitting out the GMT time. Always and forever. Or at least until sometime in 2038
That's because it doesn't work correctly. (Blah hahaha!!! Sorry, I couldn't resist. that was waaaaaay too easy to pass up.)
P.S. 1900 was not a leap year. I'm not sure how/if that affects your humantime.
So.... I replace the NTP TransmitTimestamp with the calculated (and verified) value, and it gives me the wrong time. But I let the NTP value go through without replacing it and it's correct.
I think I've had it for tonight. Maybe a beer will help me figure it out! LOL
Thanks for your help so far, it's been tremendously valuable in helping me to figure out what's going on. I understand a LOT more about the whole thing than I did this morning, so that's success in itself.
My main issue now is that while I understand a good chunk of what's going on, AND I have the time stamp function *seemingly* working - I am getting a good time and date - I don't trust it now. I'm hesitant to deploy what I've found because I am pretty sure it will fail at some future date - due to the fact that there's apparently SOME thing remaining that I just am not seeing/understanding (like why the passed value displays proper time/date, but the calculated value does not).
Thanks for the link. More playtime tomorrow.
Dave
I live by unit test as they validate code, check for edge cases like 2/29/2012, and clearly show intent. Anyway, there also an excel workbook attached used to get the math right and validate the SPIN logic.
The idea behind ((Seconds >>= 7)/675) is to shift bit 31 to the right. Bit 31 determine if the 32 bit value is negative or positive.
This is fascinating, although frustrating. I'm trying to visualize what's happening at the byte level that makes December "Special". It's given me a great level of insight into the workings of SNTP!
Right off the first equation, (Seconds >>=7) = 28090718
Then / 675 = 41615
and
+1 = 41616
... then 41616 / 365 = 114 years, which is incorrect.
but if you do this: 41616 / 1460 where 1460 is the number of days to make 1 leap day, you get 28. note:1460 = 365*4years
Now 41616 - 28 leap days = 41588 .... and 41588 / 365 = 113 years.
That help any?
Now I just need to test it to see what it does for a date in 2014....
THANKS!
1) ((Seconds >>= 7)/675) is the number of day since 1/1/1900 minus the fractional part of the current day. Another way to say this is, the timestamp is whole days plus a fraction of a day. The binary shift and division truncate the fraction part of the last day.((Seconds >>= 7)/675) + 1 adds a day (86400 secs) to the SNTP timestamp minus the faction of a day.
2) There have been 28 leap years since 1/1/1900. You're probably not accounting for leaps years in the equation Years := (Days / 365) '- 1.
Taking Item 1 and 2 into account and realizing there have been 28 leap years since 1/1/1900, it seems to reason why the date messed up around 12/3/2013 of this year. After all the number in day in December (31)-3 = 28.
The critical flaw with this approach is that we're taking a definitive value (the ntp seconds value) starting to manipulate it using numbers that aren't consistent conversions. For instance, 1460 sounds great, because we have 1 leap day every 1460 days. But that day occurs a strange time of the year. Any division by that number is only valid when you do something with the remainder... because it's significant. Worse yet, if calculations are ran before the leap day of a leap year, we do X with the remainder. Otherwise, we have to do Y with it. The problem is we don't know where we are in time until we figure it out. And to figure it out, we need... oh yeah. My head hurts now.
Try to follow through my code. It works because it takes everything 1 step at a time and rolls values up or down as it goes.
http://174.97.235.77:5000/time.xml
EST time, btw...
Right now I'm just in a holding pattern and onto another project while I digest what I've learned so far. I think when I return to it in January, fresh, it'll probably seem more obvious.
Thanks again for all your help & observations. And especially for letting me know I wasn't the only one seeing the issue!!!! :-)