Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
datetime.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2022 Aldric Renaudin.
4 Copyright (C) 2022 HansBull.
5 Copyright (C) 2022 Martin Baƙinka.
6 Copyright (C) 2022 Philippe Weyland.
7 Copyright (C) 2024, 2026 Guillaume Stutin.
8
9 darktable is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 darktable is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with darktable. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "common/darktable.h"
24#include "common/datetime.h"
25
26#define DT_DATETIME_ORIGIN "0001-01-01 00:00:00.000"
27#define DT_DATETIME_EPOCH "1970-01-01 00:00:00.000"
28#define DT_DATETIME_EXIF_FORMAT "%Y:%m:%d %H:%M:%S"
29
31{
32 darktable.utc_tz = g_time_zone_new_utc();
33 darktable.origin_gdt = g_date_time_new_from_iso8601(DT_DATETIME_ORIGIN, darktable.utc_tz);
34}
35
36gboolean _datetime_gdatetime_to_numbers(dt_datetime_t *dt, GDateTime *gdt)
37{
38 if(gdt)
39 {
40 dt->year = g_date_time_get_year(gdt);
41 dt->month = g_date_time_get_month(gdt);
42 dt->day = g_date_time_get_day_of_month(gdt);
43 dt->hour = g_date_time_get_hour(gdt);
44 dt->minute = g_date_time_get_minute(gdt);
45 dt->second = g_date_time_get_second(gdt);
46 dt->msec = g_date_time_get_microsecond(gdt) * 0.001;
47 return TRUE;
48 }
49 return FALSE;
50}
51
52static char *_datetime_append_msec(char *exif, GDateTime *gdt)
53{
54 return g_strdup_printf("%s%s%03d", exif, ".", (int)(g_date_time_get_microsecond(gdt) * 0.001));
55 }
56
57static GTimeSpan _gdatetime_to_gtimespan(GDateTime *gdt)
58{
59 if(gdt)
60 {
61 GTimeSpan gts = g_date_time_difference(gdt, darktable.origin_gdt);
62 g_date_time_unref(gdt);
63 return gts;
64 }
65 return 0;
66}
67
68gboolean dt_datetime_exif_to_numbers(dt_datetime_t *dt, const char *exif)
69{
70 if(IS_NULL_PTR(exif) || !*exif || IS_NULL_PTR(dt)) return FALSE;
71
72 // fast-path for ISO-8601 (handles timezone offsets and 'T' separator)
73 GDateTime *gdt = g_date_time_new_from_iso8601(exif, darktable.utc_tz);
74 if(gdt)
75 {
76 const gboolean res = _datetime_gdatetime_to_numbers(dt, gdt);
77 g_date_time_unref(gdt);
78 return res;
79 }
80
81 // fallback for EXIF-like formats (YYYY:MM:DD HH:MM:SS[.sss])
82 // convert date separators to ISO form before parsing
84 g_strlcpy(sdt, exif, sizeof(sdt));
85 sdt[4] = sdt[7] = '-';
86 gdt = g_date_time_new_from_iso8601(sdt, darktable.utc_tz);
87 if(gdt)
88 {
89 const gboolean res = _datetime_gdatetime_to_numbers(dt, gdt);
90 g_date_time_unref(gdt);
91 return res;
92 }
93
94 return FALSE;
95}
96
97gboolean dt_datetime_exif_to_numbers_raw(dt_datetime_t *dt, const char *exif)
98{
99 if(exif && *exif && dt)
100 {
101 GMatchInfo *match_info;
102 // we capture each date componenent
103 GRegex *regex = g_regex_new(
104 "^\\s*(\\d{4})?(?::(\\d{2}))?(?::(\\d{2}))?(?: (\\d{2}))?(?::(\\d{2}))?(?::(\\d{2}))?\\s*$", 0, 0, NULL);
105 g_regex_match_full(regex, exif, -1, 0, 0, &match_info, NULL);
106 int match_count = g_match_info_get_match_count(match_info);
107 if(match_count == 7)
108 {
109 dt->year = atoi(g_match_info_fetch(match_info, 1));
110 dt->month = atoi(g_match_info_fetch(match_info, 2));
111 dt->day = atoi(g_match_info_fetch(match_info, 3));
112 dt->hour = atoi(g_match_info_fetch(match_info, 4));
113 dt->minute = atoi(g_match_info_fetch(match_info, 5));
114 dt->second = atoi(g_match_info_fetch(match_info, 6));
115 g_match_info_free(match_info);
116 g_regex_unref(regex);
117 return TRUE;
118 }
119 g_match_info_free(match_info);
120 g_regex_unref(regex);
121 }
122 return FALSE;
123}
124
125gboolean dt_datetime_gdatetime_to_local(char *local, const size_t local_size,
126 GDateTime *gdt, const gboolean msec, const gboolean tz)
127{
128 if(IS_NULL_PTR(local) || !local_size || IS_NULL_PTR(gdt)) return FALSE;
129 local[0] = '\0';
130 if(gdt)
131 {
132 char *sdt;
133 if(tz)
134 {
135 GDateTime *lgdt = g_date_time_to_local(gdt);
136 sdt = g_date_time_format(lgdt, "%a %x %X");
137 g_date_time_unref(lgdt);
138 }
139 else
140 sdt = g_date_time_format(gdt, "%a %x %X");
141 if(sdt)
142 {
143 if(msec)
144 { // add milliseconds
145 char *sdt2 = _datetime_append_msec(sdt, gdt);
146 dt_free(sdt);
147 sdt = sdt2;
148 }
149 g_strlcpy(local, sdt, local_size);
150 dt_free(sdt);
151 return TRUE;
152 }
153 }
154 return FALSE;
155}
156
157gboolean dt_datetime_gtimespan_to_local(char *local, const size_t local_size,
158 const GTimeSpan gts, const gboolean msec, const gboolean tz)
159{
160 gboolean res = FALSE;
161 if(IS_NULL_PTR(local) || !local_size) return FALSE;
162 local[0] = '\0';
163 GDateTime *gdt = g_date_time_add(darktable.origin_gdt, gts);
164 if(gdt)
165 {
166 res = dt_datetime_gdatetime_to_local(local, local_size, gdt, msec, tz);
167 g_date_time_unref(gdt);
168 }
169 return res;
170}
171
172gboolean dt_datetime_img_to_local(char *local, const size_t local_size,
173 const dt_image_t *img, const gboolean msec)
174{
175 return dt_datetime_gtimespan_to_local(local, local_size, img->exif_datetime_taken, msec, FALSE);
176}
177
178gboolean dt_datetime_unix_to_img(dt_image_t *img, const time_t *unix)
179{
180 GDateTime *gdt = g_date_time_new_from_unix_local(*unix);
181 if(gdt)
182 {
183 img->exif_datetime_taken = g_date_time_difference(gdt, darktable.origin_gdt);
184 g_date_time_unref(gdt);
185 return TRUE;
186 }
187 img->exif_datetime_taken = 0;
188 return FALSE;
189}
190
191gboolean dt_datetime_unix_to_exif(char *exif, const size_t exif_size, const time_t *unix)
192{
193 GDateTime *gdt = g_date_time_new_from_unix_local(*unix);
194 if(gdt)
195 {
196 const gboolean res = dt_datetime_gdatetime_to_exif(exif, exif_size, gdt);
197 g_date_time_unref(gdt);
198 return res;
199 }
200 return FALSE;
201}
202
203
205{
206 if(IS_NULL_PTR(exif)) return;
207 exif[0] = '\0';
208 GDateTime *gdt = g_date_time_new_now_local();
209 if(gdt)
210 {
212 g_date_time_unref(gdt);
213 }
214}
215
217{
218 GDateTime *gdt = g_date_time_new_now_local();
219 return _gdatetime_to_gtimespan(gdt);
220}
221
222void dt_datetime_exif_to_img(dt_image_t *img, const char *exif)
223{
224 if(IS_NULL_PTR(exif)) return;
225 GDateTime *gdt = dt_datetime_exif_to_gdatetime(exif, darktable.utc_tz);
226 if(gdt)
227 {
228 img->exif_datetime_taken = g_date_time_difference(gdt, darktable.origin_gdt);
229 g_date_time_unref(gdt);
230 }
231 else img->exif_datetime_taken = 0;
232}
233
234gboolean dt_datetime_img_to_exif(char *exif, const size_t exif_size, const dt_image_t *img)
235{
236 return dt_datetime_gtimespan_to_exif(exif, exif_size, img->exif_datetime_taken);
237}
238
239GDateTime *dt_datetime_exif_to_gdatetime(const char *exif, const GTimeZone *tz)
240{
241 dt_datetime_t dt;
242 if(dt_datetime_exif_to_numbers(&dt, exif))
243 {
244 GDateTime *gdt = g_date_time_new((GTimeZone *)tz, dt.year, dt.month, dt.day,
245 dt.hour, dt.minute, dt.second);
246 if(gdt)
247 {
248 if(dt.msec)
249 {
250 GDateTime *gdt2 = g_date_time_add(gdt, dt.msec * 1000);
251 g_date_time_unref(gdt);
252 return gdt2;
253 }
254 }
255 return gdt;
256 }
257 return NULL;
258}
259
260gboolean dt_datetime_gdatetime_to_exif(char *exif, const size_t exif_size, GDateTime *gdt)
261{
262 if(IS_NULL_PTR(exif) || !exif_size || IS_NULL_PTR(gdt)) return FALSE;
263 exif[0] = '\0';
264 char *sdt = g_date_time_format(gdt, DT_DATETIME_EXIF_FORMAT);
265 if(sdt)
266 {
267 if(exif_size == DT_DATETIME_LENGTH)
268 {
269 // the format %f seems not to be available in glib2.0 before version 2.70
270 char *sdt2 = _datetime_append_msec(sdt, gdt);
271 dt_free(sdt);
272 sdt = sdt2;
273 }
274 g_strlcpy(exif, sdt, exif_size);
275 dt_free(sdt);
276 return TRUE;
277 }
278 return FALSE;
279}
280
281GDateTime *dt_datetime_img_to_gdatetime(const dt_image_t *img, const GTimeZone *tz)
282{
283 // GTimeSpan is UTC based. Therefore we have to cheat a little bit to get image datetime
284 if(IS_NULL_PTR(tz)) return NULL;
285 GDateTime *gdt = g_date_time_add(darktable.origin_gdt, img->exif_datetime_taken);
286 if(gdt)
287 {
288 dt_datetime_t dt;
290 {
291 g_date_time_unref(gdt);
292 gdt = g_date_time_new((GTimeZone *)tz, dt.year, dt.month, dt.day,
293 dt.hour, dt.minute, (double)dt.second);
294 return gdt;
295 }
296 }
297 return NULL;
298}
299
300GDateTime *dt_string_to_datetime(const char *string)
301{
302 if(g_utf8_strlen(string, -1) > DT_DATETIME_LENGTH - 1)
303 return FALSE;
304
305 char idt[DT_DATETIME_LENGTH];
306 g_strlcpy(idt, DT_DATETIME_ORIGIN, sizeof(idt));
307 memcpy(idt, string, g_utf8_strlen(string, -1));
308 idt[4] = idt[7] = '-';
309 return g_date_time_new_from_iso8601(idt, darktable.utc_tz);
310}
311
312gboolean dt_datetime_entry_to_exif(char *exif, const size_t exif_size, const char *entry)
313{
314 if(IS_NULL_PTR(exif) || !exif_size) return FALSE;
315 exif[0] = '\0';
316 GDateTime *gdt = dt_string_to_datetime(entry);
317
318 if(gdt)
319 {
320 const gboolean res = dt_datetime_gdatetime_to_exif(exif, exif_size, gdt);
321 g_date_time_unref(gdt);
322 return res;
323 }
324 return FALSE;
325}
326
327gboolean dt_datetime_entry_to_exif_upper_bound(char *exif, const size_t exif_size, const char *entry)
328{
329 if(IS_NULL_PTR(exif) || !exif_size) return FALSE;
330 exif[0] = '\0';
331
332 const int len = strlen(entry);
333 if(len > DT_DATETIME_LENGTH - 1)
334 return FALSE;
335 char idt[DT_DATETIME_LENGTH];
336 g_strlcpy(idt, DT_DATETIME_ORIGIN, sizeof(idt));
337 memcpy(idt, entry, strlen(entry));
338 idt[4] = idt[7] = '-';
339 GDateTime *gdt = g_date_time_new_from_iso8601(idt, darktable.utc_tz);
340 if(gdt)
341 {
342 GDateTime *gdt2 = NULL;
343 if(len < 7)
344 gdt2 = g_date_time_add_years(gdt, 1);
345 else if(len < 10)
346 gdt2 = g_date_time_add_months(gdt, 1);
347 else if(len < 13)
348 gdt2 = g_date_time_add_days(gdt, 1);
349 else if(len < 16)
350 gdt2 = g_date_time_add_hours(gdt, 1);
351 else if(len < 19)
352 gdt2 = g_date_time_add_minutes(gdt, 1);
353 else if(len < 23)
354 gdt2 = g_date_time_add_seconds(gdt, 1);
355 else
356 gdt2 = g_date_time_add(gdt, 2);
357 g_date_time_unref(gdt);
358 if(gdt2)
359 {
360 GDateTime *gdt3 = g_date_time_add(gdt2, -1);
361 g_date_time_unref(gdt2);
362 gdt = gdt3;
363 if(gdt)
364 {
365 const gboolean res = dt_datetime_gdatetime_to_exif(exif, exif_size, gdt);
366 g_date_time_unref(gdt);
367 return res;
368 }
369 }
370 }
371 return FALSE;
372}
373
374void dt_datetime_add_subsec_to_exif(char *exif, const size_t exif_size, const char*subsec)
375{
376 if(IS_NULL_PTR(exif) || exif_size < DT_DATETIME_EXIF_LENGTH + 1) return;
377
378 g_strlcpy(&exif[DT_DATETIME_EXIF_LENGTH - 1], ".000000", exif_size - DT_DATETIME_EXIF_LENGTH + 1);
379 for(int i = 0; i < 6 && subsec[i] != '\0' && (DT_DATETIME_EXIF_LENGTH + i < exif_size - 1); i++)
380 exif[DT_DATETIME_EXIF_LENGTH + i] = subsec[i];
381 exif[exif_size - 1] = '\0';
382}
383
384gboolean dt_datetime_gtimespan_to_exif(char *sdt, const size_t sdt_size, const GTimeSpan gts)
385{
386 if(IS_NULL_PTR(sdt) || !sdt_size) return FALSE;
387 sdt[0] = '\0';
388 if(!gts) return FALSE;
389 GDateTime *gdt = g_date_time_add(darktable.origin_gdt, gts);
390 if(gdt)
391 {
392 const gboolean res = dt_datetime_gdatetime_to_exif(sdt, sdt_size, gdt);
393 g_date_time_unref(gdt);
394 return res;
395 }
396 return FALSE;
397}
398
399GTimeSpan dt_datetime_exif_to_gtimespan(const char *sdt)
400{
401 GTimeSpan gts = 0;
402 if(IS_NULL_PTR(sdt)) return gts;
403 GDateTime *gdt = dt_datetime_exif_to_gdatetime(sdt, darktable.utc_tz);
404 if(gdt)
405 {
406 gts = g_date_time_difference(gdt, darktable.origin_gdt);
407 g_date_time_unref(gdt);
408 }
409 return gts;
410}
411
412gboolean dt_datetime_gtimespan_to_numbers(dt_datetime_t *dt, const GTimeSpan gts)
413{
414 GDateTime *gdt = g_date_time_add(darktable.origin_gdt, gts);
415 if(gdt)
416 {
417 const gboolean res = _datetime_gdatetime_to_numbers(dt, gdt);
418 g_date_time_unref(gdt);
419 return res;
420 }
421 return FALSE;
422}
423
424GDateTime *dt_datetime_gtimespan_to_gdatetime(const GTimeSpan gts)
425{
426 return g_date_time_add(darktable.origin_gdt, gts);
427}
428
430{
431 if(IS_NULL_PTR(dt)) return 0;
432 GDateTime *gdt = g_date_time_new(darktable.utc_tz,
433 dt->year, dt->month, dt->day,
434 dt->hour, dt->minute, (double)dt->second);
435 return _gdatetime_to_gtimespan(gdt);
436}
437
438GTimeSpan dt_datetime_gdatetime_to_gtimespan(GDateTime *gdt)
439{
440 if(gdt)
441 return g_date_time_difference(gdt, darktable.origin_gdt);
442 else
443 return 0;
444}
445
446GDateTime *dt_datetime_gdatetime_add_numbers(GDateTime *dte, const dt_datetime_t numbers, const gboolean add)
447{
448 const int s = add ? 1 : -1;
449
450 GDateTime *dt2 = g_date_time_add_years(dte, s * numbers.year);
451 GDateTime *dt = g_date_time_add_months(dt2, s * numbers.month);
452 g_date_time_unref(dt2);
453 dt2 = g_date_time_add_days(dt, s * numbers.day);
454 g_date_time_unref(dt);
455 dt = g_date_time_add_hours(dt2, s * numbers.hour);
456 g_date_time_unref(dt2);
457 dt2 = g_date_time_add_minutes(dt, s * numbers.minute);
458 g_date_time_unref(dt);
459 dt = g_date_time_add_seconds(dt2, s * numbers.second);
460 g_date_time_unref(dt2);
461 return dt;
462}
463
464GTimeSpan dt_datetime_gtimespan_add_numbers(const GTimeSpan dt, const dt_datetime_t numbers, const gboolean add)
465{
466 GDateTime *dte = dt_datetime_gtimespan_to_gdatetime(dt);
467 GDateTime *dt2 = dt_datetime_gdatetime_add_numbers(dte, numbers, add);
468 GTimeSpan ret = dt_datetime_gdatetime_to_gtimespan(dt2);
469 g_date_time_unref(dte);
470 g_date_time_unref(dt2);
471 return ret;
472}
473
474gboolean dt_datetime_exif_add_numbers(const gchar *exif, const dt_datetime_t numbers, const gboolean add,
475 gchar **result)
476{
477 GDateTime *dte = dt_datetime_exif_to_gdatetime(exif, darktable.utc_tz);
478 if(IS_NULL_PTR(dte)) return FALSE;
479 GDateTime *dt2 = dt_datetime_gdatetime_add_numbers(dte, numbers, add);
480 char txt[DT_DATETIME_EXIF_LENGTH];
482 g_date_time_unref(dte);
483 g_date_time_unref(dt2);
484 *result = g_strdup(txt);
485 return TRUE;
486}
487
488// clang-format off
489// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
490// vim: shiftwidth=2 expandtab tabstop=2 cindent
491// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
492// clang-format on
493
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
darktable_t darktable
Definition darktable.c:181
#define dt_free(ptr)
Definition darktable.h:456
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
GTimeSpan dt_datetime_exif_to_gtimespan(const char *sdt)
Definition datetime.c:399
gboolean dt_datetime_img_to_exif(char *exif, const size_t exif_size, const dt_image_t *img)
Definition datetime.c:234
gboolean dt_datetime_gdatetime_to_exif(char *exif, const size_t exif_size, GDateTime *gdt)
Definition datetime.c:260
gboolean dt_datetime_gtimespan_to_local(char *local, const size_t local_size, const GTimeSpan gts, const gboolean msec, const gboolean tz)
Definition datetime.c:157
void dt_datetime_init()
Definition datetime.c:30
void dt_datetime_now_to_exif(char *exif)
Definition datetime.c:204
void dt_datetime_add_subsec_to_exif(char *exif, const size_t exif_size, const char *subsec)
Definition datetime.c:374
GDateTime * dt_datetime_exif_to_gdatetime(const char *exif, const GTimeZone *tz)
Definition datetime.c:239
static char * _datetime_append_msec(char *exif, GDateTime *gdt)
Definition datetime.c:52
GTimeSpan dt_datetime_numbers_to_gtimespan(const dt_datetime_t *dt)
Definition datetime.c:429
gboolean dt_datetime_unix_to_img(dt_image_t *img, const time_t *unix)
Definition datetime.c:178
GDateTime * dt_datetime_gdatetime_add_numbers(GDateTime *dte, const dt_datetime_t numbers, const gboolean add)
Definition datetime.c:446
GDateTime * dt_string_to_datetime(const char *string)
Definition datetime.c:300
gboolean dt_datetime_img_to_local(char *local, const size_t local_size, const dt_image_t *img, const gboolean msec)
Definition datetime.c:172
gboolean dt_datetime_exif_add_numbers(const gchar *exif, const dt_datetime_t numbers, const gboolean add, gchar **result)
Definition datetime.c:474
#define DT_DATETIME_ORIGIN
Definition datetime.c:26
GDateTime * dt_datetime_gtimespan_to_gdatetime(const GTimeSpan gts)
Definition datetime.c:424
gboolean dt_datetime_gtimespan_to_exif(char *sdt, const size_t sdt_size, const GTimeSpan gts)
Definition datetime.c:384
GTimeSpan dt_datetime_now_to_gtimespan()
Definition datetime.c:216
GTimeSpan dt_datetime_gdatetime_to_gtimespan(GDateTime *gdt)
Definition datetime.c:438
gboolean dt_datetime_gtimespan_to_numbers(dt_datetime_t *dt, const GTimeSpan gts)
Definition datetime.c:412
gboolean dt_datetime_exif_to_numbers(dt_datetime_t *dt, const char *exif)
Definition datetime.c:68
GTimeSpan dt_datetime_gtimespan_add_numbers(const GTimeSpan dt, const dt_datetime_t numbers, const gboolean add)
Definition datetime.c:464
gboolean dt_datetime_gdatetime_to_local(char *local, const size_t local_size, GDateTime *gdt, const gboolean msec, const gboolean tz)
Definition datetime.c:125
static GTimeSpan _gdatetime_to_gtimespan(GDateTime *gdt)
Definition datetime.c:57
gboolean dt_datetime_exif_to_numbers_raw(dt_datetime_t *dt, const char *exif)
Definition datetime.c:97
#define DT_DATETIME_EXIF_FORMAT
Definition datetime.c:28
void dt_datetime_exif_to_img(dt_image_t *img, const char *exif)
Definition datetime.c:222
gboolean dt_datetime_entry_to_exif_upper_bound(char *exif, const size_t exif_size, const char *entry)
Definition datetime.c:327
gboolean dt_datetime_unix_to_exif(char *exif, const size_t exif_size, const time_t *unix)
Definition datetime.c:191
gboolean dt_datetime_entry_to_exif(char *exif, const size_t exif_size, const char *entry)
Definition datetime.c:312
gboolean _datetime_gdatetime_to_numbers(dt_datetime_t *dt, GDateTime *gdt)
Definition datetime.c:36
GDateTime * dt_datetime_img_to_gdatetime(const dt_image_t *img, const GTimeZone *tz)
Definition datetime.c:281
#define DT_DATETIME_LENGTH
Definition datetime.h:37
#define DT_DATETIME_EXIF_LENGTH
Definition datetime.h:38
GTimeZone * utc_tz
Definition darktable.h:832
GDateTime * origin_gdt
Definition darktable.h:833
GTimeSpan exif_datetime_taken
Definition image.h:295