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 */
39bool 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", &century, &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
90std::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
129GooString *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.
135time_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

source code of poppler/poppler/DateInfo.cc