<?php
/*
Julian Date, Julian Day Number and Delta T Table Calculator
AUTHOR : Jay Tanner - 2024
LANGUAGE : PHP 7.4.9
LICENSE : Public Domain
This program generates a table of Julian Dates, JD Numbers
and Delta T values for a given range of dates.
It is good for finding historical Delta T (TDB - UT) values.
Values are for 00:00:00 UT on each date.
*/
ob_start();
$cYear = date('Y');
// ---------------------------------------------------------------
// Define the program cookie name and set it to expire in 30 days.
// This cookie can be shared by other programs in the same common
// home folder together.
$CookieName = 'JDate-JDNum-Delta_T';
$SetToExpireIn30Days = time() + 30*86400;
// ---------------------------------
// Define PHP program and HTML info.
$_AUTHOR_ = 'Jay Tanner';
$_PROGRAM_VERSION_ = 'v1.00 - '; $at = "at Local Time "; $LTC = "UTC";
$_SCRIPT_FILE_PATH_ = Filter_Input(INPUT_SERVER, 'SCRIPT_FILENAME');
$_REVISION_DATE_ = $_PROGRAM_VERSION_ .'Revised: '. date("Y-F-d-l $at h:i:s A ($LTC", FileMTime($_SCRIPT_FILE_PATH_))."−05:00)";
$_BROWSER_TAB_TEXT_ = "JDate, JDNum and Delta T";
$_INTERFACE_TITLE_ = "<span style='font-size:15pt;'>Julian Date, Julian Day Number and ΔT Table Calculator</span><br><span style='font-size:12pt;'>BC 9999-Mar-16-Sat to AD 9999-Dec-30-Thu</span><br><br><span style='font-size:10pt;'>PHP Program By Jay Tanner - $cYear - Built Around the NASA/JPL Horizons API</span>";
// -------------------------------------
// Define main TextArea text and background
// colors and HTML table row span. If an
// error is reported, then these colors
// will change internally to red/white.
$TxColor = 'black';
$BgColor = 'white';
// ---------------------------------------------------
// Define JavaScript message to display while working.
$_COMPUTING_ = "TextArea1.innerHTML='W.O.R.K.I.N.G --- This may take a few seconds.';";
// ---------------------------------------------
// Do this only if [COMPUTE] button was clicked.
$w = Filter_Input(INPUT_POST, 'ComputeButton');
if (!IsSet($w))
{
// ----------------------------------------------------------------------
// If this program is being called externally, rather than being executed
// by clicking the [COMPUTE] button, and an active cookie also exists,
// then restore the previously saved interface settings from it. If
// the user leaves and comes back later, all the interface settings
// will be remembered and restored if the cookie was not deleted.
$w = Filter_Input(INPUT_COOKIE, $CookieName);
if (IsSet($w))
{
$CookieDataString = Filter_Input(INPUT_COOKIE, $CookieName);
list($StartBCAD,$StartYear,$StartMonth,$StartDay,$StopBCAD,$StopYear,$StopMonth,$StopDay) = Preg_Split("[,]", $CookieDataString);
}
else
// -----------------------------------------------------------
// If there is no previous cookie with the interface settings,
// then set the initial default interface startup values and
// store them in a new cookie.
{
$StartBCAD = 'AD';
$StartYear = date('Y');
$StartMonth = date('M');
$StartDay = date('d');
$StopBCAD = 'AD';
$StopYear = date('Y');
$StopMonth = date('M');
$StopDay = date('d');
// -------------------------------------------
// Store current interface settings in cookie.
$CookieDataString = "$StartBCAD,$StartYear,$StartMonth,$StartDay,$StopBCAD,$StopYear,$StopMonth,$StopDay";
SetCookie ($CookieName, $CookieDataString, $SetToExpireIn30Days);
} // End of else {...}
} // End of if (!isset(_POST['ComputeButton']))
// ------------------------------------------
// Read values of all interface arguments and
// set all empty arguments to default values.
$w = Filter_Input(INPUT_POST, 'ComputeButton');
if (isset($w))
{
$StartBCAD = trim(Filter_Input(INPUT_POST, 'StartBCAD')); $StartBCAD = Check_Era($StartBCAD);
$StartYear = trim(Filter_Input(INPUT_POST, 'StartYear')); $StartYear = Check_Year($StartYear);
$StartMonth = trim(Filter_Input(INPUT_POST, 'StartMonth')); $StartMonth = Check_Month($StartMonth);
$StartDay = trim(Filter_Input(INPUT_POST, 'StartDay')); $StartDay = Check_Day($StartDay);
$StopBCAD = trim(Filter_Input(INPUT_POST, 'StopBCAD')); $StopBCAD = Check_Era($StopBCAD);
$StopYear = trim(Filter_Input(INPUT_POST, 'StopYear')); $StopYear = Check_Year($StopYear);
$StopMonth = trim(Filter_Input(INPUT_POST, 'StopMonth')); $StopMonth = Check_Month($StopMonth);
$StopDay = trim(Filter_Input(INPUT_POST, 'StopDay')); $StopDay = Check_Day($StopDay);
// --------------------------------------------
// Set empty arguments to their default values.
if ($StartBCAD == '') {$StartBCAD = 'AD';}
if ($StartYear == '') {$StartYear = date('Y');}
if ($StartMonth == '') {$StartMonth = date('M');}
if ($StartDay == '') {$StartDay = date('d');}
if ($StopBCAD == '') {$StopBCAD = 'AD';}
if ($StopYear == '') {$StopYear = date('Y');}
if ($StopMonth == '') {$StopMonth = date('M');}
if ($StopDay == '') {$StopDay = date('d');}
// ------------------------------------
// Store interface arguments in cookie.
$CookieDataString = "$StartBCAD,$StartYear,$StartMonth,$StartDay,$StopBCAD,$StopYear,$StopMonth,$StopDay";
SetCookie ($CookieName, $CookieDataString, $SetToExpireIn30Days);
}
// ******************************************
// If error was reported (TRUE), then display
// the error message on a red background.
$ErrMssg = ''; // DEFAULT DEVELOPMENT MODE = ERROR DETECTION MESSAGE OFF
if ($ErrMssg <> '')
{
$TxColor = 'white';
$BgColor = '#CC0000';
$TextArea2Text = '';
$TextArea1Text =
"=== ERROR ===
Invalid argument: 'ArgXX'
$ErrMssg";
}
else
{
// *********************************************************
// BEGIN MAIN COMPUTATIONS HERE IF NO ERRORS REPORTED ABOVE.
// *********************************************************
$StartDate = "$StartBCAD $StartYear-$StartMonth-$StartDay";
$StopDate = "$StopBCAD $StopYear-$StopMonth-$StopDay";
$JDDeltaTTable = JD_and_Delta_T_Table ($StartDate, $StopDate);
$w = Check_For_API_Errors ($JDDeltaTTable);
if ($w <> '') {$JDDeltaTTable = $w; $TxColor = 'white'; $BgColor = '#CC0000';}
$JDDeltaTTable = Str_Replace(',', ' ', $JDDeltaTTable);
// *******************************************
// DROP THROUGH HERE AFTER COMPUTATIONS ABOVE.
// *******************************************
$TextArea1Text =
"==============================================================================
JULIAN DATE, JULIAN DAY NUMBER and DELTA−T (TDB − UT) TABLE CALCULATOR
00:00:00 UT ON EACH DATE
$JDDeltaTTable
";
}
// ****************************
// Define TextArea2 text block.
$TextArea2Text =
"
";
// **************************************************************************
// Determine number of text columns and rows to use in the output text areas.
// These values vary randomly according to the text block width and length.
// The idea is to eliminate the need for scroll-bars within the text areas
// or worry as much about the variable dimensions of a text display area.
// -------------------------------------------
// TextArea1 Default = At least 80 columns.
$Text1Cols = 1 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea1Text))));
if ($Text1Cols < 90) {$Text1Cols = 78;}
$Text1Rows = 2 + Substr_Count($TextArea1Text, "\n");
// -------------------------------------------
// TextArea2 Default = At least 80 columns.
$Text2Cols = 1 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea2Text))));
if ($Text2Cols < 80) {$Text2Cols = 80;}
$Text2Rows = 2 + Substr_Count($TextArea2Text, "\n");
// ******************************************
// ******************************************
// GENERATE CLIENT WEB PAGE TO DISPLAY OUTPUT
print <<< _HTML
<!DOCTYPE HTML>
<HTML>
<head>
<title>$_BROWSER_TAB_TEXT_</title>
<meta name='viewport' content='width=device-width, initial-scale=0.78'>
<meta http-equiv='content-type' content='text/html; charset=UTF-8'>
<meta http-equiv='pragma' content='no-cache'>
<meta http-equiv='expires' content='-1'>
<meta name='description' content='Julian Date,JD Number,Delta-T calculator'>
<meta name='keywords' content='PHPScienceLabs.com'>
<meta name='author' content='Jay Tanner - https://www.PHPScienceLabs.com'>
<meta name='robots' content='index,follow'>
<meta name='googlebot' content='index,follow'>
<style>
BODY {color:white; background:black; font-family:Verdana; font-size:12pt; line-height:125%;}
TABLE
{font-size:13pt; border: 1px solid black;}
TD
{
color:black; background:white; line-height:150%; font-size:10pt;
padding:6px; text-align:center;
}
UL
{font-family:Verdana; font-size:12pt; line-height:150%; text-align:justify;}
PRE
{
background:white; color:black; font-family:monospace; font-size:12.5pt;
font-weight:bold; text-align:left; line-height:125%; padding:6px;
border:2px solid black; border-radius:8px;
page-break-before:page;
}
DIV
{
background:white; color:black; font-family:Verdana; font-size:11pt;
font-weight:normal; line-height:125%; padding:6px;
}
TEXTAREA
{
background:white; color:black; font-family:monospace; font-size:13pt;
font-weight:bold; padding:4pt; white-space:pre; border-radius:8px;
line-height:125%;
}
INPUT[type='text']::-ms-clear {width:0; height:0;}
INPUT[type='text']
{
font-family:monospace; color:black; background:white; font-size:13pt;
font-weight:bold; text-align:center; box-shadow:2px 2px 3px #666666;
border:2px solid black; border-radius:4px;
}
INPUT[type='text']:focus
{
font-family:monospace; background:white; box-shadow:2px 2px 3px #666666;
font-size:13pt; border:2px solid blue; text-align:center; font-weight:bold;
border-radius:4px;
}
INPUT[type='submit']
{
background:black; color:cyan; font-family:Verdana; font-size:10pt;
font-weight:bold; border-radius:4px; border:4px solid #777777;
padding:3pt;
}
INPUT[type='submit']:hover
{
background:black; color:white; font-family:Verdana; font-size:10pt;
font-weight:bold; border-radius:4px; border:4px solid red;
padding:3pt;
}
A:link
{
font-size:10pt; background:transparent; color:#8080FF; border-radius:4px;
font-family:Verdana; font-weight:bold; text-decoration:none;
line-height:175%; padding:3px; border:1px solid transparent;
}
A:visited
{
font-size:10pt; background:transparent; color:DarkCyan; border-radius:4px;
}
A:hover
{
font-size:10pt; background:yellow; color:black; border:1px solid black;
box-shadow:1px 1px 3px #222222; border-radius:4px;
}
A:active
{
font-size:10pt; background:yellow; color:black; border-radius:4px;
}
HR {background:red; height:4px; border:0px;}
::selection{background-color:yellow !important; color:black !important;}
::-moz-selection{background-color:yellow !important; color:black !important;}
@media screen
{
.forscreen {display:block; font-family:monospace;}
.forprinting {display:none; font-family:monospace;}
}
@media print
{
.forscreen {display:none; font-family:monospace;}
.forprinting {display:block; font-family:monospace;}
}
</style>
<script type="text/javascript">
function Print_TEXTAREA(TEXTAREA, Print_DIV)
{
document.getElementById(Print_DIV).innerHTML = document.getElementById(TEXTAREA).value;
print();
}
</script>
</head>
<body>
<form name='form1' method='post' action=''>
<table width='800' align='center' border='0' cellspacing='1' cellpadding='3'>
<!-- Define main page title/header. --->
<tr>
<td colspan="99" style='color:white; background:#000066; border:2px solid white; border-radius:8px 8px 0px 0px; line-height:150%;'>$_INTERFACE_TITLE_</td>
</tr>
<!-- Define text boxes for date input arguments --->
<tr>
<td>
<b style='font-size:12pt;'>Start Date</b><br>
BC|AD Year-Month-Day<br>
<input name='StartBCAD' type='text' value="$StartBCAD" size='3' maxlength='2'>
<input name='StartYear' type='text' value="$StartYear" size='5' maxlength='4'>
<input name='StartMonth' type='text' value="$StartMonth" size='4' maxlength='3'>
<input name='StartDay' type='text' value="$StartDay" size='3' maxlength='2'>
</td>
<td>
<b style='font-size:12pt;'>Stop Date</b><br>
BC|AD Year-Month-Day<br>
<input name='StopBCAD' type='text' value="$StopBCAD" size='3' maxlength='2'>
<input name='StopYear' type='text' value="$StopYear" size='5' maxlength='4'>
<input name='StopMonth' type='text' value="$StopMonth" size='4' maxlength='3'>
<input name='StopDay' type='text' value="$StopDay" size='3' maxlength='2'>
</td>
</tr>
<!-- Define [COMPUTE] button --->
<tr><td colspan="99" style='background-color:black;'><input name='ComputeButton' type='submit' value=' C O M P U T E ' OnClick="$_COMPUTING_"></td></tr>
<!-- Define TextArea1 --->
<tr>
<td colspan="99" style='color:GreenYellow; background-color:black; text-align:center;'>Double-Click Within Text Area to Select ALL Text<br>
<textarea ID='TextArea1' name='TextArea1' style='color:$TxColor; background:$BgColor; padding:6px; border:2px solid white;' cols="$Text1Cols" rows="$Text1Rows" ReadOnly OnDblClick='this.select();' OnMouseUp='return true;'>
$TextArea1Text
</textarea>
</td>
</tr>
</table>
<br>
<!-- Define page footer --->
<table align='center'>
<tr>
<td colspan="1" style="font-size:10pt; color:GreenYellow; background:black; text-align:center;">
<b><a href='View-Source-Code.php' target='_blank'>View Source Code</a></b>
</td>
</tr>
<tr><td colspan="99" style='color:GreenYellow; background:black;'>Program by $_AUTHOR_<br><span style='color:silver; background:black;'>$_REVISION_DATE_</span></td></tr>
</table>
</form>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
</body>
</HTML>
_HTML;
/*
---------------------------------------------------------------------------
Check the era symbol. Default = 'AD'.
Logic: If not 'BC', then 'AD'.
*/
function Check_Era ($BCAD)
{
return (StrToUpper(substr(trim($BCAD),0,1)) == 'B')? 'BC':'AD';
}
/*
---------------------------------------------------------------------------
Check year format. It will always be expressed as a positive 4-digit
integer value which will be prefixed by BC|AD.
ERRORS:
A non-numeric year value converts to '0000', which is an invalid value.
Valid Year Values = 'BC 9999' to 'AD 9999'
Horizons will report a bad date value.
---------------------------------------------------------------------------
*/
function Check_Year ($Year)
{
$y = trim($Year);
if (Is_Numeric($y))
{
return SPrintF("%04d", abs($y));
}
return $y;
}
/*
---------------------------------------------------------------------------
Check month. The month can be a number from 1 to 12 or a 3-letter English
month name abbreviation from 'Jan' to 'Dec'. Abbreviations are NOT case
sensitive.
If a valid number is given, then the corresponding 3-letter abbreviation
will be returned.
If the month is given as a valid 3-letter abbreviation, it will be re-
turned as-is.
ERRORS
Horizons will report a bad date.
---------------------------------------------------------------------------
*/
function Check_Month ($Month)
{
$m = Str_Replace('-', '', trim($Month));
$MONTHS = 'JanFebMarAprMayJunJulAugSepOctNovDec';
// ----------------------------------------------
// Check for 3-letter month abbreviation. MUST be
// the exact spelling, but is NOT case-sensitive.
// Horizons will report a bad date error.
if (!Is_Numeric($m))
{
$m = UCFirst(StrToLower(substr($m,0,3)));
@ $i = StrPos($MONTHS, $m);
if ($i === FALSE) {return $m;}
$m = 1 + $i/3;
}
// ----------------------------
// Check numerical month value.
// Horizons will report a bad date.
if ($m < 1 or $m > 12) {return SPrintF("%02d", $m);}
return substr($MONTHS, 3*($m-1), 3);
}
/*
---------------------------------------------------------------------------
Check day value. 2-digit integer '01' to '31'.
ERRORS:
Horizons will report a bad date.
*/
function Check_Day ($Day)
{
$d = abs(trim($Day)); if ($d == '') {return $d;}
if (Is_Numeric($d)) {return SPrintF("%02d", $d);}
}
/*
---------------------------------------------------------------------------
Check for certain possible returned API error strings within the raw API
response and replace them with customized error messages instead. An empty
string is returned if no error strings are found. The error strings ARE
case-sensitive.
------------------------------------
SOME POSSIBLE RETURNED ERROR STRINGS
Cannot interpret date
No matches found
Multiple major-bodies match string
Cannot read coordinate triplet
altitude must be LESS than 40000 km
Unknown units specification
observer=target disallowed.
---------------------------------------------------------------------------
*/
function Check_For_API_Errors ($RawEphemerisTable)
{
$w = trim($RawEphemerisTable);
$u = '';
if (StrPos($w, 'Cannot interpret date') !== FALSE)
{$u = "Error in a date argument.";}
if (StrPos($w, 'No matches found') !== FALSE)
{$u = "Error. No matches found for target body ID in database.";}
if (StrPos($w, 'Multiple major-bodies match') !== FALSE)
{$u = "Alert: Multiple bodies match target body.\nUse NASA ID, record number or unique name string.";}
if (StrPos($w, 'Cannot read coordinate triplet') !== FALSE)
{$u = "Error in coordinates or altitude value.\nAltitude must be LESS than 40000 km";}
if (StrPos($w, 'Unknown units specification') !== FALSE)
{$u = "Error: Unrecognized or unsupported Step Size units.";}
if (StrPos($w, 'observer=target disallowed') !== FALSE)
{$u = "The Earth itself cannot be used as the ephemeris target body.";}
if (StrPos($w, 'Projected output length') !== FALSE)
{$u = "The requested table exceeds the 90,024 lines limit.\nChoose a smaller time interval.";}
return $u;
}
/*
This function returns a table of Julian Dates, Julian Day
Numbers and Delta-T (TDB-UT) values for any specified date
based on the NASA/JPL Horizons API.
Range: BC 9999-Mar-16-Sat to AD 9999-Dec-30-Thu
*/
function JD_and_Delta_T_Table ($StartDate, $StopDate)
{
// =========================================================
// CONSTRUCT CALLING URL FOR JPL HORIZONS API.
// FORCED TO UT MODE FOR THIS PROGRAM.
$From_Horizons_API =
"https://ssd.jpl.nasa.gov/api/horizons.api?format=text" .
"&COMMAND='0'" .
"&MAKE_EPHEM='YES'" .
"&OBJ_DATA='NO'" .
"&CENTER='500@399'" .
"&TIME_DIGITS='SECONDS'" .
"&START_TIME='$StartDate 00:00:00 UT'" .
"&STOP_TIME='$StopDate 00:00:00.001'" .
"&STEP_SIZE='1 day'" .
"&QUANTITIES='30'" .
"&CAL_FORMAT='BOTH'";
// ==============================================================
// Send query to Horizons API to get the requested Delta-T table.
$w = File_Get_Contents($From_Horizons_API);
// -------------------------------------------------------------
// Extract contents of ephemeris table between the start '$$SOE'
// marker and the end of ephemeris '$$EOE' marker.
$i = StrPos($w, '$$SOE');
if ($i === FALSE) {return "$w";}
$j = StrPos($w, '$$EOE');
$w = trim(substr($w, $i+5, $j-$i-5));
$w = (Is_Numeric(substr($w,0,1)))? " $w" : $w;
// Parse the table.
$wTable = '';
$wArray = PReg_Split("[\n]", $w);
$wCount = count($wArray);
for ($i=0; $i < $wCount; $i++)
{
$w = trim($wArray[$i]);
if (StrToLower(substr($w,0,1)) == 'b')
{$w = 'BC ' . substr($w, 1, StrLen($w));}
else
{$w = "AD $w";}
// Extract Delta T, DateStr,TimeStr.
$DeltaTSec = trim(substr($w, -16));
$w = trim(substr($w, 0, StrLen($w)-16));
$JDate = trim(StrRChr($w, ' ')); //$LJDate = StrLen($JDate);
$JDNum = floor($JDate + 0.5);
$DoWi = ($JDNum + 1) % 7; $DoWi += ($DoWi < 0)? 7:0;
$DoWStr = substr('SunMonTueWedThuFriSat', 3*$DoWi, 3);
$w = trim(substr($w, 0, StrLen($w)-18));
$TimeStr = trim(StrRChr($w, ' '));
$DateStr = trim(substr($w,0,14));
// -------------------
// Do some formatting.
$JDate = SPrintF("% 10.1f", $JDate); // +1234567.5
$JDNum = SPrintF("% 8d", $JDNum); // +1234567
if ($DeltaTSec > 0) {$DeltaTSec = "+$DeltaTSec";}
$DeltaTSec = substr(" $DeltaTSec", -14);
// ------------------------------
// Compute Delta T in -+ D H M S.
$d = 'd'; $h = 'h'; $m = 'm'; $s = 's';
$Q = 10;
$DTSec = trim($DeltaTSec);
$DTsss = StrRChr($DTSec, '.');
$DTFmt = StrLen(trim($DTsss)) - 1;
$DTSgn = ($DTSec < 0)? '-' : '+';
$DTSec = abs($DTSec);
$day = bcDiv($DTSec, '86400', $Q);
$DTdd = bcAdd('0',$day);
$DThrs = bcMul('24',bcSub($day,$DTdd,$Q),$Q);
$DThh = bcAdd('0',$DThrs);
$DTmin = bcMul('60',bcSub($DThrs,$DThh,$Q),$Q);
$DTmm = SPrintF("%02d", bcAdd('0',$DTmin));
$DTss = floor(bcMul('60',bcSub($DTmin,$DTmm,$Q),$Q)).$DTsss;
$DThh = SPrintF("%02d", bcAdd('0',$DThh));
if ($DTss < 10) {$DTss = "0$DTss";}
// -----------------------------
// Construct DHMS output string.
$DTHMS = "$DTSgn$DTdd$d $DThh:$DTmm:$DTss";
$DTSec = $DTSgn.floor($DTSec).$DTsss; // exit("$DTSec");
// -----------------------------
// Append current line to table.
$wTable .= "$DateStr-$DoWStr,$JDate,$JDNum, $DTHMS, $DTSec\n";
}
$w = trim($wTable);
// --------------------
// Define table header.
$L ="================== ========== ======== =====================================\n";
$HeadFoot =
" Calendar_Date J_Date JD_Num Delta_T dd hh:mm:ss = Seconds";
return "$HeadFoot\n$L$w\n$L$HeadFoot";
} // End of JD_Delta_T_Table (...)
// END OF PROGRAM
?>