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