1 | //======================================================================== |
2 | // |
3 | // DateInfo.cc |
4 | // |
5 | // Copyright (C) 2008, 2018, 2019, 2021, 2022 Albert Astals Cid <aacid@kde.org> |
6 | // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org> |
7 | // Copyright (C) 2015 André Guerreiro <aguerreiro1985@gmail.com> |
8 | // Copyright (C) 2015 André Esser <bepandre@hotmail.com> |
9 | // Copyright (C) 2016, 2018, 2021 Adrian Johnson <ajohnson@redneon.com> |
10 | // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden |
11 | // Copyright (C) 2021 Albert Astals Cid <aacid@kde.org> |
12 | // Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
13 | // Copyright (C) 2024 Erich E. Hoover <erich.e.hoover@gmail.com> |
14 | // |
15 | // To see a description of the changes please see the Changelog file that |
16 | // came with your tarball or type make ChangeLog if you are building from git |
17 | // |
18 | //======================================================================== |
19 | |
20 | //======================================================================== |
21 | // |
22 | // Based on code from pdfinfo.cc |
23 | // |
24 | // Copyright 1998-2003 Glyph & Cog, LLC |
25 | // |
26 | //======================================================================== |
27 | |
28 | #include <config.h> |
29 | |
30 | #include "glibc.h" |
31 | #include "gmem.h" |
32 | #include "DateInfo.h" |
33 | #include "UTF.h" |
34 | |
35 | #include <cstdio> |
36 | #include <cstring> |
37 | |
38 | /* See PDF Reference 1.3, Section 3.8.2 for PDF Date representation */ |
39 | bool parseDateString(const GooString *date, int *year, int *month, int *day, int *hour, int *minute, int *second, char *tz, int *tzHour, int *tzMinute) |
40 | { |
41 | std::vector<Unicode> u = TextStringToUCS4(textStr: date->toStr()); |
42 | GooString s; |
43 | for (auto &c : u) { |
44 | // Ignore any non ASCII characters |
45 | if (c < 128) { |
46 | s.append(c); |
47 | } |
48 | } |
49 | const char *dateString = s.c_str(); |
50 | |
51 | if (strlen(s: dateString) < 2) { |
52 | return false; |
53 | } |
54 | |
55 | if (dateString[0] == 'D' && dateString[1] == ':') { |
56 | dateString += 2; |
57 | } |
58 | |
59 | *month = 1; |
60 | *day = 1; |
61 | *hour = 0; |
62 | *minute = 0; |
63 | *second = 0; |
64 | *tz = 0x00; |
65 | *tzHour = 0; |
66 | *tzMinute = 0; |
67 | |
68 | if (sscanf(s: dateString, format: "%4d%2d%2d%2d%2d%2d%c%2d%*c%2d" , year, month, day, hour, minute, second, tz, tzHour, tzMinute) > 0) { |
69 | /* Workaround for y2k bug in Distiller 3 stolen from gpdf, hoping that it won't |
70 | * be used after y2.2k */ |
71 | if (*year < 1930 && strlen(s: dateString) > 14) { |
72 | int century, years_since_1900; |
73 | if (sscanf(s: dateString, format: "%2d%3d%2d%2d%2d%2d%2d" , ¢ury, &years_since_1900, month, day, hour, minute, second) == 7) { |
74 | *year = century * 100 + years_since_1900; |
75 | } else { |
76 | return false; |
77 | } |
78 | } |
79 | |
80 | if (*year <= 0) { |
81 | return false; |
82 | } |
83 | |
84 | return true; |
85 | } |
86 | |
87 | return false; |
88 | } |
89 | |
90 | std::string timeToStringWithFormat(const time_t *timeA, const char *format) |
91 | { |
92 | const time_t timet = timeA ? *timeA : time(timer: nullptr); |
93 | |
94 | struct tm localtime_tm; |
95 | localtime_r(timer: &timet, tp: &localtime_tm); |
96 | |
97 | char timeOffset[12]; |
98 | |
99 | // strftime "%z" does not work on windows (it prints zone name, not offset) |
100 | // calculate time zone offset by comparing local and gmtime time_t value for same |
101 | // time. |
102 | const time_t timeg = timegm(tp: &localtime_tm); |
103 | const int offset = static_cast<int>(difftime(time1: timeg, time0: timet)); // find time zone offset in seconds |
104 | if (offset > 0) { |
105 | snprintf(s: timeOffset, maxlen: sizeof(timeOffset), format: "+%02d'%02d'" , offset / 3600, (offset % 3600) / 60); |
106 | } else if (offset < 0) { |
107 | snprintf(s: timeOffset, maxlen: sizeof(timeOffset), format: "-%02d'%02d'" , -offset / 3600, (-offset % 3600) / 60); |
108 | } else { |
109 | snprintf(s: timeOffset, maxlen: sizeof(timeOffset), format: "Z" ); |
110 | } |
111 | std::string fmt(format); |
112 | const char timeOffsetPattern[] = "%z" ; |
113 | size_t timeOffsetPosition = fmt.find(s: timeOffsetPattern); |
114 | if (timeOffsetPosition != std::string::npos) { |
115 | fmt.replace(pos: timeOffsetPosition, n1: sizeof(timeOffsetPattern) - 1, s: timeOffset); |
116 | } |
117 | |
118 | if (fmt.length() == 0) { |
119 | return "" ; |
120 | } |
121 | size_t bufLen = 50; |
122 | std::string buf(bufLen, ' '); |
123 | while (strftime(s: &buf[0], maxsize: buf.size(), format: fmt.c_str(), tp: &localtime_tm) == 0) { |
124 | buf.resize(n: bufLen *= 2); |
125 | } |
126 | return buf; |
127 | } |
128 | |
129 | GooString *timeToDateString(const time_t *timeA) |
130 | { |
131 | return new GooString(timeToStringWithFormat(timeA, format: "D:%Y%m%d%H%M%S%z" )); |
132 | } |
133 | |
134 | // Convert PDF date string to time. Returns -1 if conversion fails. |
135 | time_t dateStringToTime(const GooString *dateString) |
136 | { |
137 | int year, mon, day, hour, min, sec, tz_hour, tz_minute; |
138 | char tz; |
139 | struct tm tm; |
140 | time_t time; |
141 | |
142 | if (!parseDateString(date: dateString, year: &year, month: &mon, day: &day, hour: &hour, minute: &min, second: &sec, tz: &tz, tzHour: &tz_hour, tzMinute: &tz_minute)) { |
143 | return -1; |
144 | } |
145 | |
146 | tm.tm_year = year - 1900; |
147 | tm.tm_mon = mon - 1; |
148 | tm.tm_mday = day; |
149 | tm.tm_hour = hour; |
150 | tm.tm_min = min; |
151 | tm.tm_sec = sec; |
152 | tm.tm_wday = -1; |
153 | tm.tm_yday = -1; |
154 | tm.tm_isdst = -1; /* 0 = DST off, 1 = DST on, -1 = don't know */ |
155 | |
156 | /* compute tm_wday and tm_yday and check date */ |
157 | time = timegm(tp: &tm); |
158 | if (time == (time_t)-1) { |
159 | return time; |
160 | } |
161 | |
162 | time_t offset = (tz_hour * 60 + tz_minute) * 60; |
163 | if (tz == '-') { |
164 | offset *= -1; |
165 | } |
166 | time -= offset; |
167 | |
168 | return time; |
169 | } |
170 | |