
// Display the number of days between two dates
function spanDT(DTFa,DTFb)
{   var dtfa,dtfb,a,b,c,x,h

    dtfa=nuDATE(DTFa)
    dtfb=nuDATE(DTFb)
    a=dtfa.getTime()/_msD_
    b=dtfb.getTime()/_msD_
    c=Abs(a-b)
    x=''
    h=1/24
    if ((c>0) && (c<h)) {x='['+fmtDecml(24*60*c)+' minutes]'}
    if ((c>=h) && (c<1)) {x='['+fmtDecml(24*c)+' hours]'}
    if (c>366) {x='['+fmtDecml(c/365.25)+' years]'}
    alert('The time between dates is \n'+fmtDecml(c)+' days '+x)
}

// 24 hour or AM/PM formatting of hours, minutes and seconds
function showTime(Hr,Mn,Sc,Tcode)
{   var hr,mn,sc,tod,suffix,h

    hr=String(Hr)
    mn=String(Mn)
    sc=String(Sc)
    tod=''
    if (mn.length<2) {mn='0'+mn}
    if (sc.length<2) {sc='0'+sc}
    if (Tcode==0)  // 24 hour
    {
        tod=hr+':'+mn+':'+sc
    }
    else  // AM/PM format
    {
        suffix=' AM'
        h=Number(hr)
        if ((h==0) && (mn=='00') && (sc=='00')) {return 'Midnight'}
        if ((h==12) && (mn=='00') && (sc=='00')) {return '12 Noon'}
        if (h>=12) {suffix=' PM'; hr=String(h-12)}
        sc=':'+sc
        mn=':'+mn
        if (sc==':00')
        {
            sc=''
            if (mn==':00') {mn=' '}
        }
        tod=hr+mn+sc+suffix
    }
    return tod
}

// Prepare a short (civil) date string
function SHORTdt(DTF)
{   var dtf,mo,dy,yr,hr,mn,sc

    dtf=nuDATE(DTF)
    mo=(dtf.getMonth()+1)+'/'
    dy=dtf.getDate()+'    '
    yr=dtf.getFullYear()+'/'
    hr=dtf.getHours()+':'
    mn=dtf.getMinutes()+':'
    if (mn.length<3) {mn='0'+mn}
    sc=dtf.getSeconds()+''
    if (sc.length<2) {sc='0'+sc}
    return yr+mo+dy + hr+mn+sc
}

// Display the standard (civil) date
function civilDT(DTF,Tcode)
{   var dtf,wd,mo,dy,yr,hr,mn,sc

    dtf=nuDATE(DTF)
    wd=_wkD_[dtf.getDay()]+', '
    mo=_Mon_[dtf.getMonth()]+' '
    dy=dtf.getDate()+', '
    yr=dtf.getFullYear()+'   '
    hr=dtf.getHours()
    mn=dtf.getMinutes()
    sc=dtf.getSeconds()
    alert('The civil date and time is ' +wd+ '\n' +mo+dy+yr + showTime(hr,mn,sc,Tcode))
}

// Display the Greenwich Date and Time
function gmtDT(DTF,Tcode)
{   var dtf,wd,mo,dy,yr,hr,mn,sc

    dtf=nuDATE(DTF)
    wd=_wkD_[dtf.getUTCDay()]+', '
    mo=_Mon_[dtf.getUTCMonth()]+' '
    dy=dtf.getUTCDate()+', '
    yr=dtf.getUTCFullYear()+'   '
    hr=dtf.getUTCHours()
    mn=dtf.getUTCMinutes()
    sc=dtf.getUTCSeconds()
    alert('The GMT date and time is '+wd+'\n'+mo+dy+yr + showTime(hr,mn,sc,Tcode))
}

// Display the Julian Day Number
function julianDT(DTF)
{   var jdn

    jdn=Round(jdnDT(DTF)*1000)/1000
    jdn=String(jdn)
    alert('Julian Day Number is '+jdn)
}

// Compute the Greenwich TOD
function GTOD(DTF)
{   var dtf,hr,mn,sc

    dtf=nuDATE(DTF)
    hr=dtf.getHours()*3600
    mn=dtf.getMinutes()*60
    sc=dtf.getSeconds()
    return (((hr+mn+sc)/3600)+9600)%24
}

// Compute the Sidereal Time
function GST(DTF)
{   var T,G0,dtf,hr,mn,sc,a

    T=(jdnDT(DTF)-2451545)/36525
    G0=24110.548 + (8640184.812866*T)+(0.93104*T*T)-(0.0000062*T*T*T)
    dtf=nuDATE(DTF)
    hr=dtf.getHours()*3600
    mn=dtf.getMinutes()*60
    sc=dtf.getSeconds()
    a=(hr+mn+sc)*1.00273790934
    return (((a+G0)/3600)+9600)%24
}

// Display the Sidereal Time
function siderDT(DTF)
{   var Gst,Gtd,dif

    Gst=GST(DTF)
    Gtd=GTOD(DTF)
    dif=Gst-Gtd
    alert('The local sidereal time is '+fmtSxgml(Gst,':: ') +
          ' offset is '+fmtSxgml(dif,':: ') )
}

// Determine the Local Sidereal Date and Time from the longitude
function LST(DTF,L)
{
    return GST(DTF)-(L/15)
}

// Calculate the Julian Day Number
function jdnDT(DTF)
{   var dtf,zone,jdn

    dtf=nuDATE(DTF)
    zone=dtf.getTimezoneOffset()/(24*60)
    jdn=dtf.getTime()/_msD_
    return jdn-zone+_jsE_
}

// This function is an extension of the built in function "new Date".
// See datehelp.htm for a conceptual idea of its functions
function nuDATE(DTF)
{   var i,j,k,l,m,n,dtf,DATe,TIMe,ZONe,wrk,Wrk,Now,jdn,key,yr,mo,dy,ymdt,TD,WD

    if (DTF==null) {return new Date()}
    if (DTF=='')   {return new Date()}

    dtf=String(DTF)
    dtf=dtf.toLowerCase()
    dtf=strip(dtf)

    // Change North American Time ZONes to GMT offsets
    //             Eastern     Central     Mountain    Pacific
    Wrk=new Array (' adt',' ast',' edt',' est',' cdt',' cst',' mdt',' mst',' pdt',' pst')
    wrk=new Array ( '-3'  , '-4', '-4' , '-5' , '-5' , '-6' , '-6' , '7'  , '-7' , '-8' )
    i=0
    while (i<Wrk.length)
    {
        j=dtf.indexOf(Wrk[i])
        if (j>=0)
        {
            dtf=dtf.substring(0,j) + ' gmt'+wrk[i]+':00'
            i=Wrk.length
        }
        ++i
    }

    // Change J19YY.ddd & J20YY.ddd to Julian Day Numbers
    i=dtf.indexOf('j19'); if (i==none) {i=dtf.indexOf('j20')}
    if (i>none)
    {
        i=dtf.substring(i+1)
        i=((Number(i)-2000)*365.25)+2451545.0
        dtf=String(Number(i))
    }

    // Delete noise word at and replace noon and midnight by equivalent times
    dtf=replace(dtf,' at ',' ')
    dtf=replace(dtf,'12 noon','12:00')
    dtf=replace(dtf,'noon','12:00')
    dtf=replace(dtf,'12 midnight','00:00')
    dtf=replace(dtf,'midnight','00:00')

    // Pull off any time zone modifiers to be added later
    ZONe=''
    i=dtf.indexOf('gmt')
    if (i>none)
    {
        ZONe=dtf.substring(i)
        dtf=dtf.substring(0,i)
    }

    if (dtf.indexOf('now')>none) {return chkDate('',ZONe)}

    Now=new Date()

    // Check if the date is a Julian Day Number (numeric) and handle it
    Wrk='0123456789.'
    j=0
    for (i=0;i<dtf.length;++i)
    {
        l=dtf.substring(i,i+1)
        j=j+Number((Wrk.indexOf(l)>none)?1:0)
    }
    if (j==dtf.length)
    {
        jdn=_msD_*(Number(dtf)-_jsE_)
        wrk=chkDate(jdn,ZONe)
        wrk=wrk.getTimezoneOffset()/(24*60)
        jdn=_msD_*(Number(dtf)-_jsE_+wrk)
        return chkDate(jdn,ZONe)
    }

    // Look for times without a colon (ie just hh). Try to append ":00" at
    // the end of the numeric part of the time (before AM or PM if found)
    // Otherwise assume that symbolic date starts at midnight
    if (dtf.indexOf(':')==none)
    {
        wrk=dtf
        Wrk=''
        m=dtf.indexOf('am')
        if (m==none) {m=dtf.indexOf('pm')}
        if (m!=none)
        {
            wrk=strip(dtf.substring(0,m))
            Wrk=' '+strip(dtf.substring(m))
        }
        j=wrk.length-1
        j=wrk.substring(j)
        if (('0'<=j) && (j<='9'))
        {
          dtf=wrk+':00'+Wrk
        }
        else
        {
          dtf=wrk+' 0:00'+Wrk
        }
    }

    // Make a search for the "keywords" that indicate an FDO extension
    wrk=new Array('last','next','today','tonight')
    key=none
    for (i=0;i<wrk.length;++i)
    {
        Wrk=wrk[i]
        if (dtf.indexOf(Wrk)>none) {key=i; break}
    }

    // If no FDO extensions are left, let Windows have a crack at it
    if (key==none) {return chkDate(dtf,ZONe)}

    //  Split dft into a time and date part leaving dtf itself intact.
    i=dtf.indexOf(':')
    while (i>1)
    {
        if (dtf.substring(i,i+1)==' ') {break}
        --i
    }
    TIMe=dtf.substring(i+1)
    DATe=dtf.substring(0,i)

    // Determine today's YYYY/MM/DD and append the time
    yr=Now.getFullYear()
    mo=Now.getMonth()+1
    dy=Now.getDate()
    ymdt=yr+'/'+mo+'/'+dy+' '+TIMe

    // If the user specified 'today' return today's date and time
    if (key==2)  // today
        {return chkDate(ymdt,ZONe)}

    // If the user specified "Tonight" treat it like "Today" above if the user
    // specified AM, PM or any time except 6:00:00 to 12:00:00
    if (key==3)  // tonight
    {
        if (dtf.indexOf('am')>none) {return chkDate(ymdt,ZONe)}
        if (dtf.indexOf('pm')>none) {return chkDate(ymdt,ZONe)}

        j=TIMe.indexOf(':')
        Wrk=TIMe.substring(0,j)
        wrk=new Array('6','7','8','9','10','11')
        for (k=0;k<6;++k) {if (Wrk==wrk[k]) {TIMe=TIMe+' pm'}}
        ymdt=yr+'/'+mo+'/'+dy+' '+TIMe
        return chkDate(ymdt,ZONe)
    }

    // Process requests for next [or last] Weekday by name. The arithmetic is the same
    // except that requests for last week are seven days earlier.

    TD=Now.getDay();        // Get the index of today's name
    WD=none
    wrk=new Array('sunday','monday','tuesday','wednesday','thursday','friday','saturday')
    for (i=0;i<7;++i) { if (dtf.indexOf(wrk[i])>none) {WD=i} }
    if (WD==none)
    {
        alert('Unknown day of the week is assumed to be now');
        return new Date()
    }

    // We have a valid name of a day of the week. Compute offset to that day less
    // Go through JavaScript stuff to create a valid date and return with it.
    if (key==0) {l=7} else l=0
    k=((7-TD+WD)%7)-l
    k=(k==0)?7:k
    Now.setTime(Now.getTime()+(k*_msD_))
    yr=Now.getFullYear()
    mo=Now.getMonth()+1
    dy=Now.getDate()
    ymdt=yr+'/'+mo+'/'+dy+' '+TIMe
    return chkDate(ymdt,ZONe)

    // Logical end of nuDate

    // This INTERNAL function is a portion of nuDate

    // Check date validity, and substitute today if required. Adjust for time zones.
    function chkDate(DTF,zone)
    {   var dtf,i,offset
        if (DTF=='')
            {dtf=new Date()}
        else
            {dtf=new Date(DTF)}
        i=dtf.getDay()
        if ((0<=i) && (i<=6))
        {
            if (zone.length>1)
            {
                offset=0
                if (zone.length>3)
                   {offset=rdAngle(zone.substring(3))/15}
                dtf.setUTCHours(dtf.getHours()-offset)
            }
            return dtf
        }
        alert('Unknown date and/or time is assumed to be now.')
        return new Date()

    } // End of chkDate

} // End of nuDate


