1 """
2 Helpers for time parsing and conversion.
3 """
4
5
6
7
8
9
10
11 import bisect
12 import datetime
13 import math
14
15 from gavo import utils
16 from gavo.stc import common
17
18
19 JD_MJD = 2400000.5
26
30 """returns a ``datetime.datetime`` instance for a julian day number.
31 """
32 return jYearToDateTime((jd-2451545.0)/365.25+2000.0)
33
37 """returns a ``datetime.datetime`` instance for a modified julian day number.
38
39 Beware: This loses a couple of significant digits due to transformation
40 to jd.
41 """
42 return jdnToDateTime(mjd+JD_MJD)
43
47 """returns a datetime.datetime instance for a fractional Besselian year.
48
49 This uses the formula given by Lieske, J.H., A&A 73, 282 (1979).
50 """
51 jdn = (bYear-1900.0)*common.tropicalYear+2415020.31352
52 return jdnToDateTime(jdn)
53
57 """returns a datetime.datetime instance for a fractional (julian) year.
58
59 This refers to time specifications like J2001.32.
60 """
61 return datetime.datetime(2000, 1, 1, 12)+datetime.timedelta(
62 days=(jYear-2000.0)*365.25)
63
64
65 dtJ2000 = jYearToDateTime(2000.0)
66 dtB1950 = bYearToDateTime(1950.0)
70 """returns a julian day number (including fractionals) from a datetime
71 instance.
72 """
73 a = (14-dt.month)//12
74 y = dt.year+4800-a
75 m = dt.month+12*a-3
76 jdn = dt.day+(153*m+2)//5+365*y+y//4-y//100+y//400-32045
77 try:
78 secsOnDay = dt.hour*3600+dt.minute*60+dt.second+dt.microsecond/1e6
79 except AttributeError:
80 secsOnDay = 0
81 return jdn+(secsOnDay-43200)/86400.
82
88
91
95 """returns a fractional (julian) year for a datetime.datetime instance.
96 """
97 return (dateTimeToJdn(dt)-2451545)/365.25+2000
98
101 """returns the number of seconds corresponding to a timedelta object.
102 """
103 return td.days*86400+td.seconds+td.microseconds*1e-6
104
105
106
107
108
109 _TDTminusTAI = datetime.timedelta(seconds=32.184)
113 """returns TAI for a (datetime.datetime) TDT.
114 """
115 return tdt+_TDTminusTAI
116
119 """returns TDT for a (datetime.datetime) TAI.
120 """
121 return tai-_TDTminusTAI
122
125 """returns the TDB-TDT according to [EXS] 2.222-1.
126 """
127 g = (357.53+0.9856003*(dateTimeToJdn(tdb)-2451545.0))/180*math.pi
128 return datetime.timedelta(0.001658*math.sin(g)+0.000014*math.sin(2*g))
129
131 """returns an approximate TT from a TDB.
132
133 The simplified formula 2.222-1 from [EXS] is used.
134 """
135 return tdb-_getTDBOffset(tdb)
136
138 """returns approximate TDB from TT.
139
140 The simplified formula 2.222-1 from [EXS] is used.
141 """
142 return tt+_getTDBOffset(tt)
143
144
145 _L_G = 6.969291e-10
148 return datetime.timedelta(seconds=
149 _L_G*(dateTimeToJdn(dt)-2443144.5)*86400)
150
152 """returns TT from TCG.
153
154 This uses 2.223-5 from [EXS].
155 """
156 return tt+_getTCGminusTT(tt)
157
159 """returns TT from TCG.
160
161 This uses 2.223-5 from [EXS].
162 """
163 return tcg+_getTCGminusTT(tcg)
164
165
166 _L_B = 1.550505e-8
169 return datetime.timedelta(
170 seconds=_L_B*(dateTimeToJdn(dt)-2443144.5)*86400)
171
173 """returns an approximate TCB from a TT.
174
175 This uses [EXS] 2.223-2 and the approximate conversion from TDB to TT.
176 """
177 return TDBtoTT(tcb+_getTCBminusTDB(tcb))
178
180 """returns an approximate TT from a TCB.
181
182 This uses [EXS] 2.223-2 and the approximate conversion from TT to TDB.
183 """
184 return TTtoTDB(tt)-_getTCBminusTDB(tt)
185
188 lsTable = []
189 for lsCount, lsMoment in enumerate([
190 datetime.datetime(1971, 12, 31, 23, 59, 59),
191 datetime.datetime(1972, 06, 30, 23, 59, 59),
192 datetime.datetime(1972, 12, 31, 23, 59, 59),
193 datetime.datetime(1973, 12, 31, 23, 59, 59),
194 datetime.datetime(1974, 12, 31, 23, 59, 59),
195 datetime.datetime(1975, 12, 31, 23, 59, 59),
196 datetime.datetime(1976, 12, 31, 23, 59, 59),
197 datetime.datetime(1977, 12, 31, 23, 59, 59),
198 datetime.datetime(1978, 12, 31, 23, 59, 59),
199 datetime.datetime(1979, 12, 31, 23, 59, 59),
200 datetime.datetime(1981, 06, 30, 23, 59, 59),
201 datetime.datetime(1982, 06, 30, 23, 59, 59),
202 datetime.datetime(1983, 06, 30, 23, 59, 59),
203 datetime.datetime(1985, 06, 30, 23, 59, 59),
204 datetime.datetime(1987, 12, 31, 23, 59, 59),
205 datetime.datetime(1989, 12, 31, 23, 59, 59),
206 datetime.datetime(1990, 12, 31, 23, 59, 59),
207 datetime.datetime(1992, 06, 30, 23, 59, 59),
208 datetime.datetime(1993, 06, 30, 23, 59, 59),
209 datetime.datetime(1994, 06, 30, 23, 59, 59),
210 datetime.datetime(1995, 12, 31, 23, 59, 59),
211 datetime.datetime(1997, 06, 30, 23, 59, 59),
212 datetime.datetime(1998, 12, 31, 23, 59, 59),
213 datetime.datetime(2005, 12, 31, 23, 59, 59),
214 datetime.datetime(2008, 12, 31, 23, 59, 59),
215 ]):
216 lsTable.append((lsMoment, datetime.timedelta(seconds=lsCount+10)))
217 return lsTable
218
219
220 leapSecondTable = _makeLeapSecondTable()
221 del _makeLeapSecondTable
222 _sentinelTD = datetime.timedelta(seconds=0)
225 """returns TAI-UTC for the datetime dt.
226 """
227 ind = bisect.bisect_left(leapSecondTable, (dt, _sentinelTD))
228 if ind==0:
229 return datetime.timedelta(seconds=9.)
230 return table[ind-1][1]
231
234 """returns TT from UTC.
235
236 The leap second table is complete through 2009-5.
237
238 >>> getLeapSeconds(datetime.datetime(1998,12,31,23,59,58))
239 datetime.timedelta(0, 31)
240 >>> TTtoTAI(UTCtoTT(datetime.datetime(1998,12,31,23,59,59)))
241 datetime.datetime(1999, 1, 1, 0, 0, 30)
242 >>> TTtoTAI(UTCtoTT(datetime.datetime(1999,1,1,0,0,0)))
243 datetime.datetime(1999, 1, 1, 0, 0, 32)
244 """
245 return TAItoTT(utc+getLeapSeconds(utc))
246
247
248
249 ttLeapSecondTable = [(UTCtoTT(t), dt)
250 for t, dt in leapSecondTable]
254 """returns UTC from TT.
255
256 The leap second table is complete through 2009-5.
257
258 >>> TTtoUTC(UTCtoTT(datetime.datetime(1998,12,31,23,59,59)))
259 datetime.datetime(1998, 12, 31, 23, 59, 59)
260 >>> TTtoUTC(UTCtoTT(datetime.datetime(1999,1,1,0,0,0)))
261 datetime.datetime(1999, 1, 1, 0, 0)
262 """
263
264
265 return TTtoTAI(tt)-getLeapSeconds(tt, ttLeapSecondTable)
266
267
268
269 timeConversions = {
270 "UTC": (UTCtoTT, TTtoUTC),
271 "TCB": (TCBtoTT, TTtoTCB),
272 "TCG": (TCGtoTT, TTtoTCG),
273 "TDB": (TDBtoTT, TTtoTDB),
274 "TAI": (TAItoTT, TTtoTAI),
275 "TT": (utils.identity, utils.identity),
276 }
291
292 return transform
293
299
302 import time
303
304
305
306 if (colDesc["dbtype"]=="timestamp"
307 or colDesc["dbtype"]=="date"
308
309 or colDesc.original.xtype=="adql:TIMESTAMP"
310 or colDesc.original.xtype=="timestamp"):
311 unit = colDesc["unit"]
312 if (colDesc["displayHint"].get("format")=="humanDate"
313 or colDesc.original.xtype=="adql:TIMESTAMP"
314 or colDesc.original.xtype=="timestamp"):
315 fun = lambda val: (val and val.isoformat()) or None
316 destType = ("char", "*")
317 colDesc["nullvalue"] = ""
318
319 elif (colDesc["ucd"] and "MJD" in colDesc["ucd"].upper()
320 or colDesc["xtype"]=="mjd"
321 or "mjd" in colDesc["name"]):
322 colDesc["unit"] = "d"
323 fun = lambda val: (val and dateTimeToMJD(val))
324 destType = ("double", '1')
325 colDesc["nullvalue"] = "NaN"
326 colDesc["xtype"] = None
327
328 elif unit=="yr" or unit=="a":
329 fun = lambda val: (val and dateTimeToJYear(val))
330 def fun(val):
331 return (val and dateTimeToJYear(val))
332 return str(val)
333 destType = ("double", '1')
334 colDesc["nullvalue"] = "NaN"
335 colDesc["xtype"] = None
336
337 elif unit=="d":
338 fun = lambda val: (val and dateTimeToJdn(val))
339 destType = ("double", '1')
340 colDesc["nullvalue"] = "NaN"
341 colDesc["xtype"] = None
342
343 elif unit=="s":
344 fun = lambda val: (val and time.mktime(val.timetuple()))
345 destType = ("double", '1')
346 colDesc["nullvalue"] = "NaN"
347 colDesc["xtype"] = None
348
349 else:
350
351 fun = lambda val: (val and val.isoformat()) or None
352 destType = ("char", "*")
353 colDesc["nullvalue"] = ""
354 colDesc["xtype"] = "timestamp"
355
356 colDesc["datatype"], colDesc["arraysize"] = destType
357 return fun
358 utils.registerDefaultMF(datetimeMapperFactory)
365
366 if __name__=="__main__":
367 _test()
368