master ohmu/fastfetch-openbsd / src / modules / datetime / datetime.c
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};