1// Copyright 2016 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "fpdfsdk/cpdfsdk_datetime.h"
8
9#include "core/fxcrt/fx_extension.h"
10
11namespace {
12
13int GetTimeZoneInSeconds(int8_t tzhour, uint8_t tzminute) {
14  return (int)tzhour * 3600 + (int)tzminute * (tzhour >= 0 ? 60 : -60);
15}
16
17bool IsLeapYear(int16_t year) {
18  return ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0)));
19}
20
21uint16_t GetYearDays(int16_t year) {
22  return (IsLeapYear(year) ? 366 : 365);
23}
24
25uint8_t GetMonthDays(int16_t year, uint8_t month) {
26  uint8_t mDays;
27  switch (month) {
28    case 1:
29    case 3:
30    case 5:
31    case 7:
32    case 8:
33    case 10:
34    case 12:
35      mDays = 31;
36      break;
37
38    case 4:
39    case 6:
40    case 9:
41    case 11:
42      mDays = 30;
43      break;
44
45    case 2:
46      if (IsLeapYear(year))
47        mDays = 29;
48      else
49        mDays = 28;
50      break;
51
52    default:
53      mDays = 0;
54      break;
55  }
56
57  return mDays;
58}
59
60}  // namespace
61
62CPDFSDK_DateTime::CPDFSDK_DateTime() {
63  ResetDateTime();
64}
65
66CPDFSDK_DateTime::CPDFSDK_DateTime(const ByteString& dtStr) {
67  ResetDateTime();
68  FromPDFDateTimeString(dtStr);
69}
70
71CPDFSDK_DateTime::CPDFSDK_DateTime(const CPDFSDK_DateTime& that)
72    : m_year(that.m_year),
73      m_month(that.m_month),
74      m_day(that.m_day),
75      m_hour(that.m_hour),
76      m_minute(that.m_minute),
77      m_second(that.m_second),
78      m_tzHour(that.m_tzHour),
79      m_tzMinute(that.m_tzMinute) {}
80
81CPDFSDK_DateTime::CPDFSDK_DateTime(const FX_SYSTEMTIME& st) {
82  tzset();
83
84  m_year = static_cast<int16_t>(st.wYear);
85  m_month = static_cast<uint8_t>(st.wMonth);
86  m_day = static_cast<uint8_t>(st.wDay);
87  m_hour = static_cast<uint8_t>(st.wHour);
88  m_minute = static_cast<uint8_t>(st.wMinute);
89  m_second = static_cast<uint8_t>(st.wSecond);
90}
91
92void CPDFSDK_DateTime::ResetDateTime() {
93  tzset();
94
95  time_t curTime;
96  time(&curTime);
97
98  struct tm* newtime = localtime(&curTime);
99  m_year = newtime->tm_year + 1900;
100  m_month = newtime->tm_mon + 1;
101  m_day = newtime->tm_mday;
102  m_hour = newtime->tm_hour;
103  m_minute = newtime->tm_min;
104  m_second = newtime->tm_sec;
105}
106
107bool CPDFSDK_DateTime::operator==(const CPDFSDK_DateTime& that) const {
108  return m_year == that.m_year && m_month == that.m_month &&
109         m_day == that.m_day && m_hour == that.m_hour &&
110         m_minute == that.m_minute && m_second == that.m_second &&
111         m_tzHour == that.m_tzHour && m_tzMinute == that.m_tzMinute;
112}
113
114bool CPDFSDK_DateTime::operator!=(const CPDFSDK_DateTime& datetime) const {
115  return !(*this == datetime);
116}
117
118time_t CPDFSDK_DateTime::ToTime_t() const {
119  struct tm newtime;
120
121  newtime.tm_year = m_year - 1900;
122  newtime.tm_mon = m_month - 1;
123  newtime.tm_mday = m_day;
124  newtime.tm_hour = m_hour;
125  newtime.tm_min = m_minute;
126  newtime.tm_sec = m_second;
127
128  return mktime(&newtime);
129}
130
131CPDFSDK_DateTime& CPDFSDK_DateTime::FromPDFDateTimeString(
132    const ByteString& dtStr) {
133  int strLength = dtStr.GetLength();
134  if (strLength <= 0)
135    return *this;
136
137  int i = 0;
138  while (i < strLength && !std::isdigit(dtStr[i]))
139    ++i;
140
141  if (i >= strLength)
142    return *this;
143
144  int j = 0;
145  int k = 0;
146  char ch;
147  while (i < strLength && j < 4) {
148    ch = dtStr[i];
149    k = k * 10 + FXSYS_DecimalCharToInt(ch);
150    j++;
151    if (!std::isdigit(ch))
152      break;
153    i++;
154  }
155  m_year = static_cast<int16_t>(k);
156  if (i >= strLength || j < 4)
157    return *this;
158
159  j = 0;
160  k = 0;
161  while (i < strLength && j < 2) {
162    ch = dtStr[i];
163    k = k * 10 + FXSYS_DecimalCharToInt(ch);
164    j++;
165    if (!std::isdigit(ch))
166      break;
167    i++;
168  }
169  m_month = static_cast<uint8_t>(k);
170  if (i >= strLength || j < 2)
171    return *this;
172
173  j = 0;
174  k = 0;
175  while (i < strLength && j < 2) {
176    ch = dtStr[i];
177    k = k * 10 + FXSYS_DecimalCharToInt(ch);
178    j++;
179    if (!std::isdigit(ch))
180      break;
181    i++;
182  }
183  m_day = static_cast<uint8_t>(k);
184  if (i >= strLength || j < 2)
185    return *this;
186
187  j = 0;
188  k = 0;
189  while (i < strLength && j < 2) {
190    ch = dtStr[i];
191    k = k * 10 + FXSYS_DecimalCharToInt(ch);
192    j++;
193    if (!std::isdigit(ch))
194      break;
195    i++;
196  }
197  m_hour = static_cast<uint8_t>(k);
198  if (i >= strLength || j < 2)
199    return *this;
200
201  j = 0;
202  k = 0;
203  while (i < strLength && j < 2) {
204    ch = dtStr[i];
205    k = k * 10 + FXSYS_DecimalCharToInt(ch);
206    j++;
207    if (!std::isdigit(ch))
208      break;
209    i++;
210  }
211  m_minute = static_cast<uint8_t>(k);
212  if (i >= strLength || j < 2)
213    return *this;
214
215  j = 0;
216  k = 0;
217  while (i < strLength && j < 2) {
218    ch = dtStr[i];
219    k = k * 10 + FXSYS_DecimalCharToInt(ch);
220    j++;
221    if (!std::isdigit(ch))
222      break;
223    i++;
224  }
225  m_second = static_cast<uint8_t>(k);
226  if (i >= strLength || j < 2)
227    return *this;
228
229  ch = dtStr[i++];
230  if (ch != '-' && ch != '+')
231    return *this;
232  if (ch == '-')
233    m_tzHour = -1;
234  else
235    m_tzHour = 1;
236  j = 0;
237  k = 0;
238  while (i < strLength && j < 2) {
239    ch = dtStr[i];
240    k = k * 10 + FXSYS_DecimalCharToInt(ch);
241    j++;
242    if (!std::isdigit(ch))
243      break;
244    i++;
245  }
246  m_tzHour *= static_cast<int8_t>(k);
247  if (i >= strLength || j < 2)
248    return *this;
249
250  if (dtStr[i++] != '\'')
251    return *this;
252  j = 0;
253  k = 0;
254  while (i < strLength && j < 2) {
255    ch = dtStr[i];
256    k = k * 10 + FXSYS_DecimalCharToInt(ch);
257    j++;
258    if (!std::isdigit(ch))
259      break;
260    i++;
261  }
262  m_tzMinute = static_cast<uint8_t>(k);
263  return *this;
264}
265
266ByteString CPDFSDK_DateTime::ToCommonDateTimeString() {
267  return ByteString::Format("%04d-%02u-%02u %02u:%02u:%02u ", m_year, m_month,
268                            m_day, m_hour, m_minute, m_second) +
269         (m_tzHour < 0 ? "-" : "+") +
270         ByteString::Format("%02d:%02u", std::abs(static_cast<int>(m_tzHour)),
271                            m_tzMinute);
272}
273
274ByteString CPDFSDK_DateTime::ToPDFDateTimeString() {
275  ByteString dtStr;
276  char tempStr[32];
277  memset(tempStr, 0, sizeof(tempStr));
278  FXSYS_snprintf(tempStr, sizeof(tempStr) - 1, "D:%04d%02u%02u%02u%02u%02u",
279                 m_year, m_month, m_day, m_hour, m_minute, m_second);
280  dtStr = ByteString(tempStr);
281  if (m_tzHour < 0)
282    dtStr += ByteString("-");
283  else
284    dtStr += ByteString("+");
285  memset(tempStr, 0, sizeof(tempStr));
286  FXSYS_snprintf(tempStr, sizeof(tempStr) - 1, "%02d'%02u'",
287                 std::abs(static_cast<int>(m_tzHour)), m_tzMinute);
288  dtStr += ByteString(tempStr);
289  return dtStr;
290}
291
292void CPDFSDK_DateTime::ToSystemTime(FX_SYSTEMTIME& st) {
293  time_t t = this->ToTime_t();
294  struct tm* pTime = localtime(&t);
295
296  if (!pTime)
297    return;
298
299  st.wYear = static_cast<uint16_t>(pTime->tm_year) + 1900;
300  st.wMonth = static_cast<uint16_t>(pTime->tm_mon) + 1;
301  st.wDay = static_cast<uint16_t>(pTime->tm_mday);
302  st.wDayOfWeek = static_cast<uint16_t>(pTime->tm_wday);
303  st.wHour = static_cast<uint16_t>(pTime->tm_hour);
304  st.wMinute = static_cast<uint16_t>(pTime->tm_min);
305  st.wSecond = static_cast<uint16_t>(pTime->tm_sec);
306  st.wMilliseconds = 0;
307}
308
309CPDFSDK_DateTime CPDFSDK_DateTime::ToGMT() const {
310  CPDFSDK_DateTime new_dt = *this;
311  new_dt.AddSeconds(-GetTimeZoneInSeconds(new_dt.m_tzHour, new_dt.m_tzMinute));
312  new_dt.m_tzHour = 0;
313  new_dt.m_tzMinute = 0;
314  return new_dt;
315}
316
317CPDFSDK_DateTime& CPDFSDK_DateTime::AddDays(short days) {
318  if (days == 0)
319    return *this;
320
321  int16_t y = m_year;
322  uint8_t m = m_month;
323  uint8_t d = m_day;
324
325  int ldays = days;
326  if (ldays > 0) {
327    int16_t yy = y;
328    if ((static_cast<uint16_t>(m) * 100 + d) > 300)
329      yy++;
330    int ydays = GetYearDays(yy);
331    int mdays;
332    while (ldays >= ydays) {
333      y++;
334      ldays -= ydays;
335      yy++;
336      mdays = GetMonthDays(y, m);
337      if (d > mdays) {
338        m++;
339        d -= mdays;
340      }
341      ydays = GetYearDays(yy);
342    }
343    mdays = GetMonthDays(y, m) - d + 1;
344    while (ldays >= mdays) {
345      ldays -= mdays;
346      m++;
347      d = 1;
348      mdays = GetMonthDays(y, m);
349    }
350    d += ldays;
351  } else {
352    ldays *= -1;
353    int16_t yy = y;
354    if ((static_cast<uint16_t>(m) * 100 + d) < 300)
355      yy--;
356    int ydays = GetYearDays(yy);
357    while (ldays >= ydays) {
358      y--;
359      ldays -= ydays;
360      yy--;
361      int mdays = GetMonthDays(y, m);
362      if (d > mdays) {
363        m++;
364        d -= mdays;
365      }
366      ydays = GetYearDays(yy);
367    }
368    while (ldays >= d) {
369      ldays -= d;
370      m--;
371      d = GetMonthDays(y, m);
372    }
373    d -= ldays;
374  }
375
376  m_year = y;
377  m_month = m;
378  m_day = d;
379
380  return *this;
381}
382
383CPDFSDK_DateTime& CPDFSDK_DateTime::AddSeconds(int seconds) {
384  if (seconds == 0)
385    return *this;
386
387  int n;
388  int days;
389
390  n = m_hour * 3600 + m_minute * 60 + m_second + seconds;
391  if (n < 0) {
392    days = (n - 86399) / 86400;
393    n -= days * 86400;
394  } else {
395    days = n / 86400;
396    n %= 86400;
397  }
398  m_hour = static_cast<uint8_t>(n / 3600);
399  m_hour %= 24;
400  n %= 3600;
401  m_minute = static_cast<uint8_t>(n / 60);
402  m_second = static_cast<uint8_t>(n % 60);
403  if (days != 0)
404    AddDays(days);
405
406  return *this;
407}
408