<?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 "&#97;&#116;&#32;&#76;&#111;&#99;&#97;&#108;&#32;&#84;&#105;&#109;&#101;&#32;"$LTC "&#85;&#84;&#67;";
   
$_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_))."&minus;05:00)";
   
$_BROWSER_TAB_TEXT_ "JDate, JDNum and Delta T";
   
$_INTERFACE_TITLE_  "<span style='font-size:15pt;'>Julian Date, Julian Day Number and &Delta;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&minus;T (TDB &minus; 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 Max(Array_Map('StrLen'PReg_Split("[\n]"trim($TextArea1Text))));
    if (
$Text1Cols 90) {$Text1Cols 78;}
   
$Text1Rows Substr_Count($TextArea1Text"\n");

// -------------------------------------------
// TextArea2    Default = At least 80 columns.

   
$Text2Cols Max(Array_Map('StrLen'PReg_Split("[\n]"trim($TextArea2Text))));
    if (
$Text2Cols 80) {$Text2Cols 80;}
   
$Text2Rows 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 $i/3;
     }

// ----------------------------
// Check numerical month value.
// Horizons will report a bad date.

   
if ($m or $m 12) {return SPrintF("%02d"$m);}

   return 
substr($MONTHS3*($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&#44;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($w1StrLen($w));}
   else
      {
$w "AD $w";}

// Extract Delta T, DateStr,TimeStr.
   
$DeltaTSec trim(substr($w, -16));

   
$w trim(substr($w0StrLen($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*$DoWi3);
   
$w trim(substr($w0StrLen($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 =
"&nbsp;&nbsp;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



?>