4 * Implements the clock command
7 #include "jimautoconf.h"
9 /* For strptime() - currently nothing sets this */
10 #ifdef STRPTIME_NEEDS_XOPEN_SOURCE
12 #define _XOPEN_SOURCE 500
26 #include <jim-subcmd.h>
28 #ifdef HAVE_SYS_TIME_H
32 struct clock_options
{
37 /* Parses the options ?-format string? ?-gmt boolean? and fills in *opts.
38 * Any options not present are not set.
41 * Returns JIM_OK or JIM_ERR and sets an error result.
43 static int parse_clock_options(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
, struct clock_options
*opts
)
45 static const char * const options
[] = { "-gmt", "-format", NULL
};
46 enum { OPT_GMT
, OPT_FORMAT
, };
49 for (i
= 0; i
< argc
; i
+= 2) {
51 if (Jim_GetEnum(interp
, argv
[i
], options
, &option
, NULL
, JIM_ERRMSG
| JIM_ENUM_ABBREV
) != JIM_OK
) {
56 if (Jim_GetBoolean(interp
, argv
[i
+ 1], &opts
->gmt
) != JIM_OK
) {
61 opts
->format
= Jim_String(argv
[i
+ 1]);
68 static int clock_cmd_format(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
70 /* How big is big enough? */
74 struct clock_options options
= { 0, "%a %b %d %H:%M:%S %Z %Y" };
77 if (Jim_GetWide(interp
, argv
[0], &seconds
) != JIM_OK
) {
83 if (parse_clock_options(interp
, argc
- 1, argv
+ 1, &options
) == JIM_ERR
) {
88 tm
= options
.gmt
? gmtime(&t
) : localtime(&t
);
90 if (tm
== NULL
|| strftime(buf
, sizeof(buf
), options
.format
, tm
) == 0) {
91 Jim_SetResultString(interp
, "format string too long or invalid time", -1);
95 Jim_SetResultString(interp
, buf
, -1);
101 /* Implement timegm() that doesn't require messing with timezone
102 * Based on: http://howardhinnant.github.io/date_algorithms.html#days_from_civil
104 static time_t jim_timegm(const struct tm
*tm
)
106 int m
= tm
->tm_mon
+ 1;
107 int y
= 1900 + tm
->tm_year
- (m
<= 2);
108 int era
= (y
>= 0 ? y
: y
- 399) / 400;
109 unsigned yoe
= (unsigned)(y
- era
* 400);
110 unsigned doy
= (153 * (m
+ (m
> 2 ? -3 : 9)) + 2) / 5 + tm
->tm_mday
- 1;
111 unsigned doe
= yoe
* 365 + yoe
/ 4 - yoe
/ 100 + doy
;
112 long days
= (era
* 146097 + (int)doe
- 719468);
113 int secs
= tm
->tm_hour
* 3600 + tm
->tm_min
* 60 + tm
->tm_sec
;
115 return days
* 24 * 60 * 60 + secs
;
118 static int clock_cmd_scan(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
122 time_t now
= time(NULL
);
123 /* No default format */
124 struct clock_options options
= { 0, NULL
};
130 if (parse_clock_options(interp
, argc
- 1, argv
+ 1, &options
) == JIM_ERR
) {
133 if (options
.format
== NULL
) {
137 localtime_r(&now
, &tm
);
139 pt
= strptime(Jim_String(argv
[0]), options
.format
, &tm
);
140 if (pt
== 0 || *pt
!= 0) {
141 Jim_SetResultString(interp
, "Failed to parse time according to format", -1);
145 /* Now convert into a time_t */
146 Jim_SetResultInt(interp
, options
.gmt
? jim_timegm(&tm
) : mktime(&tm
));
152 static int clock_cmd_seconds(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
154 Jim_SetResultInt(interp
, time(NULL
));
159 static int clock_cmd_micros(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
163 gettimeofday(&tv
, NULL
);
165 Jim_SetResultInt(interp
, (jim_wide
) tv
.tv_sec
* 1000000 + tv
.tv_usec
);
170 static int clock_cmd_millis(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
174 gettimeofday(&tv
, NULL
);
176 Jim_SetResultInt(interp
, (jim_wide
) tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000);
181 static const jim_subcmd_type clock_command_table
[] = {
187 /* Description: Returns the current time in 'clicks' */
190 "seconds ?-format string? ?-gmt boolean?",
194 /* Description: Format the given time */
201 /* Description: Returns the current time in microseconds */
208 /* Description: Returns the current time in milliseconds */
212 "str -format format ?-gmt boolean?",
216 /* Description: Determine the time according to the given format */
224 /* Description: Returns the current time as seconds since the epoch */
229 int Jim_clockInit(Jim_Interp
*interp
)
231 if (Jim_PackageProvide(interp
, "clock", "1.0", JIM_ERRMSG
))
234 Jim_CreateCommand(interp
, "clock", Jim_SubCmdProc
, (void *)clock_command_table
, NULL
);