/src/poppler/poppler/DateInfo.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //======================================================================== |
2 | | // |
3 | | // DateInfo.cc |
4 | | // |
5 | | // Copyright (C) 2008, 2018, 2019, 2021, 2022, 2024 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, 2025 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 "goo/gmem.h" |
31 | | #include "DateInfo.h" |
32 | | #include "UTF.h" |
33 | | |
34 | | #include <cstdio> |
35 | | #include <cstring> |
36 | | |
37 | | /* See PDF Reference 1.3, Section 3.8.2 for PDF Date representation */ |
38 | | bool parseDateString(const GooString *date, int *year, int *month, int *day, int *hour, int *minute, int *second, char *tz, int *tzHour, int *tzMinute) |
39 | 131 | { |
40 | 131 | std::vector<Unicode> u = TextStringToUCS4(date->toStr()); |
41 | 131 | GooString s; |
42 | 8.51k | for (auto &c : u) { |
43 | | // Ignore any non ASCII characters |
44 | 8.51k | if (c < 128) { |
45 | 7.30k | s.append(c); |
46 | 7.30k | } |
47 | 8.51k | } |
48 | 131 | const char *dateString = s.c_str(); |
49 | | |
50 | 131 | if (strlen(dateString) < 2) { |
51 | 0 | return false; |
52 | 0 | } |
53 | | |
54 | 131 | if (dateString[0] == 'D' && dateString[1] == ':') { |
55 | 131 | dateString += 2; |
56 | 131 | } |
57 | | |
58 | 131 | *month = 1; |
59 | 131 | *day = 1; |
60 | 131 | *hour = 0; |
61 | 131 | *minute = 0; |
62 | 131 | *second = 0; |
63 | 131 | *tz = 0x00; |
64 | 131 | *tzHour = 0; |
65 | 131 | *tzMinute = 0; |
66 | | |
67 | 131 | if (sscanf(dateString, "%4d%2d%2d%2d%2d%2d%c%2d%*c%2d", year, month, day, hour, minute, second, tz, tzHour, tzMinute) > 0) { |
68 | | /* Workaround for y2k bug in Distiller 3 stolen from gpdf, hoping that it won't |
69 | | * be used after y2.2k */ |
70 | 131 | if (*year < 1930 && strlen(dateString) > 14) { |
71 | 0 | int century, years_since_1900; |
72 | 0 | if (sscanf(dateString, "%2d%3d%2d%2d%2d%2d%2d", ¢ury, &years_since_1900, month, day, hour, minute, second) == 7) { |
73 | 0 | *year = century * 100 + years_since_1900; |
74 | 0 | } else { |
75 | 0 | return false; |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | 131 | if (*year <= 0) { |
80 | 0 | return false; |
81 | 0 | } |
82 | | |
83 | 131 | return true; |
84 | 131 | } |
85 | | |
86 | 0 | return false; |
87 | 131 | } |
88 | | |
89 | | std::string timeToStringWithFormat(const time_t *timeA, const char *format) |
90 | 90.7k | { |
91 | 90.7k | const time_t timet = timeA ? *timeA : time(nullptr); |
92 | | |
93 | 90.7k | struct tm localtime_tm; |
94 | 90.7k | localtime_r(&timet, &localtime_tm); |
95 | | |
96 | 90.7k | char timeOffset[12]; |
97 | | |
98 | | // strftime "%z" does not work on windows (it prints zone name, not offset) |
99 | | // calculate time zone offset by comparing local and gmtime time_t value for same |
100 | | // time. |
101 | 90.7k | const time_t timeg = timegm(&localtime_tm); |
102 | 90.7k | const int offset = static_cast<int>(difftime(timeg, timet)); // find time zone offset in seconds |
103 | 90.7k | if (offset > 0) { |
104 | 0 | snprintf(timeOffset, sizeof(timeOffset), "+%02d'%02d'", offset / 3600, (offset % 3600) / 60); |
105 | 90.7k | } else if (offset < 0) { |
106 | 0 | snprintf(timeOffset, sizeof(timeOffset), "-%02d'%02d'", -offset / 3600, (-offset % 3600) / 60); |
107 | 90.7k | } else { |
108 | 90.7k | snprintf(timeOffset, sizeof(timeOffset), "Z"); |
109 | 90.7k | } |
110 | 90.7k | std::string fmt(format); |
111 | 90.7k | const char timeOffsetPattern[] = "%z"; |
112 | 90.7k | size_t timeOffsetPosition = fmt.find(timeOffsetPattern); |
113 | 90.7k | if (timeOffsetPosition != std::string::npos) { |
114 | 90.7k | fmt.replace(timeOffsetPosition, sizeof(timeOffsetPattern) - 1, timeOffset); |
115 | 90.7k | } |
116 | | |
117 | 90.7k | if (fmt.empty()) { |
118 | 0 | return ""; |
119 | 0 | } |
120 | 90.7k | size_t bufLen = 50; |
121 | 90.7k | std::string buf(bufLen, ' '); |
122 | 90.7k | while (strftime(&buf[0], buf.size(), fmt.c_str(), &localtime_tm) == 0) { |
123 | 0 | buf.resize(bufLen *= 2); |
124 | 0 | } |
125 | 90.7k | buf.resize(buf.find('\0')); |
126 | 90.7k | return buf; |
127 | 90.7k | } |
128 | | |
129 | | std::unique_ptr<GooString> timeToDateString(const time_t *timeA) |
130 | 90.7k | { |
131 | 90.7k | return std::make_unique<GooString>(timeToStringWithFormat(timeA, "D:%Y%m%d%H%M%S%z")); |
132 | 90.7k | } |
133 | | |
134 | | // Convert PDF date string to time. Returns -1 if conversion fails. |
135 | | time_t dateStringToTime(const GooString *dateString) |
136 | 131 | { |
137 | 131 | int year, mon, day, hour, min, sec, tz_hour, tz_minute; |
138 | 131 | char tz; |
139 | 131 | struct tm tm; |
140 | 131 | time_t time; |
141 | | |
142 | 131 | if (!parseDateString(dateString, &year, &mon, &day, &hour, &min, &sec, &tz, &tz_hour, &tz_minute)) { |
143 | 0 | return -1; |
144 | 0 | } |
145 | | |
146 | 131 | tm.tm_year = year - 1900; |
147 | 131 | tm.tm_mon = mon - 1; |
148 | 131 | tm.tm_mday = day; |
149 | 131 | tm.tm_hour = hour; |
150 | 131 | tm.tm_min = min; |
151 | 131 | tm.tm_sec = sec; |
152 | 131 | tm.tm_wday = -1; |
153 | 131 | tm.tm_yday = -1; |
154 | 131 | 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 | 131 | time = timegm(&tm); |
158 | 131 | if (time == (time_t)-1) { |
159 | 0 | return time; |
160 | 0 | } |
161 | | |
162 | 131 | time_t offset = (tz_hour * 60 + tz_minute) * 60; |
163 | 131 | if (tz == '-') { |
164 | 0 | offset *= -1; |
165 | 0 | } |
166 | 131 | time -= offset; |
167 | | |
168 | 131 | return time; |
169 | 131 | } |