1 |
/* |
2 |
* time_convert - change TAI time since 1977 into ascii with a given timezone |
3 |
* and from an ascii time to seconds. Takes one argument of time or seconds and an optional |
4 |
* flag argument of time zone. Default is UTC for conversion to ascii. |
5 |
*/ |
6 |
|
7 |
/** |
8 |
@defgroup time_convert time_convert |
9 |
@ingroup su_util |
10 |
|
11 |
@brief Convert among the internal DRMS time representation and other time representations. |
12 |
|
13 |
@par Synopsis: |
14 |
@code |
15 |
time_convert -h |
16 |
time_convert s=<secondsJSOC> | sdo=<secondsSDO> | egse=<secondsEGSE> | time=<calenderTime> | |
17 |
ord=<ordinalDate> [o=jsoc | o=sdo | o=egse | o=ord | o=cal] [zone=<zone>] [p=<precision>] |
18 |
|
19 |
@endcode |
20 |
|
21 |
Converts internal DRMS time (seconds since 15 seconds |
22 |
before January 1, 1977 UTC) to an external ascii string |
23 |
representation. The exact external representation depends on the command-line |
24 |
arguments provided to this utility. Possiblities include: <tt>SDO time</tt>, |
25 |
<tt>EGSE time</tt>, <tt>Ordinal date</tt>, and <tt>Calendar time</tt>. |
26 |
<tt>SDO time</tt> is the number of seconds that have elapsed since |
27 |
January 1, 1958 TAI, i.e. SDO onboard time. The full format is the string |
28 |
representation of a double data type. <tt>EGSE time</tt> is |
29 |
the number of seconds that have elapsed since @e APPROXIMATELY January 1, 2004 UTC |
30 |
(actual epoch is 2003.12.30_23:59:36.000_UTC). The full format is also the string |
31 |
representation of a double data type. <tt>Ordinal date</tt> is |
32 |
the day number of the year (starting at day 1 on January 1). The full format |
33 |
is YYYY.DDD[_ZZZ], where YYYY is the year, DDD is the day number, and ZZZ is the |
34 |
zone (e.g., UTC, TDT, PDT, etc.). <tt>Calendar time</tt> gives the year, month, date, hour, |
35 |
minutes, and seconds for a given system of time (like @e UTC). The full format is |
36 |
specified in <tt>JSOC TN 07-001</tt> (http://jsoc.stanford.edu/doc/timerep.html), |
37 |
but in short it looks like: |
38 |
|
39 |
@par |
40 |
1. year.month.fracday[_type] |
41 |
@par |
42 |
2. year.month.day_hour:minute[:second][_type] |
43 |
@par |
44 |
3. {MJD|JD}_julday[_type] |
45 |
|
46 |
where @a type refers to the time system or time zone (e.g., @e UTC, or @e PST), @e MJD and |
47 |
@e JD refer to Modified Julian Date and Julian Date, and @e julday refers to a Julian |
48 |
day number. |
49 |
|
50 |
The precision of the seconds field is specified with the <tt>p</tt> parameter. The default is 0. Setting p=3 will produce output identical to the original version of time_convert. |
51 |
|
52 |
Alternatively, and with the appropriate command-line parameters, @ref time_convert |
53 |
converts from any supported time representation to any other representation. |
54 |
|
55 |
If multiple input format strings are specified as arguments |
56 |
(e.g., time_convert s=234235235.35 ord=1982.035), only one will be used. A descriptive |
57 |
string will be printed describing which input string was used. Given a definitive |
58 |
input format, @ref time_convert chooses a default output format. If the input is |
59 |
an internal time, an SDO time, an EGSE time, or an ordinal date, the default output |
60 |
is a calendar time. If the definitive input format is a calendar time, the default |
61 |
output is an internal time. The default output format can be overwritten |
62 |
by providing the @a o=format argument. |
63 |
|
64 |
When the output format is either an ordinal time or a calendar time, the |
65 |
time can be expressed in any of several supported time systems (e.g., @e UTC, |
66 |
@e TDT, @e TAI, or even a time zone, like @e PDT). This is accomplished by |
67 |
supplying the appropriate @a zone=system argument. Refer to |
68 |
<tt>JSOC TN 07-001</tt> (http://jsoc.stanford.edu/doc/timerep.html) for a |
69 |
complete list of the supported "zones". |
70 |
|
71 |
The default time format is with no fractions of seconds. If o=jsoc is specified the seconds are provided to the nearest ms. |
72 |
|
73 |
@par Flags: |
74 |
@c -h: Show usage message. |
75 |
|
76 |
@param s An input time formatted as an internal time. |
77 |
<secondsJSOC> is seconds since 15 seconds before January 1, 1977 UTC. |
78 |
@param sdo An input time formatted as an SDO time. |
79 |
<secondsSDO> is seconds since January 1, 1958 TAI. |
80 |
@param egse An input time formatted as an EGSE time. |
81 |
<secondsEGSE> is seconds since 2003.12.30_23:59:36.000_UTC |
82 |
@param time An input time formatted as a calendar time. |
83 |
<calenderTime> is as specified in |
84 |
<tt>JSOC TN 07-001</tt> (http://jsoc.stanford.edu/doc/timerep.html) |
85 |
@param ord An input time formatted as an ordinal date. |
86 |
<ordinalDate> is yyyy.ddd[_zone], where @a zone is any supported |
87 |
time system as specified in <tt>JSOC TN 07-001</tt> |
88 |
(http://jsoc.stanford.edu/doc/timerep.html). |
89 |
@param o The output format to be used. "jsoc" refers to internal time; |
90 |
"sdo" refers to SDO time; "egse" refers to EGSE time; |
91 |
"ord" refers to ordinal date; and "cal" refers to calendar time. |
92 |
|
93 |
@par Example to convert an internal time to the default output format (UTC calendar time): |
94 |
@code |
95 |
time_convert s=234253535.23 |
96 |
@endcode |
97 |
|
98 |
@par Example to convert an internal time to the default output format (calendar time), but in TAI time: |
99 |
@code |
100 |
time_convert s=234253535.23 zone=TAI |
101 |
@endcode |
102 |
|
103 |
@par Example to convert an internal time to an EGSE time: |
104 |
@code |
105 |
time_convert s=234253535.23 o=egse |
106 |
@endcode |
107 |
|
108 |
@par Example to convert an EGSE time to a calendar time in the PDT time zone: |
109 |
@code |
110 |
time_convert egse=232533636.362 o=cal zone=PDT |
111 |
@endcode |
112 |
|
113 |
@par Example to convert an ordinal time to a calendar time in the TDT system: |
114 |
@code |
115 |
time_convert ord=2007.352 o=cal zone=TDT |
116 |
@endcode |
117 |
|
118 |
@par Example to convert a calendar time to an internal time: |
119 |
@code |
120 |
time_convert time=1998.02.04_06:00:17.230_UTC |
121 |
@endcode |
122 |
|
123 |
@par Example to convert a calendar time to an internal time (explicitly): |
124 |
@code |
125 |
time_convert time=1998.02.04_06:00:17.230_UTC o=jsoc |
126 |
@endcode |
127 |
|
128 |
@par Example to convert a calendar time to an SDO time |
129 |
@code |
130 |
time_convert time=1998.02.04_06:00:17.230_UTC o=sdo |
131 |
@endcode |
132 |
*/ |
133 |
/* @{ */ |
134 |
#include <stdlib.h> |
135 |
#include <time.h> |
136 |
#include <errno.h> |
137 |
#include <stdio.h> |
138 |
#include "timeio.h" |
139 |
#include "jsoc.h" |
140 |
#include "cmdparams.h" |
141 |
|
142 |
ModuleArgs_t module_args[] = |
143 |
{ |
144 |
{ARG_STRING, "s", "NOT SPECIFIED", "<DSDS/JSOC time in seconds>"}, |
145 |
{ARG_STRING, "sdo", "NOT SPECIFIED", "<SDO time in seconds>"}, |
146 |
{ARG_STRING, "egse", "NOT SPECIFIED", "<EGSE time in seconds>"}, |
147 |
{ARG_STRING, "time", "NOT SPECIFIED", "<Time as yyyy.mm...>"}, |
148 |
{ARG_STRING, "ord", "NOT SPECIFIED", "<Time as yyyy.ddd...>"}, |
149 |
{ARG_STRING, "zone", "UTC", "<Time zone>"}, |
150 |
{ARG_STRING, "o", "NOT SPECIFIED", "format of time output"}, |
151 |
{ARG_INT, "p", "0", "precision of seconds for time output"}, |
152 |
{ARG_FLAG, "h", "0", "help message"}, |
153 |
{ARG_END} |
154 |
}; |
155 |
|
156 |
ModuleArgs_t *gModArgs = module_args; |
157 |
/* @} */ |
158 |
|
159 |
CmdParams_t cmdparams; |
160 |
|
161 |
#define SEC1970TO2004 1072828800 |
162 |
#define SECSPERDAY 86400.0 |
163 |
|
164 |
typedef enum |
165 |
{ |
166 |
kTimeFormat_None = 0, |
167 |
kTimeFormat_Internal, |
168 |
kTimeFormat_SDO, |
169 |
kTimeFormat_EGSE, |
170 |
kTimeFormat_Ordinal, |
171 |
kTimeFormat_Calendar |
172 |
} TimeFormat_t; |
173 |
|
174 |
char *TimeFormatStr[] = |
175 |
{ |
176 |
"None", |
177 |
"Internal/JSOC", |
178 |
"SDO", |
179 |
"EGSE", |
180 |
"Ordinal", |
181 |
"Calendar" |
182 |
}; |
183 |
|
184 |
static void PrintTime(TIME t, TimeFormat_t f, int precision); |
185 |
static TIME ZoneAdjustment(TIME t, char *zone, int precision); |
186 |
|
187 |
int nice_intro () |
188 |
{ |
189 |
int usage = cmdparams_get_int (&cmdparams, "h", NULL); |
190 |
if (usage) |
191 |
{ |
192 |
printf ("Usage:\ntime_convert -h\n" |
193 |
"time_convert s=<secondsJSOC>|sdo=<secondsSDO>|egse=<secondsEGSE>|time=<calender time>|ord=<ordinal date> [o=jsoc | o=sdo | o=egse | o=ord | o=cal] [zone=<zone>]\n" |
194 |
"<secondsJSOC> = JSOC standard internal time, i.e. secs since 1977.01.01_TAI \n" |
195 |
"<secondsSDO> = seconds since January 1, 1958 TAI, i.e. SDO onboard time\n" |
196 |
"<secondsEGSE> = seconds since APPROXIMATELY January 1, 2004 \n" |
197 |
"<calender time> = yyyy.mm.dd_hh:mm:ss<zone>\n" |
198 |
"<ordinal date> = yyyy.ddd<zone>\n" |
199 |
"<zone> = time zone as UT, TAI, PST, etc. - default is UTC\n" |
200 |
"h - show usage message\n" |
201 |
"o - output time in format specified\n" |
202 |
"p - precision of seconds printed\n"); |
203 |
return(1); |
204 |
} |
205 |
return (0); |
206 |
} |
207 |
|
208 |
int main(int argc, char **argv) |
209 |
{ |
210 |
int status; |
211 |
TIME sscan_time(char *s); |
212 |
|
213 |
char *s, *sdo, *time; |
214 |
char *ord; /* Users can now supply the time argument as an ordinal date ('day-of-year' format - ISO 8601): YYYY.DDD */ |
215 |
char *egse = NULL; |
216 |
char *oArg = NULL; |
217 |
int precision = 0; |
218 |
|
219 |
TIME t; |
220 |
|
221 |
/* Parse command line parameters */ |
222 |
status = cmdparams_parse (&cmdparams, argc, argv); |
223 |
if (status == CMDPARAMS_QUERYMODE) |
224 |
{ |
225 |
cmdparams_usage (argv[0]); |
226 |
return 0; |
227 |
} |
228 |
|
229 |
if (nice_intro ()) |
230 |
return (0); |
231 |
|
232 |
/* The are the different types of input. */ |
233 |
s = cmdparams_get_str(&cmdparams, "s", NULL); |
234 |
sdo = cmdparams_get_str(&cmdparams, "sdo", NULL); |
235 |
time = cmdparams_get_str(&cmdparams, "time", NULL); |
236 |
ord = cmdparams_get_str(&cmdparams, "ord", NULL); |
237 |
egse = cmdparams_get_str(&cmdparams, "egse", NULL); |
238 |
|
239 |
/* Determine seconds precision */ |
240 |
precision = cmdparams_get_int(&cmdparams, "p", NULL); |
241 |
|
242 |
int err = 0; |
243 |
|
244 |
TIME timeToPrint; |
245 |
TimeFormat_t inputFormat = kTimeFormat_None; |
246 |
TimeFormat_t outputFormat = kTimeFormat_None; |
247 |
TimeFormat_t outputOverride = kTimeFormat_None; |
248 |
int mFormats = 0; |
249 |
|
250 |
/* These are the different types of output. */ |
251 |
oArg = cmdparams_get_str(&cmdparams, "o", NULL); |
252 |
if (strcmp(oArg, "NOT SPECIFIED") != 0) |
253 |
{ |
254 |
if (strcmp(oArg, "jsoc") == 0) |
255 |
{ |
256 |
outputOverride = kTimeFormat_Internal; |
257 |
} |
258 |
else if (strcmp(oArg, "sdo") == 0) |
259 |
{ |
260 |
outputOverride = kTimeFormat_SDO; |
261 |
} |
262 |
else if (strcmp(oArg, "egse") == 0) |
263 |
{ |
264 |
outputOverride = kTimeFormat_EGSE; |
265 |
} |
266 |
else if (strcmp(oArg, "ord") == 0) |
267 |
{ |
268 |
outputOverride = kTimeFormat_Ordinal; |
269 |
} |
270 |
else if (strcmp(oArg, "cal") == 0) |
271 |
{ |
272 |
outputOverride = kTimeFormat_Calendar; |
273 |
} |
274 |
else |
275 |
{ |
276 |
fprintf(stderr, "Output format %s is not recognized. Using default.\n", oArg); |
277 |
} |
278 |
} |
279 |
|
280 |
if (strcmp(s, "NOT SPECIFIED")!= 0) |
281 |
{ |
282 |
/* internal seconds was specified */ |
283 |
if (inputFormat == kTimeFormat_None) |
284 |
{ |
285 |
sscanf(s, "%lf", &t); |
286 |
timeToPrint = t; |
287 |
outputFormat = kTimeFormat_Calendar; |
288 |
inputFormat = kTimeFormat_Internal; |
289 |
} |
290 |
else |
291 |
{ |
292 |
mFormats = 1; |
293 |
} |
294 |
} |
295 |
if (strcmp(sdo, "NOT SPECIFIED")!= 0) |
296 |
{ |
297 |
/* SDO seconds was specified */ |
298 |
if (inputFormat == kTimeFormat_None) |
299 |
{ |
300 |
sscanf(sdo, "%lf", &t); |
301 |
timeToPrint = t + sscan_time("1958.01.01_00:00:00_TAI"); |
302 |
outputFormat = kTimeFormat_Calendar; |
303 |
inputFormat = kTimeFormat_SDO; |
304 |
} |
305 |
else |
306 |
{ |
307 |
mFormats = 1; |
308 |
} |
309 |
} |
310 |
if (strcmp(egse, "NOT SPECIFIED") != 0) |
311 |
{ |
312 |
/* Time since ~ 1/1/2004 was provided as input */ |
313 |
if (inputFormat == kTimeFormat_None) |
314 |
{ |
315 |
sscanf(egse, "%lf", &t); |
316 |
timeToPrint = t + SEC1970TO2004 + sscan_time("1970.01.01_00:00_UTC"); |
317 |
outputFormat = kTimeFormat_Calendar; |
318 |
inputFormat = kTimeFormat_EGSE; |
319 |
} |
320 |
else |
321 |
{ |
322 |
mFormats = 1; |
323 |
} |
324 |
} |
325 |
if (strcmp(time, "NOT SPECIFIED")!= 0) |
326 |
{ |
327 |
/* calendar time string was specified */ |
328 |
if (inputFormat == kTimeFormat_None) |
329 |
{ |
330 |
t = sscan_time(time); |
331 |
timeToPrint = t; |
332 |
outputFormat = kTimeFormat_Internal; |
333 |
inputFormat = kTimeFormat_Calendar; |
334 |
} |
335 |
else |
336 |
{ |
337 |
mFormats = 1; |
338 |
} |
339 |
} |
340 |
if (ord != NULL && *ord != NULL && strcmp(ord, "NOT SPECIFIED") != 0) |
341 |
{ |
342 |
/* day-of-year was specified - convert to internal representation */ |
343 |
if (inputFormat == kTimeFormat_None) |
344 |
{ |
345 |
err = 1; |
346 |
|
347 |
long year = 0; |
348 |
int day = 0; |
349 |
char *tFormat = NULL; |
350 |
|
351 |
char *ordDate = strdup(ord); |
352 |
int timeStrLen = strlen(ordDate); |
353 |
|
354 |
if (ordDate != NULL) |
355 |
{ |
356 |
char *loc = strchr(ordDate, '.'); |
357 |
char *loc2 = strchr(ordDate, '_'); |
358 |
|
359 |
if (loc != NULL) |
360 |
{ |
361 |
*loc = '\0'; |
362 |
|
363 |
if (sscanf(ordDate, "%ld", &year) != 0) |
364 |
{ |
365 |
if (loc2 != NULL && loc2 > loc) |
366 |
{ |
367 |
*loc2 = '\0'; |
368 |
sscanf(loc + 1, "%d", &day); |
369 |
|
370 |
if (loc2 + 1 <= &ordDate[timeStrLen - 1]) |
371 |
{ |
372 |
tFormat = (char *)malloc(sizeof(char) * strlen(loc2 + 1) + 1); |
373 |
if (tFormat) |
374 |
{ |
375 |
sscanf(loc2 + 1, "%s", tFormat); |
376 |
} |
377 |
} |
378 |
} |
379 |
else if (loc + 1 <= &ordDate[timeStrLen - 1]) |
380 |
{ |
381 |
sscanf((loc + 1), "%d", &day); |
382 |
} |
383 |
|
384 |
err = (day < 1 || day > 366); |
385 |
|
386 |
if (!err) |
387 |
{ |
388 |
/* first convert to calendar date */ |
389 |
char timeBuf[64]; |
390 |
snprintf(timeBuf, sizeof(timeBuf), "%ld.01.%d_00:00_%s", year, day, tFormat ? tFormat : "UT"); |
391 |
/* then use sscan_time to convert to internal time */ |
392 |
// t = sscan_time(timeBuf) - sscan_time("1977.01.01_00:00_TAI"); |
393 |
// sscan_time("1977.01.01_00:00_TAI") == 0 |
394 |
t = sscan_time(timeBuf); |
395 |
timeToPrint = t; |
396 |
outputFormat = kTimeFormat_Internal; |
397 |
inputFormat = kTimeFormat_Ordinal; |
398 |
} |
399 |
else |
400 |
{ |
401 |
fprintf(stderr, "invalid day of year\n"); |
402 |
} |
403 |
} |
404 |
else |
405 |
{ |
406 |
fprintf(stderr, "invalid year\n"); |
407 |
} |
408 |
} |
409 |
else |
410 |
{ |
411 |
fprintf(stderr, "missing day of year\n"); |
412 |
} |
413 |
|
414 |
free(ordDate); |
415 |
} |
416 |
|
417 |
if (tFormat) |
418 |
{ |
419 |
free(tFormat); |
420 |
} |
421 |
} |
422 |
else |
423 |
{ |
424 |
mFormats = 1; |
425 |
} |
426 |
} |
427 |
|
428 |
if (inputFormat == kTimeFormat_None) |
429 |
{ |
430 |
fprintf(stderr, "No input format specified.\n"); |
431 |
err = 1; |
432 |
} |
433 |
else if (mFormats) |
434 |
{ |
435 |
fprintf(stderr, |
436 |
"Multiple input formats specified, using %s time\n", |
437 |
TimeFormatStr[inputFormat]); |
438 |
} |
439 |
|
440 |
if (outputOverride != kTimeFormat_None) |
441 |
{ |
442 |
outputFormat = outputOverride; |
443 |
} |
444 |
|
445 |
if (!err) |
446 |
{ |
447 |
PrintTime(timeToPrint, outputFormat, precision); |
448 |
} |
449 |
|
450 |
if (err) |
451 |
{ |
452 |
fprintf(stderr,"time_convert call error\n"); |
453 |
return(1); |
454 |
} |
455 |
else |
456 |
{ |
457 |
return(0); |
458 |
} |
459 |
} |
460 |
|
461 |
/* t is time in TAI seconds since 1/1/1977, f is the format in which to print the time string */ |
462 |
void PrintTime(TIME t, TimeFormat_t f, int precision) |
463 |
{ |
464 |
if (f == kTimeFormat_Calendar) |
465 |
{ |
466 |
char at[128]; |
467 |
sprint_time(at, t, cmdparams_get_str(&cmdparams, "zone", NULL), precision); |
468 |
// broken for precision == 3, causes seg fault, fixed maybe |
469 |
// sprint_time(at, t, cmdparams_get_str(&cmdparams, "zone", NULL), 0); |
470 |
printf("%s\n", at); |
471 |
} |
472 |
else if (f == kTimeFormat_SDO) |
473 |
{ |
474 |
/* no zone associated with sdo time, so zone parameter is ignored */ |
475 |
printf("%12.*f\n", precision, t - sscan_time("1958.01.01_00:00:00_TAI")); |
476 |
} |
477 |
else if (f == kTimeFormat_EGSE) |
478 |
{ |
479 |
/* no zone associated with egse time, so zone parameter is ignored */ |
480 |
printf("%12.*f\n", precision, t - SEC1970TO2004 - sscan_time("1970.01.01_00:00_UTC")); |
481 |
} |
482 |
else if (f == kTimeFormat_Ordinal) |
483 |
{ |
484 |
/* This does NOT provide a fractional day - any fractional part is discarded. */ |
485 |
int ordTimeDays = 0; |
486 |
char zoneStr[32]; |
487 |
char at[128]; |
488 |
TIME jan1 = 0; |
489 |
TIME secsSinceJan1 = 0; |
490 |
|
491 |
char *zone = cmdparams_get_str(&cmdparams, "zone", NULL); |
492 |
|
493 |
/* First, get internal time for January 1 of the year we are going to output. */ |
494 |
sprint_time(at, t, zone, precision); |
495 |
char *tz = strrchr(at, '_'); |
496 |
|
497 |
if (tz) |
498 |
{ |
499 |
snprintf(zoneStr, sizeof(zoneStr), "_%s", tz + 1); |
500 |
} |
501 |
|
502 |
char *dot = strchr(at, '.'); |
503 |
if (dot != NULL) |
504 |
{ |
505 |
*dot = '\0'; |
506 |
char timeStr[64]; |
507 |
snprintf(timeStr, sizeof(timeStr), "%s.01.01_00:00:00%s", at, zoneStr); |
508 |
jan1 = sscan_time(timeStr); |
509 |
} |
510 |
|
511 |
secsSinceJan1 = t - jan1; |
512 |
|
513 |
/* Now, get zone adjustment for jan1 and t. Subtract Adj(t) - Adj(jan1) from |
514 |
* secsSinceJan1. */ |
515 |
TIME adjJan1 = ZoneAdjustment(jan1, zone, precision); |
516 |
TIME adjT = ZoneAdjustment(t, zone, precision); |
517 |
TIME ordTimeSecs = secsSinceJan1 - (adjT - adjJan1); |
518 |
|
519 |
/* Divide by TAI secs per day (86400). */ |
520 |
ordTimeDays = 1 + ordTimeSecs / SECSPERDAY; |
521 |
|
522 |
/* Output */ |
523 |
printf("%s.%03d%s\n", at, ordTimeDays, zoneStr); |
524 |
} |
525 |
else |
526 |
{ |
527 |
if (f != kTimeFormat_Internal) |
528 |
{ |
529 |
// format error |
530 |
fprintf(stderr, "Invalid output format, defaulting to JSOC time.\n"); |
531 |
} |
532 |
printf("%0.*f\n", precision, t); |
533 |
} |
534 |
} |
535 |
|
536 |
/* <t> is the TAI-seconds equivalent of a time in the <zone> time zone. */ |
537 |
TIME ZoneAdjustment(TIME t, char *zone, int precision) |
538 |
{ |
539 |
char timestr[128]; |
540 |
sprint_time(timestr, t, zone, precision); |
541 |
|
542 |
char *tz = strrchr(timestr, '_'); |
543 |
tz[1] = 'T'; |
544 |
tz[2] = 'A'; |
545 |
tz[3] = 'I'; |
546 |
tz[4] = '\0'; |
547 |
|
548 |
TIME taiDateSecs = sscan_time(timestr); |
549 |
return t - taiDateSecs; |
550 |
} |