File indexing completed on 2024-04-14 14:11:32

0001 /*
0002     SPDX-FileCopyrightText: 2004 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kstarsdatetime.h"
0008 
0009 #include "dms.h"
0010 #include "ksnumbers.h"
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <kstars_debug.h>
0015 
0016 KStarsDateTime::KStarsDateTime() : QDateTime()
0017 {
0018     setTimeSpec(Qt::UTC);
0019     setDJD(J2000);
0020 }
0021 
0022 KStarsDateTime::KStarsDateTime(const KStarsDateTime &kdt) : QDateTime()
0023 {
0024     *this = kdt;
0025 }
0026 
0027 KStarsDateTime& KStarsDateTime::operator=(const KStarsDateTime &kdt) noexcept
0028 {
0029     setDJD(kdt.djd());
0030     setTimeSpec(kdt.timeSpec());
0031     //utcoffset deprecated
0032     //setUtcOffset(kdt.utcOffset());
0033     return *this;
0034 }
0035 
0036 /*KStarsDateTime::KStarsDateTime( const QDateTime &kdt ) :
0037     QDateTime( kdt )
0038 {
0039     //don't call setDJD() because we don't need to compute the time; just set DJD directly
0040     QTime _t = kdt.time();
0041     QDate _d = kdt.date();
0042     long double jdFrac = ( _t.hour()-12 + ( _t.minute() + ( _t.second() + _t.msec()/1000.)/60.)/60.)/24.;
0043     DJD = static_cast<long double>( _d.toJulianDay() ) + jdFrac;
0044 }*/
0045 
0046 KStarsDateTime::KStarsDateTime(const QDateTime &qdt) : QDateTime(qdt) //, QDateTime::Spec::UTC() )
0047 {
0048     // FIXME: This method might be buggy. Need to write some tests -- asimha (Oct 2016)
0049     QTime _t           = qdt.time();
0050     QDate _d           = qdt.date();
0051     long double jdFrac = (_t.hour() - 12 + (_t.minute() + (_t.second() + _t.msec() / 1000.) / 60.) / 60.) / 24.;
0052     DJD                = static_cast<long double>(_d.toJulianDay()) + jdFrac;
0053     setTimeSpec(qdt.timeSpec());
0054     //setUtcOffset(qdt.utcOffset());
0055 }
0056 
0057 KStarsDateTime::KStarsDateTime(const QDate &_d, const QTime &_t, Qt::TimeSpec timeSpec)
0058     : //QDateTime( _d, _t, QDateTime::Spec::UTC() )
0059       QDateTime(_d, _t, timeSpec)
0060 {
0061     //don't call setDJD() because we don't need to compute the time; just set DJD directly
0062     long double jdFrac = (_t.hour() - 12 + (_t.minute() + (_t.second() + _t.msec() / 1000.) / 60.) / 60.) / 24.;
0063     DJD                = static_cast<long double>(_d.toJulianDay()) + jdFrac;
0064 }
0065 
0066 KStarsDateTime::KStarsDateTime(long double _jd) : QDateTime()
0067 {
0068     setTimeSpec(Qt::UTC);
0069     setDJD(_jd);
0070 }
0071 
0072 //KStarsDateTime KStarsDateTime::currentDateTime( QDateTime::Spec spec ) {
0073 KStarsDateTime KStarsDateTime::currentDateTime()
0074 {
0075     KStarsDateTime dt(QDateTime::currentDateTime());
0076     // if ( dt.time().hour()==0 && dt.time().minute()==0 )        // midnight or right after?
0077     //   dt.setDate( QDateTime::currentDateTime(spec).date() ); // fetch date again
0078 
0079     return dt;
0080 }
0081 
0082 KStarsDateTime KStarsDateTime::currentDateTimeUtc()
0083 {
0084     KStarsDateTime dt(QDateTime::currentDateTimeUtc());
0085     //if ( dt.time().hour()==0 && dt.time().minute()==0 )        // midnight or right after?
0086     //        dt.setDate( QDateTime::currentDateTime(spec).date() ); // fetch date again
0087 
0088     return dt;
0089 }
0090 
0091 KStarsDateTime KStarsDateTime::fromString(const QString &s)
0092 {
0093     //DEBUG
0094     qCDebug(KSTARS) << "Date string: " << s;
0095 
0096     KStarsDateTime dtResult(QDateTime::fromString(s, Qt::TextDate));
0097 
0098     if (dtResult.isValid())
0099         return dtResult;
0100 
0101     dtResult = KStarsDateTime(QDateTime::fromString(s, Qt::ISODate));
0102     if (dtResult.isValid())
0103         return dtResult;
0104 
0105     //dtResult = QDateTime::fromString( s, QDateTime::RFCDate );
0106     dtResult = KStarsDateTime(QDateTime::fromString(s, Qt::RFC2822Date));
0107     if (dtResult.isValid())
0108         return dtResult;
0109 
0110     qCWarning(KSTARS) << "Could not parse Date/Time string:" << s;
0111     qCWarning(KSTARS) << "Valid date formats:";
0112     qCWarning(KSTARS) << "  1950-02-25   ;  1950-02-25T05:30:00";
0113     qCWarning(KSTARS) << "  25 Feb 1950  ;  25 Feb 1950 05:30:00";
0114     qCWarning(KSTARS) << "  Sat Feb 25 1950  ;  Sat Feb 25 05:30:00 1950";
0115     return KStarsDateTime(QDateTime()); //invalid
0116 }
0117 
0118 void KStarsDateTime::setDJD(long double _jd)
0119 {
0120     //QDateTime::setTimeSpec( QDateTime::Spec::UTC() );
0121     //QDateTime::setTimeSpec(Qt::UTC);
0122 
0123     DJD            = _jd;
0124     long int ijd   = (long int)_jd;
0125     double dayfrac = _jd - (double)ijd + 0.5;
0126     if (dayfrac > 1.0)
0127     {
0128         ijd++;
0129         dayfrac -= 1.0;
0130     }
0131 
0132     QDate dd = QDate::fromJulianDay(ijd);
0133     QDateTime::setDate(dd);
0134 
0135     double hour = 24. * dayfrac;
0136     int h       = int(hour);
0137     int m       = int(60. * (hour - h));
0138     int s       = int(60. * (60. * (hour - h) - m));
0139     int ms      = int(1000. * (60. * (60. * (hour - h) - m) - s));
0140 
0141     QDateTime::setTime(QTime(h, m, s, ms));
0142 }
0143 
0144 void KStarsDateTime::setDate(const QDate &_d)
0145 {
0146     //Save the JD fraction
0147     long double jdFrac = djd() - static_cast<long double>(date().toJulianDay());
0148 
0149     //set the integer portion of the JD and add back the JD fraction:
0150     setDJD(static_cast<long double>(_d.toJulianDay()) + jdFrac);
0151 }
0152 
0153 KStarsDateTime KStarsDateTime::addSecs(double s) const
0154 {
0155     long double ds = static_cast<long double>(s) / 86400.;
0156     KStarsDateTime kdt(djd() + ds);
0157     kdt.setTimeSpec(timeSpec());
0158     return kdt;
0159 }
0160 
0161 void KStarsDateTime::setTime(const QTime &_t)
0162 {
0163     KStarsDateTime _dt(date(), _t, timeSpec());
0164     setDJD(_dt.djd());
0165 }
0166 
0167 dms KStarsDateTime::gst() const
0168 {
0169     dms gst0 = GSTat0hUT();
0170 
0171     double hr = double(time().hour() - offsetFromUtc() / 3600.0);
0172     double mn = double(time().minute());
0173     double sc = double(time().second()) + double(0.001 * time().msec());
0174     double st = (hr + (mn + sc / 60.0) / 60.0) * SIDEREALSECOND;
0175 
0176     dms gst = dms(gst0.Degrees() + st * 15.0).reduce();
0177     return gst;
0178 }
0179 
0180 dms KStarsDateTime::GSTat0hUT() const
0181 {
0182     double sinOb, cosOb;
0183 
0184     // Mean greenwich sidereal time
0185     KStarsDateTime t0(date(), QTime(0, 0, 0));
0186     long double s = t0.djd() - J2000;
0187     double t      = s / 36525.0;
0188     double t1     = 6.697374558 + 2400.051336 * t + 0.000025862 * t * t + 0.000000002 * t * t * t;
0189 
0190     // To obtain the apparent sidereal time, we have to correct the
0191     // mean greenwich sidereal time with nutation in longitude multiplied
0192     // by the cosine of the obliquity of the ecliptic. This correction
0193     // is called nutation in right ascention, and may amount to 0.3 secs.
0194     KSNumbers num(t0.djd());
0195     num.obliquity()->SinCos(sinOb, cosOb);
0196 
0197     // nutLong has to be in hours of time since t1 is hours of time.
0198     double nutLong = num.dEcLong() * cosOb / 15.0;
0199     t1 += nutLong;
0200 
0201     dms gst;
0202     gst.setH(t1);
0203     return gst.reduce();
0204 }
0205 
0206 QTime KStarsDateTime::GSTtoUT(dms GST) const
0207 {
0208     dms gst0 = GSTat0hUT();
0209 
0210     //dt is the number of sidereal hours since UT 0h.
0211     double dt = GST.Hours() - gst0.Hours();
0212     while (dt < 0.0)
0213         dt += 24.0;
0214     while (dt >= 24.0)
0215         dt -= 24.0;
0216 
0217     //convert to solar time.  dt is now the number of hours since 0h UT.
0218     dt /= SIDEREALSECOND;
0219 
0220     int hr = int(dt);
0221     int mn = int(60.0 * (dt - double(hr)));
0222     int sc = int(60.0 * (60.0 * (dt - double(hr)) - double(mn)));
0223     int ms = int(1000.0 * (60.0 * (60.0 * (dt - double(hr)) - double(mn)) - double(sc)));
0224 
0225     return (QTime(hr, mn, sc, ms));
0226 }
0227 
0228 void KStarsDateTime::setFromEpoch(double epoch)
0229 {
0230     if (epoch == 1950.0) // Assume Besselian
0231         setFromEpoch(epoch, BESSELIAN);
0232     else
0233         setFromEpoch(epoch, JULIAN); // Assume Julian
0234 }
0235 
0236 bool KStarsDateTime::setFromEpoch(double epoch, EpochType type)
0237 {
0238     if (type != JULIAN && type != BESSELIAN)
0239         return false;
0240     else
0241         setDJD(epochToJd(epoch, type));
0242     return true;
0243 }
0244 
0245 bool KStarsDateTime::setFromEpoch(const QString &eName)
0246 {
0247     bool result;
0248     double epoch;
0249     epoch = stringToEpoch(eName, result);
0250 
0251     if (!result)
0252         return false;
0253     return setFromEpoch(epoch, JULIAN); // We've already converted
0254 }
0255 
0256 long double KStarsDateTime::epochToJd(double epoch, EpochType type)
0257 {
0258     switch (type)
0259     {
0260         case BESSELIAN:
0261             return B1900 + (epoch - 1900.0) * JD_PER_BYEAR;
0262         case JULIAN:
0263             return J2000 + (epoch - 2000.0) * 365.25;
0264         default:
0265             return NaN::d;
0266     }
0267 }
0268 
0269 double KStarsDateTime::jdToEpoch(long double jd, KStarsDateTime::EpochType type)
0270 {
0271     // Definitions for conversion formulas are from:
0272     //
0273     // * http://scienceworld.wolfram.com/astronomy/BesselianEpoch.html
0274     // * http://scienceworld.wolfram.com/astronomy/JulianEpoch.html
0275     //
0276 
0277     switch (type)
0278     {
0279         case KStarsDateTime::BESSELIAN:
0280             return 1900.0 + (jd - KStarsDateTime::B1900) / KStarsDateTime::JD_PER_BYEAR;
0281         case KStarsDateTime::JULIAN:
0282             return 2000.0 + (jd - J2000) / 365.24;
0283         default:
0284             return NaN::d;
0285     }
0286 }
0287 
0288 double KStarsDateTime::stringToEpoch(const QString &eName, bool &ok)
0289 {
0290     double epoch = J2000;
0291     ok           = false;
0292 
0293     if (eName.isEmpty()) // By default, assume J2000
0294         return epoch;
0295 
0296     if (eName.startsWith('J'))
0297         epoch = eName.midRef(1).toDouble(&ok);
0298     else if (eName.startsWith('B'))
0299     {
0300         epoch = eName.midRef(1).toDouble(&ok);
0301         epoch = jdToEpoch(epochToJd(epoch, BESSELIAN), JULIAN); // Convert Besselian epoch to Julian epoch
0302     }
0303     // Assume it's Julian
0304     else
0305         epoch = eName.toDouble(&ok);
0306 
0307     return epoch;
0308 }