1 /*
2     SPDX-FileCopyrightText: 2004 Jason Harris <jharris@30doradus.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "kstarsdatetime.h"
8 
9 #include "dms.h"
10 #include "ksnumbers.h"
11 
12 #include <KLocalizedString>
13 
14 #include <kstars_debug.h>
15 
KStarsDateTime()16 KStarsDateTime::KStarsDateTime() : QDateTime()
17 {
18     setTimeSpec(Qt::UTC);
19     setDJD(J2000);
20 }
21 
KStarsDateTime(const KStarsDateTime & kdt)22 KStarsDateTime::KStarsDateTime(const KStarsDateTime &kdt) : QDateTime()
23 {
24     *this = kdt;
25 }
26 
operator =(const KStarsDateTime & kdt)27 KStarsDateTime& KStarsDateTime::operator=(const KStarsDateTime &kdt) noexcept
28 {
29     setDJD(kdt.djd());
30     setTimeSpec(kdt.timeSpec());
31     //utcoffset deprecated
32     //setUtcOffset(kdt.utcOffset());
33     return *this;
34 }
35 
36 /*KStarsDateTime::KStarsDateTime( const QDateTime &kdt ) :
37     QDateTime( kdt )
38 {
39     //don't call setDJD() because we don't need to compute the time; just set DJD directly
40     QTime _t = kdt.time();
41     QDate _d = kdt.date();
42     long double jdFrac = ( _t.hour()-12 + ( _t.minute() + ( _t.second() + _t.msec()/1000.)/60.)/60.)/24.;
43     DJD = static_cast<long double>( _d.toJulianDay() ) + jdFrac;
44 }*/
45 
KStarsDateTime(const QDateTime & qdt)46 KStarsDateTime::KStarsDateTime(const QDateTime &qdt) : QDateTime(qdt) //, QDateTime::Spec::UTC() )
47 {
48     // FIXME: This method might be buggy. Need to write some tests -- asimha (Oct 2016)
49     QTime _t           = qdt.time();
50     QDate _d           = qdt.date();
51     long double jdFrac = (_t.hour() - 12 + (_t.minute() + (_t.second() + _t.msec() / 1000.) / 60.) / 60.) / 24.;
52     DJD                = static_cast<long double>(_d.toJulianDay()) + jdFrac;
53     setTimeSpec(qdt.timeSpec());
54     //setUtcOffset(qdt.utcOffset());
55 }
56 
KStarsDateTime(const QDate & _d,const QTime & _t,Qt::TimeSpec timeSpec)57 KStarsDateTime::KStarsDateTime(const QDate &_d, const QTime &_t, Qt::TimeSpec timeSpec)
58     : //QDateTime( _d, _t, QDateTime::Spec::UTC() )
59       QDateTime(_d, _t, timeSpec)
60 {
61     //don't call setDJD() because we don't need to compute the time; just set DJD directly
62     long double jdFrac = (_t.hour() - 12 + (_t.minute() + (_t.second() + _t.msec() / 1000.) / 60.) / 60.) / 24.;
63     DJD                = static_cast<long double>(_d.toJulianDay()) + jdFrac;
64 }
65 
KStarsDateTime(long double _jd)66 KStarsDateTime::KStarsDateTime(long double _jd) : QDateTime()
67 {
68     setTimeSpec(Qt::UTC);
69     setDJD(_jd);
70 }
71 
72 //KStarsDateTime KStarsDateTime::currentDateTime( QDateTime::Spec spec ) {
currentDateTime()73 KStarsDateTime KStarsDateTime::currentDateTime()
74 {
75     KStarsDateTime dt(QDateTime::currentDateTime());
76     // if ( dt.time().hour()==0 && dt.time().minute()==0 )        // midnight or right after?
77     //   dt.setDate( QDateTime::currentDateTime(spec).date() ); // fetch date again
78 
79     return dt;
80 }
81 
currentDateTimeUtc()82 KStarsDateTime KStarsDateTime::currentDateTimeUtc()
83 {
84     KStarsDateTime dt(QDateTime::currentDateTimeUtc());
85     //if ( dt.time().hour()==0 && dt.time().minute()==0 )        // midnight or right after?
86     //        dt.setDate( QDateTime::currentDateTime(spec).date() ); // fetch date again
87 
88     return dt;
89 }
90 
fromString(const QString & s)91 KStarsDateTime KStarsDateTime::fromString(const QString &s)
92 {
93     //DEBUG
94     qCDebug(KSTARS) << "Date string: " << s;
95 
96     KStarsDateTime dtResult(QDateTime::fromString(s, Qt::TextDate));
97 
98     if (dtResult.isValid())
99         return dtResult;
100 
101     dtResult = KStarsDateTime(QDateTime::fromString(s, Qt::ISODate));
102     if (dtResult.isValid())
103         return dtResult;
104 
105     //dtResult = QDateTime::fromString( s, QDateTime::RFCDate );
106     dtResult = KStarsDateTime(QDateTime::fromString(s, Qt::RFC2822Date));
107     if (dtResult.isValid())
108         return dtResult;
109 
110     qCWarning(KSTARS) << "Could not parse Date/Time string:" << s;
111     qCWarning(KSTARS) << "Valid date formats:";
112     qCWarning(KSTARS) << "  1950-02-25   ;  1950-02-25T05:30:00";
113     qCWarning(KSTARS) << "  25 Feb 1950  ;  25 Feb 1950 05:30:00";
114     qCWarning(KSTARS) << "  Sat Feb 25 1950  ;  Sat Feb 25 05:30:00 1950";
115     return KStarsDateTime(QDateTime()); //invalid
116 }
117 
setDJD(long double _jd)118 void KStarsDateTime::setDJD(long double _jd)
119 {
120     //QDateTime::setTimeSpec( QDateTime::Spec::UTC() );
121     //QDateTime::setTimeSpec(Qt::UTC);
122 
123     DJD            = _jd;
124     long int ijd   = (long int)_jd;
125     double dayfrac = _jd - (double)ijd + 0.5;
126     if (dayfrac > 1.0)
127     {
128         ijd++;
129         dayfrac -= 1.0;
130     }
131 
132     QDate dd = QDate::fromJulianDay(ijd);
133     QDateTime::setDate(dd);
134 
135     double hour = 24. * dayfrac;
136     int h       = int(hour);
137     int m       = int(60. * (hour - h));
138     int s       = int(60. * (60. * (hour - h) - m));
139     int ms      = int(1000. * (60. * (60. * (hour - h) - m) - s));
140 
141     QDateTime::setTime(QTime(h, m, s, ms));
142 }
143 
setDate(const QDate & _d)144 void KStarsDateTime::setDate(const QDate &_d)
145 {
146     //Save the JD fraction
147     long double jdFrac = djd() - static_cast<long double>(date().toJulianDay());
148 
149     //set the integer portion of the JD and add back the JD fraction:
150     setDJD(static_cast<long double>(_d.toJulianDay()) + jdFrac);
151 }
152 
addSecs(double s) const153 KStarsDateTime KStarsDateTime::addSecs(double s) const
154 {
155     long double ds = static_cast<long double>(s) / 86400.;
156     KStarsDateTime kdt(djd() + ds);
157     kdt.setTimeSpec(timeSpec());
158     return kdt;
159 }
160 
setTime(const QTime & _t)161 void KStarsDateTime::setTime(const QTime &_t)
162 {
163     KStarsDateTime _dt(date(), _t, timeSpec());
164     setDJD(_dt.djd());
165 }
166 
gst() const167 dms KStarsDateTime::gst() const
168 {
169     dms gst0 = GSTat0hUT();
170 
171     double hr = double(time().hour() - offsetFromUtc() / 3600.0);
172     double mn = double(time().minute());
173     double sc = double(time().second()) + double(0.001 * time().msec());
174     double st = (hr + (mn + sc / 60.0) / 60.0) * SIDEREALSECOND;
175 
176     dms gst = dms(gst0.Degrees() + st * 15.0).reduce();
177     return gst;
178 }
179 
GSTat0hUT() const180 dms KStarsDateTime::GSTat0hUT() const
181 {
182     double sinOb, cosOb;
183 
184     // Mean greenwich sidereal time
185     KStarsDateTime t0(date(), QTime(0, 0, 0));
186     long double s = t0.djd() - J2000;
187     double t      = s / 36525.0;
188     double t1     = 6.697374558 + 2400.051336 * t + 0.000025862 * t * t + 0.000000002 * t * t * t;
189 
190     // To obtain the apparent sidereal time, we have to correct the
191     // mean greenwich sidereal time with nutation in longitude multiplied
192     // by the cosine of the obliquity of the ecliptic. This correction
193     // is called nutation in right ascention, and may amount to 0.3 secs.
194     KSNumbers num(t0.djd());
195     num.obliquity()->SinCos(sinOb, cosOb);
196 
197     // nutLong has to be in hours of time since t1 is hours of time.
198     double nutLong = num.dEcLong() * cosOb / 15.0;
199     t1 += nutLong;
200 
201     dms gst;
202     gst.setH(t1);
203     return gst.reduce();
204 }
205 
GSTtoUT(dms GST) const206 QTime KStarsDateTime::GSTtoUT(dms GST) const
207 {
208     dms gst0 = GSTat0hUT();
209 
210     //dt is the number of sidereal hours since UT 0h.
211     double dt = GST.Hours() - gst0.Hours();
212     while (dt < 0.0)
213         dt += 24.0;
214     while (dt >= 24.0)
215         dt -= 24.0;
216 
217     //convert to solar time.  dt is now the number of hours since 0h UT.
218     dt /= SIDEREALSECOND;
219 
220     int hr = int(dt);
221     int mn = int(60.0 * (dt - double(hr)));
222     int sc = int(60.0 * (60.0 * (dt - double(hr)) - double(mn)));
223     int ms = int(1000.0 * (60.0 * (60.0 * (dt - double(hr)) - double(mn)) - double(sc)));
224 
225     return (QTime(hr, mn, sc, ms));
226 }
227 
setFromEpoch(double epoch)228 void KStarsDateTime::setFromEpoch(double epoch)
229 {
230     if (epoch == 1950.0) // Assume Besselian
231         setFromEpoch(epoch, BESSELIAN);
232     else
233         setFromEpoch(epoch, JULIAN); // Assume Julian
234 }
235 
setFromEpoch(double epoch,EpochType type)236 bool KStarsDateTime::setFromEpoch(double epoch, EpochType type)
237 {
238     if (type != JULIAN && type != BESSELIAN)
239         return false;
240     else
241         setDJD(epochToJd(epoch, type));
242     return true;
243 }
244 
setFromEpoch(const QString & eName)245 bool KStarsDateTime::setFromEpoch(const QString &eName)
246 {
247     bool result;
248     double epoch;
249     epoch = stringToEpoch(eName, result);
250 
251     if (!result)
252         return false;
253     return setFromEpoch(epoch, JULIAN); // We've already converted
254 }
255 
epochToJd(double epoch,EpochType type)256 long double KStarsDateTime::epochToJd(double epoch, EpochType type)
257 {
258     switch (type)
259     {
260         case BESSELIAN:
261             return B1900 + (epoch - 1900.0) * JD_PER_BYEAR;
262         case JULIAN:
263             return J2000 + (epoch - 2000.0) * 365.25;
264         default:
265             return NaN::d;
266     }
267 }
268 
jdToEpoch(long double jd,KStarsDateTime::EpochType type)269 double KStarsDateTime::jdToEpoch(long double jd, KStarsDateTime::EpochType type)
270 {
271     // Definitions for conversion formulas are from:
272     //
273     // * http://scienceworld.wolfram.com/astronomy/BesselianEpoch.html
274     // * http://scienceworld.wolfram.com/astronomy/JulianEpoch.html
275     //
276 
277     switch (type)
278     {
279         case KStarsDateTime::BESSELIAN:
280             return 1900.0 + (jd - KStarsDateTime::B1900) / KStarsDateTime::JD_PER_BYEAR;
281         case KStarsDateTime::JULIAN:
282             return 2000.0 + (jd - J2000) / 365.24;
283         default:
284             return NaN::d;
285     }
286 }
287 
stringToEpoch(const QString & eName,bool & ok)288 double KStarsDateTime::stringToEpoch(const QString &eName, bool &ok)
289 {
290     double epoch = J2000;
291     ok           = false;
292 
293     if (eName.isEmpty()) // By default, assume J2000
294         return epoch;
295 
296     if (eName.startsWith('J'))
297         epoch = eName.midRef(1).toDouble(&ok);
298     else if (eName.startsWith('B'))
299     {
300         epoch = eName.midRef(1).toDouble(&ok);
301         epoch = jdToEpoch(epochToJd(epoch, BESSELIAN), JULIAN); // Convert Besselian epoch to Julian epoch
302     }
303     // Assume it's Julian
304     else
305         epoch = eName.toDouble(&ok);
306 
307     return epoch;
308 }
309