Branch data Line data Source code
1 : : /* -*- mode: C -*-
2 : : *
3 : : * File: pdf-time-string.c
4 : : * Date: Sun May 18 13:08:37 2008
5 : : *
6 : : * GNU PDF Library - Time Module String utilities
7 : : *
8 : : */
9 : :
10 : : /* Copyright (C) 2008 Free Software Foundation, Inc. */
11 : :
12 : : /* This program is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU General Public License as published by
14 : : * the Free Software Foundation, either version 3 of the License, or
15 : : * (at your option) any later version.
16 : : *
17 : : * This program is distributed in the hope that it will be useful,
18 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : : * GNU General Public License for more details.
21 : : *
22 : : * You should have received a copy of the GNU General Public License
23 : : * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : : */
25 : :
26 : : #include <config.h>
27 : :
28 : : #include <stdio.h>
29 : : #include <string.h>
30 : : #include <stdlib.h>
31 : :
32 : : #include <pdf-alloc.h>
33 : : #include <pdf-time-string.h>
34 : :
35 : : /* In order to use this macro, make sure that every byte represents a digit */
36 : : #define __GET_FIELD2(str,start,dest) \
37 : : (dest += ((str[start] - 48) * 10 + (str[start + 1] - 48)));
38 : :
39 : :
40 : : /* Macro to check if given bytes are digits or not */
41 : : #define __CHECK_MASK(mask, masklength, str, i) \
42 : : while (i < masklength) { \
43 : : if (mask & (1 << i)) { \
44 : : if((str[i] < '0') || \
45 : : (str[i] > '9')) { \
46 : : PDF_DEBUG_BASE("Expected digit and found '%c' in '%s'", str[i], str); \
47 : : return PDF_EBADDATA; \
48 : : } \
49 : : } \
50 : : i++; \
51 : : }
52 : :
53 : : /* Maximum length of strings, including trailing NUL */
54 : : #define PDF_MAX_ISO8601_STR_LENGTH 30
55 : : #define PDF_MAX_UTCASN1_STR_LENGTH 19
56 : : #define PDF_MAX_GENASN1_STR_LENGTH 21
57 : : #define PDF_MAX_PDFDATE_STR_LENGTH 24
58 : :
59 : : static pdf_bool_t
60 : 15718 : pdf_time_check_string_pdf (const pdf_char_t *time_str,
61 : : const pdf_size_t time_str_length,
62 : : pdf_bool_t require_trailing_apostrophe,
63 : : pdf_error_t **error)
64 : : {
65 : : pdf_i32_t i;
66 : :
67 : : /* Check minimum length D:YYYY */
68 [ - + ]: 15718 : if (time_str_length < 6)
69 : : {
70 : 0 : pdf_set_error (error,
71 : : PDF_EDOMAIN_BASE_TIME,
72 : : PDF_EBADDATA,
73 : : "invalid PDF time string, too short (%u): '%s'",
74 : : time_str_length,
75 : : time_str);
76 : 0 : return PDF_FALSE;
77 : : }
78 : :
79 : : /* Check prefix */
80 [ - + ]: 15718 : if (strncmp (time_str, "D:", 2) != 0)
81 : : {
82 : 0 : pdf_set_error (error,
83 : : PDF_EDOMAIN_BASE_TIME,
84 : : PDF_EBADDATA,
85 : : "invalid PDF time string, no prefix: '%s",
86 : : time_str);
87 : 0 : return PDF_FALSE;
88 : : }
89 : :
90 : : /* We need to check the input characters are digits when we expect digits and
91 : : * the opposite as well. */
92 : :
93 : : #define PDF_STRING_DIGIT_MASK 0x36FFFC /* 0011 0110 1111 1111 1111 1100 */
94 : :
95 : : /* Don't really need to check again two first bytes, as already done before */
96 : 15718 : i = 2;
97 [ + + ][ - + ]: 337464 : __CHECK_MASK (PDF_STRING_DIGIT_MASK, time_str_length, time_str, i);
[ + + ]
98 : :
99 : : /* Check time zone definer */
100 [ + - ][ + + ]: 15718 : if ((time_str_length >=16) &&
[ + + ][ - + ]
101 : 15718 : (time_str[16] != 'Z') &&
102 : 15632 : (time_str[16] != '+') &&
103 : 8256 : (time_str[16] != '-'))
104 : : {
105 : 0 : pdf_set_error (error,
106 : : PDF_EDOMAIN_BASE_TIME,
107 : : PDF_EBADDATA,
108 : : "invalid PDF time string, "
109 : : "invalid time zone identifier ('%c'): '%s",
110 : 0 : time_str[16],
111 : : time_str);
112 : 0 : return PDF_FALSE;
113 : : }
114 : :
115 : : /* Check additional ' characters. Remember that the last ' character is
116 : : * mandatory depending on the require_trailing_apostrophe parameter */
117 [ + + ][ - + ]: 15718 : if ((time_str_length >= 20) &&
118 : 15632 : (time_str[19] != '\''))
119 : : {
120 : 0 : pdf_set_error (error,
121 : : PDF_EDOMAIN_BASE_TIME,
122 : : PDF_EBADDATA,
123 : : "invalid PDF time string, "
124 : : "invalid separator ('%c'): '%s",
125 : 0 : time_str[19],
126 : : time_str);
127 : 0 : return PDF_FALSE;
128 : : }
129 : :
130 [ + + ]: 15718 : if (require_trailing_apostrophe)
131 : : {
132 [ + + ][ - + ]: 7816 : if ((time_str_length < 23) ||
133 : 3908 : ((time_str[22] != '\'')))
134 : : {
135 [ + - ]: 3908 : pdf_set_error (error,
136 : : PDF_EDOMAIN_BASE_TIME,
137 : : PDF_EBADDATA,
138 : : "invalid PDF time string, "
139 : : "invalid separator ('%c'): '%s",
140 : 3908 : (time_str_length >= 21) ? time_str[22] : ' ',
141 : : time_str);
142 : 3908 : return PDF_FALSE;
143 : : }
144 : : }
145 : : else
146 : : {
147 [ + + ]: 7902 : if (time_str_length >= 23)
148 : : {
149 : 3908 : pdf_set_error (error,
150 : : PDF_EDOMAIN_BASE_TIME,
151 : : PDF_EBADDATA,
152 : : "invalid PDF time string, invalid separator ('%c'): '%s",
153 : 3908 : time_str[19],
154 : : time_str);
155 : 3908 : return PDF_FALSE;
156 : : }
157 : : }
158 : :
159 : 15718 : return PDF_TRUE;
160 : : }
161 : :
162 : : pdf_bool_t
163 : 15718 : pdf_time_from_string_pdf (pdf_time_t *time_var,
164 : : const pdf_char_t *time_str,
165 : : pdf_bool_t require_trailing_apostrophe,
166 : : pdf_error_t **error)
167 : : {
168 : : /*
169 : : * From PDF Reference 1.7: ( D:YYYYMMDDHHmmSSOHH'mm' )
170 : : * From ISO 32000: ( D:YYYYMMDDHHmmSSOHH'mm )
171 : : *
172 : : * Notes: Year is mandatory, all the other fields may appear if the preceding
173 : : * also appear.
174 : : *
175 : : * D: = string "D:"
176 : : * YYYY = four-digit year
177 : : * MM = two-digit month (01=January, etc.)
178 : : * DD = two-digit day of month (01 through 31)
179 : : * HH = two digits of hour (00 through 23)
180 : : * mm = two digits of minute (00 through 59)
181 : : * SS = two digits of second (00 through 59)
182 : : * O = either '+', '-' or 'Z'
183 : : * HH = two digits of hour (00 through 23) for the GMT offset
184 : : * ' = string "'"
185 : : * MM = two digits of minute (00 through 59) for the GMT offset
186 : : * ' = string "'" (NOTE: Mandatory in 1.7, non-valid in ISO32000)
187 : : */
188 : :
189 : 15718 : struct pdf_time_cal_s calendar = { 0 };
190 : : pdf_size_t time_str_length;
191 : :
192 : 15718 : time_str_length = strlen (time_str);
193 : :
194 [ + + ]: 15718 : if (!pdf_time_check_string_pdf (time_str,
195 : : time_str_length,
196 : : require_trailing_apostrophe,
197 : : error))
198 : : {
199 : 7816 : pdf_prefix_error (error,
200 : : "couldn't get time from PDF date string: ");
201 : 7816 : return PDF_FALSE;
202 : : }
203 : :
204 : : while (PDF_TRUE)
205 : : {
206 : : /* Get century */
207 : 7902 : __GET_FIELD2 (time_str, 2, calendar.year);
208 : 7902 : calendar.year *= 100;
209 : : /* Get year in century */
210 : 7902 : __GET_FIELD2 (time_str, 4, calendar.year);
211 : : /* more than year ? */
212 [ - + ]: 7902 : if (time_str_length == 6)
213 : : {
214 : 0 : calendar.month = 1;
215 : 0 : calendar.day = 1;
216 : 0 : break;
217 : : }
218 : :
219 : : /* Get month */
220 : 7902 : __GET_FIELD2 (time_str, 6, calendar.month);
221 : : /* more than month ? */
222 [ - + ]: 7902 : if (time_str_length == 8)
223 : : {
224 : 0 : calendar.day = 1;
225 : 0 : break;
226 : : }
227 : :
228 : : /* Get day */
229 : 7902 : __GET_FIELD2 (time_str, 8, calendar.day);
230 : : /* more than day ? */
231 [ + - ]: 7902 : if (time_str_length == 10)
232 : : break;
233 : :
234 : : /* Get hour */
235 : 7902 : __GET_FIELD2 (time_str, 10, calendar.hour);
236 : : /* more than hour ? */
237 [ + - ]: 7902 : if (time_str_length == 12)
238 : : break;
239 : :
240 : : /* Get minute */
241 : 7902 : __GET_FIELD2 (time_str, 12, calendar.minute);
242 : : /* more than minute ? */
243 [ + - ]: 7902 : if (time_str_length == 14)
244 : : break;
245 : :
246 : : /* Get second */
247 : 7902 : __GET_FIELD2 (time_str, 14, calendar.second);
248 : : /* more than second ? */
249 [ + + ]: 7902 : if (time_str_length <= 17) /* Considering timezone offset separator */
250 : : break;
251 : :
252 : : /* Get timezone offset hours */
253 : 7816 : __GET_FIELD2 (time_str, 17, calendar.gmt_offset);
254 : : /* And convert it in minutes */
255 : 7816 : calendar.gmt_offset *= 60;
256 : :
257 : : /* Get timezone offset minutes */
258 [ + - ]: 7816 : if (time_str_length > 19)
259 : 7816 : __GET_FIELD2 (time_str, 20, calendar.gmt_offset);
260 : :
261 : : /* Convert from minutes to seconds */
262 : 7816 : calendar.gmt_offset *= 60;
263 : :
264 : : /* Set proper sign */
265 [ + + ]: 7816 : if (time_str[16] == '-')
266 : 4128 : calendar.gmt_offset *= (-1);
267 : :
268 : : /* Stop loop :-) */
269 : : break;
270 : : }
271 : :
272 : : /* Get time value from break-down UTC calendar !*/
273 : 7902 : pdf_time_set_from_cal (time_var, &calendar);
274 : :
275 : 15718 : return PDF_TRUE;
276 : : }
277 : :
278 : : static pdf_bool_t
279 : 7988 : pdf_time_check_string_utc_asn1 (const pdf_char_t *time_str,
280 : : const pdf_size_t time_str_length,
281 : : pdf_error_t **error)
282 : : {
283 : : pdf_i32_t i;
284 : : pdf_i32_t base_mask;
285 : : pdf_i32_t mask_length;
286 : : pdf_bool_t with_gmt_offset;
287 : :
288 : : #define UTCASN1_STRING_DIGIT_MASK1 0x03FF /* 0000 0011 1111 1111 */
289 : : #define UTCASN1_STRING_DIGIT_MASK2 0x0FFF /* 0000 1111 1111 1111 */
290 : :
291 : : /* Check length */
292 [ + + ]: 7988 : if ((time_str_length == 11) ||
293 : 7988 : (time_str_length == 15))
294 : : {
295 : 3994 : base_mask = UTCASN1_STRING_DIGIT_MASK1;
296 : 3994 : mask_length = 10;
297 : : }
298 [ + - ]: 3994 : else if ((time_str_length == 13) ||
299 : 3994 : (time_str_length == 17))
300 : : {
301 : 3994 : base_mask = UTCASN1_STRING_DIGIT_MASK2;
302 : 3994 : mask_length = 12;
303 : : }
304 : : else
305 : : {
306 : 0 : pdf_set_error (error,
307 : : PDF_EDOMAIN_BASE_TIME,
308 : : PDF_EBADDATA,
309 : : "invalid UTC-ASN1 time string, invalid length (%u): '%s'",
310 : : time_str_length,
311 : : time_str);
312 : 0 : return PDF_FALSE;
313 : : }
314 : :
315 : : /* Check if GMT offset is expected */
316 : 7988 : with_gmt_offset = (time_str_length >= 15) ? PDF_TRUE : PDF_FALSE;
317 : :
318 : : /* Check extra non-digit characters */
319 [ + + ][ - + ]: 7988 : if ((!with_gmt_offset) &&
320 : 172 : (time_str[time_str_length - 1] != 'Z'))
321 : : {
322 : 0 : pdf_set_error (error,
323 : : PDF_EDOMAIN_BASE_TIME,
324 : : PDF_EBADDATA,
325 : : "invalid UTC-ASN1 time string, expected UTC string "
326 : : "but no Zulu timezone identifier found: '%s'",
327 : : time_str);
328 : 0 : return PDF_FALSE;
329 : : }
330 : :
331 [ + + ][ - + ]: 7988 : if ((with_gmt_offset) &&
332 : 7816 : ((time_str[time_str_length - 5] != '+') &&
333 : : (time_str[time_str_length - 5] != '-')))
334 : : {
335 : 0 : pdf_set_error (error,
336 : : PDF_EDOMAIN_BASE_TIME,
337 : : PDF_EBADDATA,
338 : : "invalid UTC-ASN1 time string, expected non-UTC string "
339 : : "but no GMT offset found: '%s'",
340 : : time_str);
341 : 0 : return PDF_FALSE;
342 : : }
343 : :
344 : : /* Check mask if base string */
345 : 7988 : i = 0;
346 [ + - ][ - + ]: 95856 : __CHECK_MASK (base_mask, mask_length, time_str, i);
[ + + ]
347 : : /* Check mask of offset string if available */
348 [ + + ]: 7988 : if (with_gmt_offset)
349 : : {
350 : 7816 : i = time_str_length - 4;
351 [ # # ][ # # ]: 7816 : __CHECK_MASK (0x000F, 4, time_str, i);
[ - + ]
352 : : }
353 : :
354 : 7988 : return PDF_TRUE;
355 : : }
356 : :
357 : : static pdf_i32_t
358 : : pdf_time_get_century_in_sliding_window (pdf_i32_t year_in_century)
359 : : {
360 : 7988 : pdf_i32_t full_year = -1;
361 : : pdf_time_t current;
362 : : pdf_i32_t century;
363 : : struct pdf_time_cal_s current_cal;
364 : :
365 : 7988 : pdf_time_init (¤t);
366 : :
367 : 7988 : pdf_time_set_to_current_utc_time (¤t);
368 : 7988 : pdf_time_get_utc_cal (¤t, ¤t_cal);
369 : :
370 : : /* Get century from full current year */
371 : 7988 : century = 100 * (current_cal.year / 100);
372 : :
373 : : /* Appy current century */
374 : 7988 : full_year = century + year_in_century;
375 : :
376 : : /* Check if the century must be changed */
377 [ + + + - ]: 7988 : if ((full_year > current_cal.year) &&
378 : 4988 : ((full_year - current_cal.year) > 50))
379 : : {
380 : 4988 : full_year -= 100;
381 : : }
382 [ + - ][ - + ]: 3000 : else if ((full_year < current_cal.year) &&
383 : 3000 : ((current_cal.year - full_year) > 50))
384 : : {
385 : 0 : full_year += 100;
386 : : }
387 : :
388 : 7988 : pdf_time_deinit (¤t);
389 : :
390 : 7988 : return full_year;
391 : : }
392 : :
393 : : pdf_bool_t
394 : 7988 : pdf_time_from_string_utc_asn1 (pdf_time_t *time_var,
395 : : const pdf_char_t *time_str,
396 : : pdf_error_t **error)
397 : : {
398 : : /*
399 : : * yymmddhhmmZ
400 : : * yymmddhhmmssZ
401 : : * yymmddhhmm+hhmm
402 : : * yymmddhhmm-hhmm
403 : : * yymmddhhmmss+hhmm
404 : : * yymmddhhmmss-hhmm
405 : : *
406 : : * Note: As year is only stored in 2 digits, a sliding window of [-50,+50]
407 : : * years will be used to get the century.
408 : : */
409 : 7988 : struct pdf_time_cal_s calendar = { 0 };
410 : : pdf_size_t time_str_length;
411 : :
412 : 7988 : time_str_length = strlen (time_str);
413 : :
414 [ - + ]: 7988 : if (!pdf_time_check_string_utc_asn1 (time_str,
415 : : time_str_length,
416 : : error))
417 : : {
418 : 0 : pdf_prefix_error (error,
419 : : "couldn't get time from UTC ASN1 string: ");
420 : 0 : return PDF_FALSE;
421 : : }
422 : :
423 : : while (PDF_TRUE)
424 : : {
425 : 7988 : pdf_bool_t has_seconds = PDF_FALSE;
426 : :
427 : : /* Get year in century */
428 : 7988 : __GET_FIELD2 (time_str, 0, calendar.year);
429 : : /* Get 4-digit year from 2-digit year */
430 : 15976 : calendar.year = pdf_time_get_century_in_sliding_window (calendar.year);
431 : :
432 : : /* Get month */
433 : 7988 : __GET_FIELD2 (time_str, 2, calendar.month);
434 : :
435 : : /* Get day */
436 : 7988 : __GET_FIELD2 (time_str, 4, calendar.day);
437 : :
438 : : /* Get hour */
439 : 7988 : __GET_FIELD2 (time_str, 6, calendar.hour);
440 : :
441 : : /* Get minute */
442 : 7988 : __GET_FIELD2 (time_str, 8, calendar.minute);
443 : :
444 : : /* Get second if available */
445 [ + + ]: 7988 : if ((time_str[10] >= '0') &&
446 : : (time_str[10] <= '9'))
447 : : {
448 : 3994 : has_seconds = PDF_TRUE;
449 : 3994 : __GET_FIELD2 (time_str, 10, calendar.second);
450 : : }
451 : :
452 : : /* Check if we have GMT offset */
453 [ + + ]: 7988 : if (time_str[time_str_length - 1] == 'Z')
454 : : break;
455 : :
456 : : /* Get timezone offset hours */
457 [ + + ][ + + ]: 7816 : __GET_FIELD2 (time_str, (has_seconds ? 13 : 11), calendar.gmt_offset);
458 : : /* And convert it in minutes */
459 : 7816 : calendar.gmt_offset *= 60;
460 : : /* Get timezone offset minutes */
461 [ + + ][ + + ]: 7816 : __GET_FIELD2 (time_str, (has_seconds ? 15 : 13), calendar.gmt_offset);
462 : : /* Convert from minutes to seconds */
463 : 7816 : calendar.gmt_offset *= 60;
464 : :
465 : : /* Set proper sign */
466 [ + + ][ + + ]: 7816 : if (time_str[(has_seconds ? 12 : 10)] == '-')
467 : 4128 : calendar.gmt_offset *= (-1);
468 : :
469 : : /* Stop loop :-) */
470 : : break;
471 : : }
472 : :
473 : : /* Get time value from break-down UTC calendar !*/
474 : 7988 : pdf_time_set_from_cal (time_var, &calendar);
475 : :
476 : 7988 : return PDF_TRUE;
477 : : }
478 : :
479 : : pdf_bool_t
480 : 8162 : pdf_time_from_string_generalized_asn1 (pdf_time_t *time_var,
481 : : const pdf_char_t *time_str,
482 : : pdf_error_t **error)
483 : : {
484 : : /*
485 : : * Year:
486 : : * YYYY (eg 1997)
487 : : * Year and month:
488 : : * YYYYMM (eg 199707)
489 : : * Complete date:
490 : : * YYYYMMDD (eg 19970716)
491 : : * Complete date plus hours and minutes:
492 : : * YYYYMMDDhhmmTZD (eg 199707161920+01:00)
493 : : * Complete date plus hours, minutes and seconds:
494 : : * YYYYMMDDhhmmssTZD (eg 19970716192030+01:00)
495 : : * Complete date plus hours, minutes, seconds and a decimal fraction of a
496 : : * second
497 : : * YYYYMMDDThhmmss.sTZD (eg 1997071619:20:30.45+01:00)
498 : : *
499 : : * where:
500 : : *
501 : : * YYYY = four-digit year
502 : : * MM = two-digit month (01=January, etc.)
503 : : * DD = two-digit day of month (01 through 31)
504 : : * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
505 : : * mm = two digits of minute (00 through 59)
506 : : * ss = two digits of second (00 through 59)
507 : : * s = one or more digits representing a decimal fraction of a second
508 : : * TZD = time zone designator (Z or +hh:mm or -hh:mm)
509 : : *
510 : : */
511 : 8162 : struct pdf_time_cal_s calendar = { 0 };
512 : : pdf_size_t time_str_length;
513 : :
514 : 8162 : time_str_length = strlen (time_str);
515 : :
516 : : /* Check minimum length */
517 [ - + ]: 8162 : if (time_str_length < 4)
518 : : {
519 : 0 : pdf_set_error (error,
520 : : PDF_EDOMAIN_BASE_TIME,
521 : : PDF_EBADDATA,
522 : : "couldn't get time from Generalized ASN1 date string: "
523 : : "invalid date string, too short (%u): '%s'",
524 : : time_str_length,
525 : : time_str);
526 : 0 : return PDF_FALSE;
527 : : }
528 : :
529 : : while (PDF_TRUE)
530 : : {
531 : : /* Get century */
532 : 8162 : __GET_FIELD2 (time_str, 0, calendar.year);
533 : 8162 : calendar.year *= 100;
534 : : /* Get year in century */
535 : 8162 : __GET_FIELD2 (time_str, 2, calendar.year);
536 : : /* more than year ? */
537 [ + + ]: 8162 : if (time_str_length == 4)
538 : : {
539 : 40 : calendar.month = 1;
540 : 40 : calendar.day = 1;
541 : 40 : break;
542 : : }
543 : :
544 : : /* Get month */
545 : 8122 : __GET_FIELD2 (time_str, 4, calendar.month);
546 : : /* more than month ? */
547 [ + + ]: 8122 : if (time_str_length == 6)
548 : : {
549 : 48 : calendar.day = 1;
550 : 48 : break;
551 : : }
552 : :
553 : : /* Get day */
554 : 8074 : __GET_FIELD2 (time_str, 6, calendar.day);
555 : : /* more than day ? */
556 [ + + ]: 8074 : if (time_str_length == 8)
557 : : break;
558 : :
559 : : /* Get hour and minutes */
560 : 7988 : __GET_FIELD2 (time_str, 8, calendar.hour);
561 : 7988 : __GET_FIELD2 (time_str, 10, calendar.minute);
562 : :
563 : : /* Get second if available */
564 [ + + ]: 7988 : if ((time_str[17] >= '0') &&
565 : : (time_str[17] <= '9'))
566 : : {
567 : 3994 : __GET_FIELD2 (time_str, 12, calendar.second);
568 : : }
569 : :
570 : : /* Note: Fractional part of seconds not considered */
571 [ + + ]: 7988 : if (time_str[time_str_length - 1] == 'Z')
572 : : break;
573 : :
574 : : /* Get timezone offset hours */
575 : 7816 : __GET_FIELD2 (time_str, (time_str_length - 4), calendar.gmt_offset);
576 : : /* And convert it in minutes */
577 : 7816 : calendar.gmt_offset *= 60;
578 : : /* Get timezone offset minutes */
579 : 7816 : __GET_FIELD2 (time_str, (time_str_length - 2), calendar.gmt_offset);
580 : : /* Convert from minutes to seconds */
581 : 7816 : calendar.gmt_offset *= 60;
582 : :
583 : : /* Set proper sign */
584 [ + + ]: 7816 : if (time_str[(time_str_length - 5)] == '-')
585 : 4128 : calendar.gmt_offset *= (-1);
586 : :
587 : : /* Stop loop :-) */
588 : : break;
589 : : }
590 : :
591 : : /* Get time value from break-down calendar !*/
592 : 8162 : pdf_time_set_from_cal (time_var, &calendar);
593 : :
594 : 8162 : return PDF_TRUE;
595 : : }
596 : :
597 : : pdf_bool_t
598 : 8208 : pdf_time_from_string_iso8601 (pdf_time_t *time_var,
599 : : const pdf_char_t *time_str,
600 : : pdf_error_t **error)
601 : : {
602 : : /*
603 : : * Year:
604 : : * YYYY (eg 1997)
605 : : * Year and month:
606 : : * YYYY-MM (eg 1997-07)
607 : : * Complete date:
608 : : * YYYY-MM-DD (eg 1997-07-16)
609 : : * Complete date plus hours and minutes:
610 : : * YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
611 : : * Complete date plus hours, minutes and seconds:
612 : : * YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
613 : : * Complete date plus hours, minutes, seconds and a decimal fraction of a
614 : : * second
615 : : * YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
616 : : *
617 : : * where:
618 : : *
619 : : * YYYY = four-digit year
620 : : * MM = two-digit month (01=January, etc.)
621 : : * DD = two-digit day of month (01 through 31)
622 : : * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
623 : : * mm = two digits of minute (00 through 59)
624 : : * ss = two digits of second (00 through 59)
625 : : * s = one or more digits representing a decimal fraction of a second
626 : : * TZD = time zone designator (Z or +hh:mm or -hh:mm)
627 : : *
628 : : */
629 : 8208 : struct pdf_time_cal_s calendar = { 0 };
630 : : pdf_size_t time_str_length;
631 : :
632 : 8208 : time_str_length = strlen (time_str);
633 : :
634 : : /* Check minimum length */
635 [ - + ]: 8208 : if (time_str_length < 4)
636 : : {
637 : 0 : pdf_set_error (error,
638 : : PDF_EDOMAIN_BASE_TIME,
639 : : PDF_EBADDATA,
640 : : "couldn't get time from ISO-8601 date string: "
641 : : "invalid date string, too short (%u): '%s'",
642 : : time_str_length,
643 : : time_str);
644 : 0 : return PDF_FALSE;
645 : : }
646 : :
647 : : while (PDF_TRUE)
648 : : {
649 : : /* Get century */
650 : 8208 : __GET_FIELD2 (time_str, 0, calendar.year);
651 : 8208 : calendar.year *= 100;
652 : : /* Get year in century */
653 : 8208 : __GET_FIELD2 (time_str, 2, calendar.year);
654 : : /* more than year ? */
655 [ + + ]: 8208 : if (time_str_length == 4)
656 : : {
657 : 86 : calendar.month = 1;
658 : 86 : calendar.day = 1;
659 : 86 : break;
660 : : }
661 : :
662 : : /* Get month */
663 : 8122 : __GET_FIELD2 (time_str, 5, calendar.month);
664 : : /* more than month ? */
665 [ + + ]: 8122 : if (time_str_length == 7)
666 : : {
667 : 48 : calendar.day = 1;
668 : 48 : break;
669 : : }
670 : :
671 : : /* Get day */
672 : 8074 : __GET_FIELD2 (time_str, 8, calendar.day);
673 : : /* more than day ? */
674 [ + + ]: 8074 : if (time_str_length == 10)
675 : : break;
676 : :
677 : : /* Get hour and minutes */
678 : 7988 : __GET_FIELD2 (time_str, 11, calendar.hour);
679 : 7988 : __GET_FIELD2 (time_str, 14, calendar.minute);
680 : :
681 : : /* Get second if available */
682 [ + + ][ + + ]: 7988 : if ((time_str[17] >= '0') &&
683 : 7988 : (time_str[17] <= '9') &&
684 : 7902 : (time_str[16] == ':'))
685 : : {
686 : 3994 : __GET_FIELD2 (time_str, 17, calendar.second);
687 : : }
688 : :
689 : : /* Note: Fractional part of seconds not considered */
690 [ + + ]: 7988 : if (time_str[time_str_length - 1] == 'Z')
691 : : break;
692 : :
693 : : /* Get timezone offset hours */
694 : 7816 : __GET_FIELD2 (time_str, (time_str_length - 5), calendar.gmt_offset);
695 : : /* And convert it in minutes */
696 : 7816 : calendar.gmt_offset *= 60;
697 : : /* Get timezone offset minutes */
698 : 7816 : __GET_FIELD2 (time_str, (time_str_length - 2), calendar.gmt_offset);
699 : : /* Convert from minutes to seconds */
700 : 7816 : calendar.gmt_offset *= 60;
701 : :
702 : : /* Set proper sign */
703 [ + + ]: 7816 : if (time_str[(time_str_length - 6)] == '-')
704 : 4128 : calendar.gmt_offset *= (-1);
705 : :
706 : : /* Stop loop :-) */
707 : : break;
708 : : }
709 : :
710 : : /* Get time value from break-down calendar !*/
711 : 8208 : pdf_time_set_from_cal (time_var, &calendar);
712 : :
713 : 8208 : return PDF_TRUE;
714 : : }
715 : :
716 : : /* Get Date as a string in PDF format */
717 : : pdf_char_t *
718 : 6860 : pdf_time_to_string_pdf (const pdf_time_t *time_var,
719 : : pdf_bool_t include_trailing_apostrophe,
720 : : pdf_error_t **error)
721 : : {
722 : : pdf_char_t *str;
723 : 6860 : struct pdf_time_cal_s calendar = { 0 };
724 : :
725 : 6860 : str = (pdf_char_t *) pdf_alloc (PDF_MAX_PDFDATE_STR_LENGTH);
726 [ - + ]: 6860 : if (!str)
727 : : {
728 : 0 : pdf_set_error (error,
729 : : PDF_EDOMAIN_BASE_TIME,
730 : : PDF_ENOMEM,
731 : : "cannot create string with PDF time: "
732 : : "couldn't allocate %lu bytes",
733 : : PDF_MAX_PDFDATE_STR_LENGTH);
734 : 0 : return NULL;
735 : : }
736 : :
737 : : /* D:YYYYMMDDHHmmSSOHH'mm' */
738 : 6860 : pdf_time_get_local_cal (time_var, &calendar);
739 : :
740 [ + + ]: 6860 : if (calendar.gmt_offset != 0)
741 : : {
742 : : pdf_i32_t offset_hours;
743 : : pdf_i32_t offset_minutes;
744 : :
745 [ + + ]: 6720 : offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) / 3600;
746 [ + + ]: 6720 : offset_minutes = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) % 3600;
747 : 6720 : offset_minutes /= 60; /* Get only full minutes */
748 [ + + ][ + + ]: 6720 : sprintf (str, "D:%4d%s%d%s%d%s%d%s%d%s%d%c%s%d'%s%d",
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
749 : 6720 : calendar.year,
750 : 13440 : (calendar.month < 10 ? "0" : ""), calendar.month,
751 : 13440 : (calendar.day < 10 ? "0" : ""), calendar.day,
752 : 13440 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
753 : 13440 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
754 : 13440 : (calendar.second < 10 ? "0" : ""), calendar.second,
755 : 6720 : ((calendar.gmt_offset < 0) ? '-' : '+'),
756 : : (offset_hours < 10 ? "0" : ""), offset_hours,
757 : : (offset_minutes < 10 ? "0" : ""), offset_minutes);
758 : :
759 [ + + ]: 6720 : if (include_trailing_apostrophe)
760 : : {
761 : 3360 : str[PDF_MAX_PDFDATE_STR_LENGTH - 2] = '\'';
762 : 3360 : str[PDF_MAX_PDFDATE_STR_LENGTH - 1] = '\0';
763 : : }
764 : : }
765 : : else
766 : : {
767 [ + + ][ + + ]: 140 : sprintf (str, "D:%4d%s%d%s%d%s%d%s%d%s%dZ",
[ + + ][ + + ]
[ + + ]
768 : 140 : calendar.year,
769 : 280 : (calendar.month < 10 ? "0" : ""), calendar.month,
770 : 280 : (calendar.day < 10 ? "0" : ""), calendar.day,
771 : 280 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
772 : 280 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
773 : 280 : (calendar.second < 10 ? "0" : ""), calendar.second);
774 : : }
775 : :
776 : 6860 : return str;
777 : : }
778 : :
779 : : /* Get Date as a string in UTC-ASN1 format */
780 : : pdf_char_t *
781 : 3430 : pdf_time_to_string_utc_asn1(const pdf_time_t *time_var,
782 : : pdf_error_t **error)
783 : : {
784 : : pdf_char_t *str;
785 : : struct pdf_time_cal_s calendar;
786 : : pdf_i32_t smallyear;
787 : :
788 : 3430 : str = (pdf_char_t *) pdf_alloc (PDF_MAX_UTCASN1_STR_LENGTH);
789 [ - + ]: 3430 : if (!str)
790 : : {
791 : 0 : pdf_set_error (error,
792 : : PDF_EDOMAIN_BASE_TIME,
793 : : PDF_ENOMEM,
794 : : "cannot create string with UTC ASN1 time: "
795 : : "couldn't allocate %lu bytes",
796 : : PDF_MAX_UTCASN1_STR_LENGTH);
797 : 0 : return NULL;
798 : : }
799 : :
800 : 3430 : pdf_time_get_local_cal (time_var, &calendar);
801 : :
802 : : /* Convert 4-digit year to 2-digit year */
803 : 3430 : smallyear = calendar.year -1900;
804 [ + + ]: 4900 : while (smallyear > 99)
805 : 1470 : smallyear -= 100;
806 : :
807 [ + + ]: 3430 : if (calendar.gmt_offset != 0)
808 : : {
809 : : pdf_i32_t offset_hours;
810 : : pdf_i32_t offset_minutes;
811 : :
812 [ + + ]: 3360 : offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) / 3600;
813 [ + + ]: 3360 : offset_minutes = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) % 3600;
814 : 3360 : offset_minutes /= 60;
815 : : /* yymmddhhmmss+hhmm
816 : : * yymmddhhmmss-hhmm
817 : : */
818 [ + + ][ + + ]: 3360 : sprintf (str, "%s%d%s%d%s%d%s%d%s%d%s%d%c%s%d%s%d",
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ]
819 : : (smallyear < 10 ? "0" : ""), smallyear,
820 : 6720 : (calendar.month < 10 ? "0" : ""), calendar.month,
821 : 6720 : (calendar.day < 10 ? "0" : ""), calendar.day,
822 : 6720 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
823 : 6720 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
824 : 6720 : (calendar.second < 10 ? "0" : ""), calendar.second,
825 : 3360 : ((calendar.gmt_offset < 0) ? '-' : '+'),
826 : : (offset_hours < 10 ? "0" : ""), offset_hours,
827 : : (offset_minutes < 10 ? "0" : ""), offset_minutes);
828 : : }
829 : : else
830 : : {
831 : : /*
832 : : * yymmddhhmmssZ
833 : : */
834 [ + + ][ + + ]: 70 : sprintf (str, "%s%d%s%d%s%d%s%d%s%d%s%dZ",
[ + + ][ + + ]
[ + + ][ + + ]
835 : : (smallyear < 10 ? "0" : ""), smallyear,
836 : 140 : (calendar.month < 10 ? "0" : ""), calendar.month,
837 : 140 : (calendar.day < 10 ? "0" : ""), calendar.day,
838 : 140 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
839 : 140 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
840 : 140 : (calendar.second < 10 ? "0" : ""), calendar.second);
841 : : }
842 : :
843 : 3430 : return str;
844 : : }
845 : :
846 : : /* Get Date as a string in Generalized ASN1 format */
847 : : pdf_char_t *
848 : 3430 : pdf_time_to_string_generalized_asn1(const pdf_time_t *time_var,
849 : : pdf_error_t **error)
850 : : {
851 : : pdf_char_t *str;
852 : : struct pdf_time_cal_s calendar;
853 : :
854 : 3430 : str = (pdf_char_t *) pdf_alloc (PDF_MAX_GENASN1_STR_LENGTH);
855 [ - + ]: 3430 : if (!str)
856 : : {
857 : 0 : pdf_set_error (error,
858 : : PDF_EDOMAIN_BASE_TIME,
859 : : PDF_ENOMEM,
860 : : "cannot create string with Generalized ASN1 time: "
861 : : "couldn't allocate %lu bytes",
862 : : PDF_MAX_GENASN1_STR_LENGTH);
863 : 0 : return NULL;
864 : : }
865 : :
866 : : /* YYYYMMDDhhmmssTZD (eg 19970716192030+01:00) */
867 : 3430 : pdf_time_get_local_cal (time_var, &calendar);
868 : :
869 [ + + ]: 3430 : if (calendar.gmt_offset != 0)
870 : : {
871 : : pdf_i32_t offset_hours;
872 : : pdf_i32_t offset_minutes;
873 : :
874 [ + + ]: 3360 : offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) / 3600;
875 [ + + ]: 3360 : offset_minutes = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) % 3600;
876 : 3360 : offset_minutes /= 60;
877 [ + + ][ + + ]: 3360 : sprintf (str, "%4d%s%d%s%d%s%d%s%d%s%d%c%s%d%s%d",
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
878 : 3360 : calendar.year,
879 : 6720 : (calendar.month < 10 ? "0" : ""), calendar.month,
880 : 6720 : (calendar.day < 10 ? "0" : ""), calendar.day,
881 : 6720 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
882 : 6720 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
883 : 6720 : (calendar.second < 10 ? "0" : ""), calendar.second,
884 : 3360 : ((calendar.gmt_offset < 0) ? '-' : '+'),
885 : : (offset_hours < 10 ? "0" : ""), offset_hours,
886 : : (offset_minutes < 10 ? "0" : ""), offset_minutes);
887 : : }
888 : : else
889 : : {
890 [ + + ][ + + ]: 70 : sprintf (str, "%4d%s%d%s%d%s%d%s%d%s%dZ",
[ + + ][ + + ]
[ + + ]
891 : 70 : calendar.year,
892 : 140 : (calendar.month < 10 ? "0" : ""), calendar.month,
893 : 140 : (calendar.day < 10 ? "0" : ""), calendar.day,
894 : 140 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
895 : 140 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
896 : 140 : (calendar.second < 10 ? "0" : ""), calendar.second);
897 : : }
898 : :
899 : 3430 : return str;
900 : : }
901 : :
902 : : /* Get Date as a string in ISO8601 format */
903 : : pdf_char_t *
904 : 3431 : pdf_time_to_string_iso8601(const pdf_time_t *time_var,
905 : : pdf_error_t **error)
906 : : {
907 : : pdf_char_t *str;
908 : : struct pdf_time_cal_s calendar;
909 : :
910 : 3431 : str = (pdf_char_t *) pdf_alloc (PDF_MAX_ISO8601_STR_LENGTH);
911 [ - + ]: 3431 : if (!str)
912 : : {
913 : 0 : pdf_set_error (error,
914 : : PDF_EDOMAIN_BASE_TIME,
915 : : PDF_ENOMEM,
916 : : "cannot create string with ISO8601 time: "
917 : : "couldn't allocate %lu bytes",
918 : : PDF_MAX_ISO8601_STR_LENGTH);
919 : 0 : return NULL;
920 : : }
921 : :
922 : : /* YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) */
923 : 3431 : pdf_time_get_local_cal (time_var, &calendar);
924 [ + + ]: 3431 : if (calendar.gmt_offset != 0)
925 : : {
926 : : pdf_i32_t offset_hours;
927 : : pdf_i32_t offset_minutes;
928 : :
929 [ + + ]: 3360 : offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) / 3600;
930 [ + + ]: 3360 : offset_minutes = (((calendar.gmt_offset < 0) ? (-1) : (1)) * calendar.gmt_offset) % 3600;
931 : 3360 : offset_minutes /= 60; /* Get only full minutes */
932 [ + + ][ + + ]: 3360 : sprintf (str, "%4d-%s%d-%s%dT%s%d:%s%d:%s%d%c%s%d:%s%d",
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
933 : 3360 : calendar.year,
934 : 6720 : (calendar.month < 10 ? "0" : ""), calendar.month,
935 : 6720 : (calendar.day < 10 ? "0" : ""), calendar.day,
936 : 6720 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
937 : 6720 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
938 : 6720 : (calendar.second < 10 ? "0" : ""), calendar.second,
939 : 3360 : ((calendar.gmt_offset < 0) ? '-' : '+'),
940 : : (offset_hours < 10 ? "0" : ""), offset_hours,
941 : : (offset_minutes < 10 ? "0" : ""), offset_minutes);
942 : : }
943 : : else
944 : : {
945 [ + + ][ + + ]: 71 : sprintf (str, "%4d-%s%d-%s%dT%s%d:%s%d:%s%dZ",
[ + + ][ + + ]
[ + + ]
946 : 71 : calendar.year,
947 : 142 : (calendar.month < 10 ? "0" : ""), calendar.month,
948 : 142 : (calendar.day < 10 ? "0" : ""), calendar.day,
949 : 142 : (calendar.hour < 10 ? "0" : ""), calendar.hour,
950 : 142 : (calendar.minute < 10 ? "0" : ""), calendar.minute,
951 : 142 : (calendar.second < 10 ? "0" : ""), calendar.second);
952 : : }
953 : :
954 : 3431 : return str;
955 : : }
956 : :
957 : : /* End of pdf-time-string.c */
|