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