ohmu
·
2026-10-15
1#include "common/time.h"
2#include "common/printing.h"
3#include "common/jsonconfig.h"
4#include "common/strutil.h"
5#include "modules/datetime/datetime.h"
6
7#include <time.h>
8
9#pragma GCC diagnostic ignored "-Wformat" // warning: unknown conversion type character 'F' in format
10
11typedef struct FFDateTimeResult {
12 // Examples for 21.02.2022 - 15:18:37
13 uint16_t year; // 2022
14 uint8_t yearShort; // 22
15 uint8_t month; // 2
16 char monthPretty[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // 02
17 char monthName[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // February
18 char monthNameShort[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // Feb
19 uint8_t week; // 8
20 char weekday[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // Monday
21 char weekdayShort[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // Mon
22 uint16_t dayInYear; // 52
23 uint8_t dayInMonth; // 21
24 uint8_t dayInWeek; // 1
25 char dayPretty[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // 01
26 uint8_t hour; // 15
27 char hourPretty[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // 15
28 uint8_t hour12; // 3
29 char hour12Pretty[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // 03
30 uint8_t minute; // 18
31 char minutePretty[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // 18
32 uint8_t second; // 37
33 char secondPretty[FASTFETCH_STRBUF_DEFAULT_ALLOC]; // 37
34 char offsetFromUtc[FASTFETCH_STRBUF_DEFAULT_ALLOC];
35 char timezoneName[FASTFETCH_STRBUF_DEFAULT_ALLOC];
36 char amPm[FASTFETCH_STRBUF_DEFAULT_ALLOC];
37} FFDateTimeResult;
38
39static void printDateTimeFormat(struct tm* tm, const FFModuleArgs* moduleArgs) {
40 FFDateTimeResult result;
41
42 result.year = (uint16_t) (tm->tm_year + 1900);
43 result.yearShort = (uint8_t) (result.year % 100);
44 result.month = (uint8_t) (tm->tm_mon + 1);
45 strftime(result.monthPretty, sizeof(result.monthPretty), "%m", tm);
46 strftime(result.monthName, sizeof(result.monthName), "%B", tm);
47 strftime(result.monthNameShort, sizeof(result.monthNameShort), "%b", tm);
48 result.week = (uint8_t) (tm->tm_yday / 7 + 1);
49 strftime(result.weekday, sizeof(result.weekday), "%A", tm);
50 strftime(result.weekdayShort, sizeof(result.weekdayShort), "%a", tm);
51 result.dayInYear = (uint16_t) (tm->tm_yday + 1);
52 result.dayInMonth = (uint8_t) tm->tm_mday;
53 result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday;
54 strftime(result.dayPretty, sizeof(result.dayPretty), "%d", tm);
55 result.hour = (uint8_t) tm->tm_hour;
56 strftime(result.hourPretty, sizeof(result.hourPretty), "%H", tm);
57 result.hour12 = (uint8_t) (result.hour % 12 ?: 12);
58 strftime(result.hour12Pretty, sizeof(result.hour12Pretty), "%I", tm);
59 result.minute = (uint8_t) tm->tm_min;
60 strftime(result.minutePretty, sizeof(result.minutePretty), "%M", tm);
61 result.second = (uint8_t) tm->tm_sec;
62 strftime(result.secondPretty, sizeof(result.secondPretty), "%S", tm);
63 strftime(result.offsetFromUtc, sizeof(result.offsetFromUtc), "%z", tm);
64 strftime(result.timezoneName, sizeof(result.timezoneName), "%Z", tm);
65 strftime(result.amPm, sizeof(result.amPm), "%p", tm);
66
67 FF_PRINT_FORMAT_CHECKED(
68 FF_MODULE_GET_DISPLAY_NAME(DateTime),
69 0,
70 moduleArgs,
71 FF_PRINT_TYPE_DEFAULT,
72 ((FFformatarg[]){
73 FF_ARG(result.year, "year"), // 1
74 FF_ARG(result.yearShort, "year-short"), // 2
75 FF_ARG(result.month, "month"), // 3
76 FF_ARG(result.monthPretty, "month-pretty"), // 4
77 FF_ARG(result.monthName, "month-name"), // 5
78 FF_ARG(result.monthNameShort, "month-name-short"), // 6
79 FF_ARG(result.week, "week"), // 7
80 FF_ARG(result.weekday, "weekday"), // 8
81 FF_ARG(result.weekdayShort, "weekday-short"), // 9
82 FF_ARG(result.dayInYear, "day-in-year"), // 10
83 FF_ARG(result.dayInMonth, "day-in-month"), // 11
84 FF_ARG(result.dayInWeek, "day-in-week"), // 12
85 FF_ARG(result.hour, "hour"), // 13
86 FF_ARG(result.hourPretty, "hour-pretty"), // 14
87 FF_ARG(result.hour12, "hour-12"), // 15
88 FF_ARG(result.hour12Pretty, "hour-12-pretty"), // 16
89 FF_ARG(result.minute, "minute"), // 17
90 FF_ARG(result.minutePretty, "minute-pretty"), // 18
91 FF_ARG(result.second, "second"), // 19
92 FF_ARG(result.secondPretty, "second-pretty"), // 20
93 FF_ARG(result.offsetFromUtc, "offset-from-utc"), // 21
94 FF_ARG(result.timezoneName, "timezone-name"), // 22
95 FF_ARG(result.dayPretty, "day-pretty"), // 23
96 FF_ARG(result.amPm, "am-pm"), // 24
97 }));
98}
99
100bool ffPrintDateTime(FFDateTimeOptions* options) {
101 uint64_t msNow = ffTimeGetNow();
102 time_t sNow = (time_t) (msNow / 1000);
103 struct tm* tm = localtime(&sNow);
104
105 if (options->moduleArgs.outputFormat.length > 0) {
106 printDateTimeFormat(tm, &options->moduleArgs);
107 return true;
108 }
109
110 char buffer[32];
111 if (strftime(buffer, ARRAY_SIZE(buffer), "%F %T", tm) == 0) // yyyy-MM-dd HH:mm:ss
112 {
113 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "strftime() failed");
114 return false;
115 }
116
117 ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
118
119 puts(buffer);
120 return true;
121}
122
123void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module) {
124 yyjson_val *key, *val;
125 size_t idx, max;
126 yyjson_obj_foreach (module, idx, max, key, val) {
127 if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
128 continue;
129 }
130
131 ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
132 }
133}
134
135[[gnu::cold]]
136void ffGenerateDateTimeJsonConfig(FFDateTimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
137 ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
138}
139
140bool ffGenerateDateTimeJsonResult([[maybe_unused]] FFDateTimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
141 yyjson_mut_obj_add_strcpy(doc, module, "result", ffTimeToFullStr(ffTimeGetNow()));
142 return true;
143}
144
145void ffInitDateTimeOptions(FFDateTimeOptions* options) {
146 ffOptionInitModuleArg(&options->moduleArgs, "");
147}
148
149void ffDestroyDateTimeOptions(FFDateTimeOptions* options) {
150 ffOptionDestroyModuleArg(&options->moduleArgs);
151}
152
153FFModuleBaseInfo ffDateTimeModuleInfo = {
154 .name = "DateTime",
155 .description = "Print the current date and time",
156 .displayName = {
157 .en = "Date & Time",
158 .ar = "التاريخ والوقت",
159 .cs = "Datum a čas",
160 .de = "Datum & Uhrzeit",
161 .es = "Fecha y hora",
162 .fi = "Päivämäärä ja kellonaika",
163 .fr = "Date et heure",
164 .gl = "Data e hora",
165 .he = "תאריך ושעה",
166 .id = "Tanggal & Waktu",
167 .it = "Data e ora",
168 .ja = "日付と時刻",
169 .ko = "날짜/시간",
170 .pl = "Data i godzina",
171 .pt = "Data e hora",
172 .ru = "Дата и время",
173 .tr = "Tarih ve Saat",
174 .uk = "Дата й час",
175 .vi = "Ngày giờ",
176 .zh_CN = "日期时间",
177 .zh_TW = "日期時間",
178 },
179 .initOptions = (void*) ffInitDateTimeOptions,
180 .destroyOptions = (void*) ffDestroyDateTimeOptions,
181 .parseJsonObject = (void*) ffParseDateTimeJsonObject,
182 .printModule = (void*) ffPrintDateTime,
183 .generateJsonResult = (void*) ffGenerateDateTimeJsonResult,
184 .generateJsonConfig = (void*) ffGenerateDateTimeJsonConfig,
185 .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]){
186 { "Year", "year" },
187 { "Last two digits of year", "year-short" },
188 { "Month", "month" },
189 { "Month with leading zero", "month-pretty" },
190 { "Month name", "month-name" },
191 { "Month name short", "month-name-short" },
192 { "Week number on year", "week" },
193 { "Weekday", "weekday" },
194 { "Weekday short", "weekday-short" },
195 { "Day in year", "day-in-year" },
196 { "Day in month", "day-in-month" },
197 { "Day in week", "day-in-week" },
198 { "Hour", "hour" },
199 { "Hour with leading zero", "hour-pretty" },
200 { "Hour 12h format", "hour-12" },
201 { "Hour 12h format with leading zero", "hour-12-pretty" },
202 { "Minute", "minute" },
203 { "Minute with leading zero", "minute-pretty" },
204 { "Second", "second" },
205 { "Second with leading zero", "second-pretty" },
206 { "Offset from UTC in the ISO 8601 format", "offset-from-utc" },
207 { "Locale-dependent timezone name or abbreviation", "timezone-name" },
208 { "Day in month with leading zero", "day-pretty" },
209 { "AM or PM", "am-pm" },
210 })),
211 .defaultOrder = 53,
212};