Files
Seth a6b3f039d8 Initial commit — Seth Calendar & Decimal Time clock site
Pages: /, /simple, /decimal, /seth, /calendar, /astro, /convert, /timegov
Features: Seth Calendar (10×36 + holidays), decimal time, moon phases,
astronomy (sun/moon), bidirectional time converter, Seth date display,
leap day split cell in calendar grid.
2026-03-08 22:32:38 +00:00

371 lines
14 KiB
JavaScript

timeDotGov = {};
timeDotGov.clock = {};
timeDotGov.clockController = {};
timeDotGov.clockController.dsClock = {}
timeDotGov.data = {
"then": null,
"serverTime": null,
"responseTime": null,
"RThalf": null,
"realTimeDif": null,
"requestTime": null,
"leapsecond": null,
"dststart": null,
"dstend": null,
"currYear": null,
"dstDates": null,
"leapDate": null,
"clockinstances": [],
"leapFlag": null,
"leapsec60": null,
"myhour0": "0",
"myhour1": "0",
"mysec0": "0",
"mysec1": "0",
"currentTime": null,
"twentyFour": function() {
twentyFour = document.getElementById("twenty-four");
return twentyFour.checked;
}
}
var offsetCheck = true;
var daylightTitles = false;
// CREATE ARRAY THAT WILL STORE WHICH CLOCK NUMS HAVE BEEN UPDATED
var dstClocksUpdated = [];
timeDotGov.clockController.getParameterByName = function(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
timeDotGov.clockController.getleapdate = function() {
var leap = new Date();
var temp1 = timeDotGov.data.leapdate.split(" ");
var leapdateYear = temp1[0];
var leapdateMonth = temp1[1];
leapdateMonth = leapdateMonth - 1; // month convention = 0-11 (Jan = 0)
var leapdateDay = temp1[2];
leap.setUTCFullYear(leapdateYear, leapdateMonth, leapdateDay);
leap.setUTCHours(0, 0, 0, 0);
timeDotGov.data.leapsecond = leap.getTime();
}
// GET TIME FROM SERVER
timeDotGov.clockController.dsClock.doRequest = function() {
d = new Date();
var xmlHttp = new XMLHttpRequest();
////////////////////////////////////////////////////////////////////////////////////
////// USE OF THIS .CGI BY OUTSIDE SITES OR APPLICATIONS IS STRICTLY PROHIBITED ////
// OR USING THE TIME FROM THIS SITE IN ANY WAY FOR OTHER SITES IS ALSO PROHIBITED //
///////////////////////////////////////////////////////////////////////////////////
xmlHttp.open("GET", "/zzz__2fbc6c3300df7e4483acd44c5044098a9fcc61d6.cgi?disablecache=" + d.getTime(), false);
xmlHttp.send(null);
return xmlHttp.responseText;
}
// GET t1, t2, t3, t4 in doRequest AND CALCULATE DELAYS IN doData
timeDotGov.clockController.checkservertime = function() {
var o = new Object();
var d = new Date();
var currentTimeObj = new Date();
timeDotGov.data.requestTime = currentTimeObj.getTime();
timeDotGov.data.currentTime = timeDotGov.clockController.dsClock.doRequest(); // GET NIST TIME FROM SERVER
timeDotGov.clockController.doData();
}
// PARSE DATA FROM AUXDATA.XML
timeDotGov.clockController.doAuxData = function() {
parser = new DOMParser();
xmlDoc = parser.parseFromString(timeDotGov.auxdata, "text/xml");
timeDotGov.data.curryear = xmlDoc.getElementsByTagName("currYear")[0].childNodes[0].nodeValue;
timeDotGov.data.dstdates = xmlDoc.getElementsByTagName("DstDates")[0].childNodes[0].nodeValue;
timeDotGov.data.leapdate = xmlDoc.getElementsByTagName("LeapDate")[0].childNodes[0].nodeValue;
}
timeDotGov.clockController.getDSTdates = function() {
var dstStartDate = new Date();
var dstEndDate = new Date();
var temp=timeDotGov.data.dstdates.split(" "); // makes an array of the string elements
var startmonth = temp[0];
startmonth = startmonth - 1; // to follow js convention of month = 0-11 (Jan = 0)
var startday = temp[1];
var endmonth = temp[2];
endmonth = endmonth - 1;
var endday = temp[3];
dstStartDate.setUTCFullYear(timeDotGov.data.curryear, startmonth, startday);
dstStartDate.setUTCHours(2, 0, 0, 0);
dstEndDate.setUTCFullYear(timeDotGov.data.curryear, endmonth, endday);
dstEndDate.setUTCHours(1, 0, 0, 0);
timeDotGov.data.dstStart = dstStartDate.getTime();
timeDotGov.data.dstEnd = dstEndDate.getTime();
}
timeDotGov.clockController.auxdata = function() {
this.doAuxData();
this.getDSTdates();
this.getleapdate();
}
timeDotGov.clockController.doData = function() {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(timeDotGov.data.currentTime, "text/xml");
var t2 = xmlDoc.getElementsByTagName("timestamp")[0].getAttribute("time2");
var t3 = xmlDoc.getElementsByTagName("timestamp")[0].getAttribute("time3");
var serverDelay = Math.round((t3 - t2) / 1000); // server delay in milliseconds
timeDotGov.data.serverTime = Math.round(t3/1000); // Server time in milliseconds
var currentTimeObj2 = new Date();
timeDotGov.data.responseTime = currentTimeObj2.getTime(); // t4
timeDotGov.data.RThalf = ((timeDotGov.data.responseTime - timeDotGov.data.requestTime) - serverDelay) / 2; // (t4 - t1) - (t3 -t2) NTP EQ
timeDotGov.data.realTimeDif = timeDotGov.data.serverTime - timeDotGov.data.responseTime; // t3-t4 used for correction to local clock for official time
////////////////////////////////////////////////////////////
// USE OFFSET CHECK VAR TO ONLY CHECK CLIENT CLOCK ONCE ///
////////////////////////////////////////////////////////////
if (offsetCheck) {
// ROUND TO MILLISECONDS - USE (* -1) to invert timediff pos/neg display value
var diff = (timeDotGov.data.realTimeDif/1000) * -1;
var diffDisplay = diff.toFixed(3);
if (diffDisplay > 0 ) {
diffDisplay = "+" + diffDisplay;
}
document.getElementById("realTimeDif").innerHTML = diffDisplay;
offsetCheck = false;
}
timeDotGov.clockController.runningclocks();
timeDotGov.data.leapFlag = "false";
} // END OF doData FUNCTION
// REFRESHES CLOCKS AT THE TOP OF EACH *ACTUAL* SECOND
timeDotGov.clockController.runningclocks = function() {
var deviceClock = new Date();
var fractionalZone =(Math.abs((deviceClock.getTimezoneOffset()/60))) % 1; // IF CLIENT IS IN A ZONE WITH A FRACTIONAL HOUR, MAKE ADJUSTMENT (MODULO 1 TO GET THE FRACTIONAL HOUR)
if (fractionalZone != 0) {
fractionalZone = (1 - fractionalZone); // SUBTRACT FROM 1 TO GET VALID CORRECTION FOR 30 AND 45 MIN ZONES
}
var fractionalZoneMilli = fractionalZone * 3600000; // CONVERT PARTIAL HOUR TO MILLISECONDS, TO BE SUBTRACTED FROM ADJUSTED TIME
var s = new Date(); // convert PC time and delay back to seconds (this is UTC)
s.setTime(s.getTime() + timeDotGov.data.realTimeDif + timeDotGov.data.RThalf - fractionalZoneMilli); // CORRECT FOR PC CLOCK ERROR, HALF NETWORK DELAY AND CLIENT IN PARTIAL TIME ZONE
var sec = s.getSeconds();
if (sec != timeDotGov.data.previousSec) { // call handleonrefresh as soon as you see a new second
timeDotGov.data.previousSec = sec;
timeDotGov.clockController.handleonrefresh(s);
}
}
// CHECK IF USER CHANGED CLOCK or IF NEW DST STATE or LEAP SECOND, THEN REFRESH ALL CLOCKS
timeDotGov.clockController.handleonrefresh = function(s) {
//var mins = s.getMinutes();
var now = s.getTime(); // convert time to ms since epoch (UTC)
//if (0 <= mins < 30) {
// now = now + fractionalZoneMilli;
//} else {
// now = now - fractionalZoneMilli;
//}
if (timeDotGov.data.then == null) {
timeDotGov.data.then = now; // if it's the first round, don't let it fail
}
var DidUserChangeClock = Math.abs(now - timeDotGov.data.then);
if (DidUserChangeClock >= 2000) { // if pc clock changed then reset (|now-then| should only be 1 s)
location.reload();
timeDotGov.data.then = now;
} else {
timeDotGov.data.then = now;
}
if (timeDotGov.data.leapsecond / 1000 == Math.floor(now / 1000)) {
timeDotGov.data.leapFlag = "true"
}
var clocks = document.getElementsByClassName("clock");
var clockNum; // SET TO VALUE OF 'i' IN THE FOLLOWING LOOP TO REPRESENT EACH CLOCK INSTANCE
// SET ALL OF THE CLOCKS BY CALLING setCurrentTime FOR EACH CLOCK INSTANCE
for(var i =0; i < clocks.length; i++ ){
var clock = clocks[i].getElementsByTagName("time")[0];
var zoneOffset = clocks[i].getElementsByTagName("time")[0].getAttribute("zoneOffset") || 0;
// GIVE VAR CLOCKNUM VALUE OF i
clockNum = i;
clock.innerHTML = timeDotGov.clock.setCurrentTime(timeDotGov.data.clockinstances[i], now, timeDotGov.data.twentyFour(), timeDotGov.data.dstStart, timeDotGov.data.dstEnd, zoneOffset, timeDotGov.data.leapFlag, timeDotGov.data.leapsec60, timeDotGov.data.RThalf, clockNum);
document.getElementById('timeUTC').innerHTML = timeDotGov.clock.setCurrentTime(timeDotGov.data.clockinstances[i], now, true, timeDotGov.data.dstStart, timeDotGov.data.dstEnd, 0, timeDotGov.data.leapFlag, timeDotGov.data.leapsec60, timeDotGov.data.RThalf, 999); // LAST VAR IS CLOCKNUM, PASSING AS '999' TO AVOID DUPLICATE 'i' VARIABLE VALUE SEND TO SETCURRENTTIME FUNCTION
}
} // END OF handleonrefresh FUNCTION
// CREATES CLOCK DIGITS AND DST/ST LABELS FOR EACH CLOCK INSTANCE
timeDotGov.clock.setCurrentTime = function(clock, now, twentyFour, dstStart, dstEnd, zoneOffset, leapFlag, leapsec60, RThalf, clockNum) {
var displayTime = new Date();
now = (now - (zoneOffset * 3600000));
if (leapFlag == "true"){
now = now - 1000; // if leap has occurred, show (seconds - 1) until next sync
var leapsec60 = "true"; // var to show a 60 instead of 59
}
displayTime.setTime(now);
var year = displayTime.getUTCFullYear();
var hourNum = displayTime.getUTCHours();
var minNum = displayTime.getMinutes();
var secNum = displayTime.getSeconds();
// CREATE ARRAY OF THE CLOCK NUMS WHO FOLLOW DST
// ALSO CREATE ARRAY OF DST LABEL CLASSES ////////////////////////
var DSTclocksArray = [ 0, 1, 6, 7, 8, 9 ]; // THESE ARE THE clockNums FOR THE ZONES THAT FOLLOW DST
var dstLabels = document.getElementsByClassName("DSTterm"); // CREATE ARRAY FOR DST LABELS
var dstLetters = document.getElementsByClassName("DSTletter"); // CREATE ARRAY FOR DST ABBREVIATION
var dstNums = document.getElementsByClassName("DSTnum"); // CREATE ARRAY FOR DST OFFSET NUM
if ( now >= dstStart && now <= dstEnd ) { // CHECK IF THIS SECOND IS DST OR NOT
// CHECK IF CLOCK NUM IS IN DST ARRAY, IF SO, ADD THE DST HOUR AND LABELS
// if ( DSTclocksArray.includes(clockNum) ) {
if ( DSTclocksArray.indexOf(clockNum) > -1 ) { // indexOf fixes a chance in ie
hourNum = hourNum + 1;
// AT END OF FUNC ADD/PUSH CLOCKNUM TO dstClocksUpdated ARRAY, IF CLOCKNUM IS IN THERE, DONT UPDATE AGAIN
// if ( !dstClocksUpdated.includes( clockNum ) ) {
if ( dstClocksUpdated.indexOf( clockNum ) == -1 ) { // indexOf fixes a chance in ie
dstLabels[clockNum].innerHTML = "DAYLIGHT";
dstLetters[clockNum].innerHTML = "D";
var standardNum = Number(dstNums[clockNum].innerHTML);
dstNums[clockNum].innerHTML = standardNum - 1;
dstClocksUpdated.push(clockNum); // ADD CLOCK NUM TO ARRAY OF CLOCKS UPDATED
}
}
}
// IF DST OR INCREMENTED HOUR GOES INTO NEXT DAY, RESET TO HOUR ZERO AND ADD A DAY
if (hourNum > 23) {
hourNum = 0;
now = (now + 3600000) // advance now by one hour
displayTime.setTime(now); // reset displaytime so day/date/month are correct with new day
}
// CHECK FOR AND IMPLEMENT LEAP SECOND
if (leapFlag == "true") {
if (leapsec60 == "true") {
if (minNum == "59") {
if (secNum == "59") { // if leapsec, and 59:59 then show 60 and reset
secNum = "60";
leapsec60 = "false";
}
}
}
}
var hourLabel = "";
// 12-HOUR OR 24-HOUR DISPLAY
// BOX NOT CHECKED SO 12-HOUR DISPLAY
if (!(twentyFour)) {
if (hourNum > 11) {
hourNum -= 12;
am_pm = "P.M.";
} else {
am_pm = "A.M.";
}
} else {
am_pm = "";
}
var newVal;
// SET HOURS
if(hourNum > 9) {
newVal = Number(String(hourNum).charAt(0));
if(timeDotGov.data.myhour0 != newVal) {
timeDotGov.data.myhour0 = newVal;
}
}
else if(Number(timeDotGov.data.myhour0) != 0) {
timeDotGov.data.myhour0 = "0";
}
if(Number(hourNum) > 9){
newVal=Number(String(hourNum).charAt(1));
if(timeDotGov.data.myhour1 != newVal) {
timeDotGov.data.myhour1 = newVal;
}
} else if (Number(timeDotGov.data.myhour1) != Number(hourNum)) {
timeDotGov.data.myhour1 = Number(hourNum);
}
if ((hourNum < 1) && (!(twentyFour))) { // if not 24hour time force the 12 so it's not 00
timeDotGov.data.myhour0 = 1;
timeDotGov.data.myhour1 = 2;
}
// SET MINUTES
if(minNum > 9) {
newVal = Number(String(minNum).charAt(0));
if (timeDotGov.data.mymin0 != newVal) {
timeDotGov.data.mymin0 = newVal;
}
} else if(Number(timeDotGov.data.mymin0) != 0) {
timeDotGov.data.mymin0 = 0;
}
if(Number(minNum) > 9) {
newVal = Number(String(minNum).charAt(1));
if(timeDotGov.data.mymin1 != newVal) {
timeDotGov.data.mymin1 = newVal;
}
}
else if(Number(timeDotGov.data.mymin1) != Number(minNum)) {
timeDotGov.data.mymin1 = Number(minNum);
}
// SET SECONDS
if(secNum > 9) {
newVal = Number(String(secNum).charAt(0));
if(timeDotGov.data.mysec0 != newVal) {
timeDotGov.data.mysec0 = newVal;
}
}
else if(Number(timeDotGov.data.mysec0) != 0) {
timeDotGov.data.mysec0 = 0;
}
if(Number(secNum) > 9) {
newVal=Number(String(secNum).charAt(1));
if(timeDotGov.data.mysec1 != newVal) {
timeDotGov.data.mysec1 = newVal;
}
}
else if(Number(timeDotGov.data.mysec1) != Number(secNum)) {
timeDotGov.data.mysec1 = Number(secNum);
}
// CREATE CLOCK DIGITS STRING TO DISPLAY
var clockdigits = (timeDotGov.data.myhour0 + "" + timeDotGov.data.myhour1 + ":" + timeDotGov.data.mymin0 + timeDotGov.data.mymin1 + ":" + timeDotGov.data.mysec0 + timeDotGov.data.mysec1);
clock = clockdigits + " " + am_pm;
return clock;
} // END OF setCurrentTime FUNCTION