[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[pdf-devel] Time module
From: |
Aleksander Morgado |
Subject: |
[pdf-devel] Time module |
Date: |
Fri, 25 Jul 2008 19:43:53 +0200 |
User-agent: |
Thunderbird 2.0.0.16 (X11/20080724) |
Hi,
I finished the development of the Time module, at least until the unit
tests from Anish get commited ;-).
The changes done are basically three:
* Corrected some minor bug which prevented the local calendar being
constructed in the correct way.
* Finished UTC-ASN1 and Generalized-ASN1 string handling.
* Modified the API to reflect that 'ASN1' is really 'UTC-ASN1'.
Find the patch below.
Cheers,
-Aleksander
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: address@hidden
# target_branch: file:///home/aleksander/Development/libgnupdf_repo\
# /trunk/
# testament_sha1: 41dcfcab92a845d23dd883c4f4e326e858d18c88
# timestamp: 2008-07-25 19:26:01 +0200
# base_revision_id: address@hidden
#
# Begin patch
=== modified file 'ChangeLog'
--- ChangeLog 2008-07-25 13:30:00 +0000
+++ ChangeLog 2008-07-25 17:25:07 +0000
@@ -1,3 +1,26 @@
+2008-07-25 Aleksander Morgado <address@hidden>
+
+ * src/base/pdf-time.c (pdf_time_get_cal): Function simplified to avoid
+ using pdf_i64_t when not really needed. Also small bugfix in that
+ function to correctly move the time value to local time depending on
+ the gmt_offset.
+
+ * src/base/pdf-time-string.h (pdf_time_to_string_utc_asn1): Function
+ renamed from `pdf_time_to_string_asn1'.
+ (pdf_time_from_string_utc_asn1): Function renamed from
+ `pdf_time_from_string_asn1'.
+
+ * src/base/pdf-time-string.c: Implemented to/from routines for
+ UTC ASN1 and Generalized ASN1 date formats, as specified in the ASN1
+ reference.
+ (pdf_time_from_string_pdf): Function simplified.
+ (pdf_time_from_string_iso8601): Function simplified.
+
+ * doc/gnupdf.texi: Renamed enumeration `PDF_TIME_FORMAT_ASN1' to
+ `PDF_TIME_FORMAT_UTC_ASN1'.
+
+ * src/base/pdf-time.h: Idem.
+
2008-07-25 Jose E. Marchesi <address@hidden>
* src/base/pdf-list.h: Inline semantics in MacosX.
=== modified file 'doc/gnupdf.texi'
--- doc/gnupdf.texi 2008-07-18 07:41:04 +0000
+++ doc/gnupdf.texi 2008-07-25 17:25:07 +0000
@@ -4624,10 +4624,10 @@
PDF date strings.
@item PDF_TIME_FORMAT_ISO_8601
ISO 8601 date and time strings.
address@hidden PDF_TIME_FORMAT_ASN1
address@hidden PDF_TIME_FORMAT_UTC_ASN1
UTC ASN1 date.
@item PDF_TIME_FORMAT_GENERALIZED_ASN1
-ASN1 date.
+Generalized ASN1 date.
@end table
@end deftp
=== modified file 'src/base/pdf-time-string.c'
--- src/base/pdf-time-string.c 2008-07-13 19:50:22 +0000
+++ src/base/pdf-time-string.c 2008-07-25 17:23:24 +0000
@@ -31,46 +31,35 @@
#include <pdf-time-string.h>
+/* In order to use this macro, make sure that every byte represents a
digit */
+#define __GET_FIELD2(str,start,dest) \
+ (dest += ((str[start]-48)*10 + (str[start+1]-48)));
+
+
+/* Macro to check if given bytes are digits or not */
+#define __CHECK_MASK(mask, masklength, str, i) while(i<masklength) { \
+ if(mask & (1 << i)) { \
+ if((str[i] < '0') || \
+ (str[i] > '9')) { \
+ PDF_DEBUG_BASE("Expected digit and found '%c' in '%s'", str[i],
str); \
+ return PDF_EBADDATA; \
+ } \
+ } \
+ i++; \
+}
+
/* Maximum length of strings, including trailing NUL */
#define PDF_MAX_ISO8601_STR_LENGTH 30
+#define PDF_MAX_UTCASN1_STR_LENGTH 19
+#define PDF_MAX_GENASN1_STR_LENGTH 21
#define PDF_MAX_PDFDATE_STR_LENGTH 24
-
-/* Mask to check if a given expected character is actually a digit or
not */
-
-
-
-pdf_status_t
-pdf_time_from_string_pdf(pdf_time_t time_var,
- const pdf_char_t *time_str)
+static pdf_status_t
+pdf_time_check_string_pdf(const pdf_char_t *time_str,
+ const pdf_size_t time_str_length)
{
- /*
- * From PDF Reference 1.7: ( D:YYYYMMDDHHmmSSOHH'mm' )
- * From ISO 32000: ( D:YYYYMMDDHHmmSSOHH'mm )
- *
- * Notes: Year is mandatory, all the other fields may appear if the
preceding
- * also appear.
- *
- * D: = string "D:"
- * YYYY = four-digit year
- * MM = two-digit month (01=January, etc.)
- * DD = two-digit day of month (01 through 31)
- * HH = two digits of hour (00 through 23)
- * mm = two digits of minute (00 through 59)
- * SS = two digits of second (00 through 59)
- * O = either '+', '-' or 'Z'
- * HH = two digits of hour (00 through 23) for the GMT offset
- * ' = string "'"
- * MM = two digits of minute (00 through 59) for the GMT offset
- * ' = string "'" (NOTE: Mandatory in 1.7, optional in ISO32000)
- */
- struct pdf_time_cal_s calendar;
- pdf_char_t duplicate_field[5];
- pdf_status_t ret_code;
- pdf_i32_t gmt_offset = 0;
- pdf_i32_t i;
- pdf_size_t time_str_length = strlen((char *)time_str);
-
+ pdf_i32_t i;
+
/* Check minimum length D:YYYY */
if(time_str_length < 6)
{
@@ -82,35 +71,20 @@
if(strncmp((char *)time_str,"D:",2) != 0)
{
PDF_DEBUG_BASE("Invalid PDF time string (no prefix): '%s'",
- time_str);
+ time_str);
return PDF_EBADDATA;
}
-
+
/* We need to check the input characters are digits when we expect
digits and
- * the opposite as well. Remember that for unconvertable bytes, atoi
returns
- * zero, so we must really be sure we pass digits to atoi */
-
+ * the opposite as well. */
+
#define PDF_STRING_DIGIT_MASK 0x36FFFC /* 0011 0110 1111 1111 1111 1100 */
-
+
/* Don't really need to check again two first bytes, as already done
before */
i = 2;
- while(i < time_str_length)
- {
- if(PDF_STRING_DIGIT_MASK & (1 << i))
- {
- /* Expected a digit at position 'i' */
- if((time_str[i] < '0') || \
- (time_str[i] > '9'))
- {
- PDF_DEBUG_BASE("Expected digit and found '%c' in '%s'",
- time_str[i], time_str);
- return PDF_EBADDATA;
- }
- }
- /* Update index */
- i++;
- }
+ __CHECK_MASK(PDF_STRING_DIGIT_MASK, time_str_length, time_str, i);
+
/* Check time zone definer */
if((time_str_length >=16) && \
(time_str[16] != 'Z') && \
@@ -118,10 +92,10 @@
(time_str[16] != '-'))
{
PDF_DEBUG_BASE("Invalid time zone definer '%c' found in '%s'",
- time_str[16], time_str);
+ time_str[16], time_str);
return PDF_EBADDATA;
}
-
+
/* Check additional ' characters. Remember that the last ' character is
* mandatory in PDF Ref 1.7 but optional in ISO32000*/
if(((time_str_length >= 19) && \
@@ -130,75 +104,117 @@
(time_str[22] != '\'')))
{
PDF_DEBUG_BASE("Invalid separator found ('%c' or '%c') in '%s'",
- time_str[19], time_str[22], time_str);
+ time_str[19], time_str[22], time_str);
return PDF_EBADDATA;
}
-
+ return PDF_OK;
+}
+
+
+pdf_status_t
+pdf_time_from_string_pdf(pdf_time_t time_var,
+ const pdf_char_t *time_str)
+{
+ /*
+ * From PDF Reference 1.7: ( D:YYYYMMDDHHmmSSOHH'mm' )
+ * From ISO 32000: ( D:YYYYMMDDHHmmSSOHH'mm )
+ *
+ * Notes: Year is mandatory, all the other fields may appear if the
preceding
+ * also appear.
+ *
+ * D: = string "D:"
+ * YYYY = four-digit year
+ * MM = two-digit month (01=January, etc.)
+ * DD = two-digit day of month (01 through 31)
+ * HH = two digits of hour (00 through 23)
+ * mm = two digits of minute (00 through 59)
+ * SS = two digits of second (00 through 59)
+ * O = either '+', '-' or 'Z'
+ * HH = two digits of hour (00 through 23) for the GMT offset
+ * ' = string "'"
+ * MM = two digits of minute (00 through 59) for the GMT offset
+ * ' = string "'" (NOTE: Mandatory in 1.7, optional in ISO32000)
+ */
+ struct pdf_time_cal_s calendar;
+ pdf_status_t ret_code;
+ pdf_i32_t gmt_offset = 0;
+ pdf_size_t time_str_length = strlen((char *)time_str);
+
+ ret_code = pdf_time_check_string_pdf(time_str, time_str_length);
+ if(ret_code != PDF_OK)
+ {
+ PDF_DEBUG_BASE("Input Date in PDF format is not valid (%s)",
+ time_str);
+ return ret_code;
+ }
/* Reset calendar (all integers to zero) */
memset(&calendar, 0, sizeof(calendar));
-
-#define __GET_FIELD(str,start,length,dest) do { \
- memcpy(&duplicate_field[0], &str[start], length); \
- duplicate_field[length]=0; \
- dest += atoi((char *)&duplicate_field[0]); \
-} while(0)
-
-
- /* Get year */
- __GET_FIELD(time_str, 2, 4, calendar.year);
- /* Get month */
- if(time_str_length >= 8) /* D:YYYYMM */
+ while(1)
{
- __GET_FIELD(time_str, 6, 2, calendar.month);
+ /* Get century */
+ __GET_FIELD2(time_str, 2, calendar.year);
+ calendar.year *= 100;
+ /* Get year in century */
+ __GET_FIELD2(time_str, 4, calendar.year);
+ /* more than year ? */
+ if(time_str_length == 6)
+ break;
+
+ /* Get month */
+ __GET_FIELD2(time_str, 6, calendar.month);
+ /* more than month ? */
+ if(time_str_length == 8)
+ break;
+
/* Get day */
- if(time_str_length >= 10) /* D:YYYYMMDD */
- {
- __GET_FIELD(time_str, 8, 2, calendar.day);
- /* Get hour */
- if(time_str_length >= 12) /* D:YYYYMMDDHH */
- {
- __GET_FIELD(time_str, 10, 2, calendar.hour);
- /* Get minutes */
- if(time_str_length >= 14) /* D:YYYYMMDDHHmm */
- {
- __GET_FIELD(time_str, 12, 2, calendar.minute);
- /* Get seconds */
- if(time_str_length >= 16) /* D:YYYYMMDDHHmmSS */
- {
- __GET_FIELD(time_str, 14, 2, calendar.second);
- /* Get time zone offset hours */
- if(time_str_length >= 19) /* D:YYYYMMDDHHmmSS0HH */
- {
- __GET_FIELD(time_str, 17, 2,
calendar.gmt_offset);
- /* And convert it in minutes */
- calendar.gmt_offset *= 60;
-
- /* Get time zone offset minutes */
- if(time_str_length >= 22) /*
D:YYYYMMDDHHmmSS0HH'MM */
- {
- __GET_FIELD(time_str, 20, 2,
calendar.gmt_offset);
- /* And convert it in minutes */
- calendar.gmt_offset *= 60;
- }
-
- /* Convert from minutes to seconds */
- calendar.gmt_offset *= 60;
-
- /* Set proper sign */
- if(time_str[16]=='-')
- {
- calendar.gmt_offset *= (-1);
- }
- }
- }
- }
- }
- }
+ __GET_FIELD2(time_str, 8, calendar.day);
+ /* more than day ? */
+ if(time_str_length == 10)
+ break;
+
+ /* Get hour */
+ __GET_FIELD2(time_str, 10, calendar.hour);
+ /* more than hour ? */
+ if(time_str_length == 12)
+ break;
+
+ /* Get minute */
+ __GET_FIELD2(time_str, 12, calendar.minute);
+ /* more than minute ? */
+ if(time_str_length == 14)
+ break;
+
+ /* Get second */
+ __GET_FIELD2(time_str, 14, calendar.second);
+ /* more than second ? */
+ if(time_str_length <= 17) /* Considering timezone offset separator */
+ break;
+
+ /* Get timezone offset hours */
+ __GET_FIELD2(time_str, 17, calendar.gmt_offset);
+ /* And convert it in minutes */
+ calendar.gmt_offset *= 60;
+
+ /* Get timezone offset minutes */
+ if(time_str_length > 19)
+ {
+ __GET_FIELD2(time_str, 20, calendar.gmt_offset);
+ }
+
+ /* Convert from minutes to seconds */
+ calendar.gmt_offset *= 60;
+
+ /* Set proper sign */
+ if(time_str[16]=='-')
+ {
+ calendar.gmt_offset *= (-1);
+ }
+
+ /* Stop loop :-) */
+ break;
}
-
-#undef __GET_FIELD
/* Get time value from break-down UTC calendar !*/
ret_code = pdf_time_from_cal(time_var, &calendar);
@@ -211,20 +227,299 @@
return ret_code;
}
+
+static pdf_status_t
+pdf_time_check_string_utc_asn1(const pdf_char_t *time_str,
+ const pdf_size_t time_str_length)
+{
+ pdf_i32_t i;
+ pdf_i32_t base_mask;
+ pdf_i32_t mask_length;
+ pdf_bool_t with_gmt_offset;
+
+#define UTCASN1_STRING_DIGIT_MASK1 0x03FF /* 0000 0011 1111 1111 */
+#define UTCASN1_STRING_DIGIT_MASK2 0x0FFF /* 0000 1111 1111 1111 */
+
+ /* Check length */
+ if((time_str_length == 11) || \
+ (time_str_length == 15))
+ {
+ base_mask = UTCASN1_STRING_DIGIT_MASK1;
+ mask_length = 10;
+ }
+ else if((time_str_length == 13) || \
+ (time_str_length == 17))
+ {
+ base_mask = UTCASN1_STRING_DIGIT_MASK2;
+ mask_length = 12;
+ }
+ else
+ {
+ PDF_DEBUG_BASE("Invalid UTC-ASN1 time string (invalid length): '%s'",
+ time_str);
+ return PDF_EBADDATA;
+ }
+
+ /* Check if GMT offset is expected */
+ with_gmt_offset = (time_str_length >=15) ? PDF_TRUE : PDF_FALSE;
+
+ /* Check extra non-digit characters */
+ if((!with_gmt_offset) && \
+ (time_str[time_str_length-1] != 'Z'))
+ {
+ PDF_DEBUG_BASE("Expected UTC string, but not valid");
+ return PDF_EBADDATA;
+ }
+ else if((with_gmt_offset) && \
+ ((time_str[time_str_length-5] != '+') && \
+ (time_str[time_str_length-5] != '-')))
+ {
+ PDF_DEBUG_BASE("Expected non-UTC string, but not valid");
+ return PDF_EBADDATA;
+ }
+
+ /* Check mask if base string */
+ i=0;
+ __CHECK_MASK(base_mask, mask_length, time_str, i);
+ /* Check mask of offset string if available */
+ if(with_gmt_offset)
+ {
+ i=time_str_length-4;
+ __CHECK_MASK(0x000F, 4, time_str, i);
+ }
+
+ return PDF_OK;
+}
+
+static pdf_i32_t
+pdf_time_get_century_in_sliding_window(pdf_i32_t year_in_century)
+{
+ pdf_i32_t full_year = -1;
+ pdf_time_t current;
+ struct pdf_time_cal_s current_cal;
+ current = pdf_time_new();
+
+ if((current != NULL) && \
+ (pdf_time_set_to_current_utc_time(current) == PDF_OK) && \
+ (pdf_time_get_utc_cal(current, ¤t_cal) == PDF_OK))
+ {
+ /* Get century from full current year */
+ pdf_i32_t century = 100 * (current_cal.year / 100);
+ /* Appy current century */
+ full_year = century + year_in_century;
+
+ /* Check if the century must be changed */
+ if((full_year > current_cal.year) && \
+ ((full_year - current_cal.year) > 50))
+ {
+ full_year -= 100;
+ }
+ else if((full_year < current_cal.year) && \
+ ((current_cal.year - full_year) > 50))
+ {
+ full_year += 100;
+ }
+ }
+
+ pdf_time_destroy(current);
+ return full_year;
+}
+
pdf_status_t
-pdf_time_from_string_asn1(pdf_time_t time_var,
- const pdf_char_t *time_str)
+pdf_time_from_string_utc_asn1(pdf_time_t time_var,
+ const pdf_char_t *time_str)
{
- /* TODO */
- return PDF_ERROR;
+ /*
+ * yymmddhhmmZ
+ * yymmddhhmmssZ
+ * yymmddhhmm+hhmm
+ * yymmddhhmm-hhmm
+ * yymmddhhmmss+hhmm
+ * yymmddhhmmss-hhmm
+ *
+ * Note: As year is only stored in 2 digits, a sliding window of
[-50,+50]
+ * years will be used to get the century.
+ */
+ struct pdf_time_cal_s calendar;
+ pdf_size_t time_str_length = strlen((char *)time_str);
+
+ if(pdf_time_check_string_utc_asn1(time_str, time_str_length) != PDF_OK)
+ {
+ PDF_DEBUG_BASE("Input Date in UTC ASN1 format is not valid (%s)",
+ time_str);
+ return PDF_EBADDATA;
+ }
+
+ /* Reset calendar (all integers to zero) */
+ memset(&calendar, 0, sizeof(calendar));
+
+ while(1)
+ {
+ pdf_bool_t has_seconds = PDF_FALSE;
+ /* Get year in century */
+ __GET_FIELD2(time_str, 0, calendar.year);
+ /* Get 4-digit year from 2-digit year */
+ calendar.year =
pdf_time_get_century_in_sliding_window(calendar.year);
+
+ /* Get month */
+ __GET_FIELD2(time_str, 2, calendar.month);
+
+ /* Get day */
+ __GET_FIELD2(time_str, 4, calendar.day);
+
+ /* Get hour */
+ __GET_FIELD2(time_str, 6, calendar.hour);
+
+ /* Get minute */
+ __GET_FIELD2(time_str, 8, calendar.minute);
+
+ /* Get second if available */
+ if((time_str[10] >= '0') && \
+ (time_str[10] <= '9'))
+ {
+ has_seconds = PDF_TRUE;
+ __GET_FIELD2(time_str, 10, calendar.second);
+ }
+
+ /* Check if we have GMT offset */
+ if(time_str[time_str_length-1] == 'Z')
+ {
+ break;
+ }
+
+ /* Get timezone offset hours */
+ __GET_FIELD2(time_str, (has_seconds ? 13 : 11), calendar.gmt_offset);
+ /* And convert it in minutes */
+ calendar.gmt_offset *= 60;
+ /* Get timezone offset minutes */
+ __GET_FIELD2(time_str, (has_seconds ? 15 : 13),
calendar.gmt_offset);
+ /* Convert from minutes to seconds */
+ calendar.gmt_offset *= 60;
+
+ /* Set proper sign */
+ if(time_str[(has_seconds ? 12 : 10)] == '-')
+ {
+ calendar.gmt_offset *= (-1);
+ }
+
+ /* Stop loop :-) */
+ break;
+ }
+
+ /* Get time value from break-down UTC calendar !*/
+ return pdf_time_from_cal(time_var, &calendar);
}
pdf_status_t
pdf_time_from_string_generalized_asn1(pdf_time_t time_var,
const pdf_char_t *time_str)
{
- /* TODO */
- return PDF_ERROR;
+ /*
+ * Year:
+ * YYYY (eg 1997)
+ * Year and month:
+ * YYYYMM (eg 199707)
+ * Complete date:
+ * YYYYMMDD (eg 19970716)
+ * Complete date plus hours and minutes:
+ * YYYYMMDDhhmmTZD (eg 199707161920+01:00)
+ * Complete date plus hours, minutes and seconds:
+ * YYYYMMDDhhmmssTZD (eg 19970716192030+01:00)
+ * Complete date plus hours, minutes, seconds and a decimal fraction
of a
+ * second
+ * YYYYMMDDThhmmss.sTZD (eg 1997071619:20:30.45+01:00)
+ *
+ * where:
+ *
+ * YYYY = four-digit year
+ * MM = two-digit month (01=January, etc.)
+ * DD = two-digit day of month (01 through 31)
+ * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
+ * mm = two digits of minute (00 through 59)
+ * ss = two digits of second (00 through 59)
+ * s = one or more digits representing a decimal fraction of a second
+ * TZD = time zone designator (Z or +hh:mm or -hh:mm)
+ *
+ */
+ struct pdf_time_cal_s calendar;
+ pdf_size_t time_str_length = strlen((char *)time_str);
+
+ /* Check minimum length */
+ if(time_str_length < 4)
+ {
+ PDF_DEBUG_BASE("Invalid Generalized ASN1 time string (too short):
'%s'",
+ time_str);
+ return PDF_EBADDATA;
+ }
+
+ /* Reset calendar */
+ memset(&calendar, 0, sizeof(calendar));
+
+ while(1)
+ {
+ pdf_bool_t has_seconds = PDF_FALSE;
+ /* Get century */
+ __GET_FIELD2(time_str, 0, calendar.year);
+ calendar.year *= 100;
+ /* Get year in century */
+ __GET_FIELD2(time_str, 2, calendar.year);
+ /* more than year ? */
+ if(time_str_length == 4)
+ break;
+
+
+ /* Get month */
+ __GET_FIELD2(time_str, 4, calendar.month);
+ /* more than month ? */
+ if(time_str_length == 6)
+ break;
+
+ /* Get day */
+ __GET_FIELD2(time_str, 6, calendar.day);
+ /* more than day ? */
+ if(time_str_length == 8)
+ break;
+
+ /* Get hour and minutes */
+ __GET_FIELD2(time_str, 8, calendar.hour);
+ __GET_FIELD2(time_str, 10, calendar.minute);
+
+ /* Get second if available */
+ if((time_str[17] >= '0') && \
+ (time_str[17] <= '9'))
+ {
+ has_seconds = PDF_TRUE;
+ __GET_FIELD2(time_str, 12, calendar.second);
+ }
+
+ /* Note: Fractional part of seconds not considered */
+
+ if(time_str[time_str_length-1] == 'Z')
+ {
+ break;
+ }
+
+ /* Get timezone offset hours */
+ __GET_FIELD2(time_str, (time_str_length-4), calendar.gmt_offset);
+ /* And convert it in minutes */
+ calendar.gmt_offset *= 60;
+ /* Get timezone offset minutes */
+ __GET_FIELD2(time_str, (time_str_length-2), calendar.gmt_offset);
+ /* Convert from minutes to seconds */
+ calendar.gmt_offset *= 60;
+
+ /* Set proper sign */
+ if(time_str[(time_str_length-5)]=='-')
+ {
+ calendar.gmt_offset *= (-1);
+ }
+
+ /* Stop loop :-) */
+ break;
+ }
+
+ /* Get time value from break-down calendar !*/
+ return pdf_time_from_cal(time_var, &calendar);
}
pdf_status_t
@@ -259,10 +554,6 @@
*
*/
struct pdf_time_cal_s calendar;
- pdf_char_t *duplicate;
- pdf_char_t *walker;
- pdf_status_t ret_code;
- pdf_i32_t gmt_offset = 0;
pdf_size_t time_str_length = strlen((char *)time_str);
/* Check minimum length */
@@ -273,93 +564,74 @@
return PDF_EBADDATA;
}
- /* Initialize text walker */
- duplicate = (pdf_char_t *)pdf_alloc(time_str_length+1);
- if(duplicate == NULL)
- {
- PDF_DEBUG_BASE("Problem allocating memory");
- return PDF_ENOMEM;
- }
- memcpy(duplicate, time_str, time_str_length);
- walker = duplicate;
-
/* Reset calendar */
memset(&calendar, 0, sizeof(calendar));
-
- /* Get year */
- duplicate[4] = '\0';
- calendar.year = atoi((char *)duplicate);
-
- /* Get month */
- if(time_str_length >= 7)
+
+ while(1)
{
- duplicate[7] = '\0';
- calendar.month = atoi((char *)(&duplicate[5]));
+ pdf_bool_t has_seconds = PDF_FALSE;
+ /* Get century */
+ __GET_FIELD2(time_str, 0, calendar.year);
+ calendar.year *= 100;
+ /* Get year in century */
+ __GET_FIELD2(time_str, 2, calendar.year);
+ /* more than year ? */
+ if(time_str_length == 4)
+ break;
+
+
+ /* Get month */
+ __GET_FIELD2(time_str, 5, calendar.month);
+ /* more than month ? */
+ if(time_str_length == 7)
+ break;
/* Get day */
- if(time_str_length >= 10)
- {
- duplicate[10] = '\0';
- calendar.day = atoi((char *)(&duplicate[8]));
-
- /* Get hour and minutes */
- if(time_str_length >= 16+1) /* 1 is the minimum length for TZD */
- {
- char next_field = duplicate[16];
-
- /* Get hour */
- duplicate[13] = '\0';
- calendar.hour = atoi((char *)(&duplicate[11]));
- /* Get minutes */
- duplicate[16] = '\0';
- calendar.minute = atoi((char *)(&duplicate[14]));
-
- /* Get Time Zone information */
- if(duplicate[time_str_length-1] == 'Z')
- {
- /* Time is given in UTC... do nothing */
- duplicate[time_str_length-1] = '\0';
- }
- else
- {
- /* Need to parse time zone offset */
- pdf_i32_t hours_tz;
- pdf_i32_t minutes_tz;
- minutes_tz = atoi((char
*)(&duplicate[time_str_length-2]));
- duplicate[time_str_length-3] = '\0';
- hours_tz = atoi((char *)(&duplicate[time_str_length-5]));
-
- gmt_offset = 60*(minutes_tz + 60*hours_tz);
- if(duplicate[time_str_length-6] == '-')
- {
- gmt_offset *= (-1);
- }
- }
-
- /* Read seconds if available */
- if(next_field == ':')
- {
- /* Ok, seconds available. Decimal part of the seconds
will be
- * ignored if it's available */
- duplicate[19] = '\0';
- calendar.second = atoi((char *)(&duplicate[17]));
- }
- }
- }
- }
-
- /* Set calendar as if it were UTC */
- calendar.gmt_offset = 0;
-
- /* Get time value from break-down UTC calendar !*/
- ret_code = pdf_time_from_cal(time_var, &calendar);
- if(ret_code == PDF_OK)
- {
- /* Now set GMT offset in pdf_time_t */
- time_var->gmt_offset = gmt_offset;
- }
-
- return ret_code;
+ __GET_FIELD2(time_str, 8, calendar.day);
+ /* more than day ? */
+ if(time_str_length == 10)
+ break;
+
+ /* Get hour and minutes */
+ __GET_FIELD2(time_str, 11, calendar.hour);
+ __GET_FIELD2(time_str, 14, calendar.minute);
+
+ /* Get second if available */
+ if((time_str[17] >= '0') && \
+ (time_str[17] <= '9'))
+ {
+ has_seconds = PDF_TRUE;
+ __GET_FIELD2(time_str, 17, calendar.second);
+ }
+
+ /* Note: Fractional part of seconds not considered */
+
+ if(time_str[time_str_length-1] == 'Z')
+ {
+ break;
+ }
+
+ /* Get timezone offset hours */
+ __GET_FIELD2(time_str, (time_str_length-5), calendar.gmt_offset);
+ /* And convert it in minutes */
+ calendar.gmt_offset *= 60;
+ /* Get timezone offset minutes */
+ __GET_FIELD2(time_str, (time_str_length-2), calendar.gmt_offset);
+ /* Convert from minutes to seconds */
+ calendar.gmt_offset *= 60;
+
+ /* Set proper sign */
+ if(time_str[(time_str_length-6)]=='-')
+ {
+ calendar.gmt_offset *= (-1);
+ }
+
+ /* Stop loop :-) */
+ break;
+ }
+
+ /* Get time value from break-down calendar !*/
+ return pdf_time_from_cal(time_var, &calendar);
}
@@ -417,18 +689,123 @@
}
-/* Get Date as a string in ASN1 format */
+/* Get Date as a string in UTC-ASN1 format */
pdf_char_t *
-pdf_time_to_string_asn1(const pdf_time_t time_var)
+pdf_time_to_string_utc_asn1(const pdf_time_t time_var)
{
- return NULL;
+ pdf_char_t *str;
+ struct pdf_time_cal_s calendar;
+
+ str = (pdf_char_t
*)pdf_alloc(PDF_MAX_UTCASN1_STR_LENGTH*sizeof(pdf_char_t));
+ if(str != NULL)
+ {
+ if(pdf_time_get_local_cal(time_var, &calendar) == PDF_OK)
+ {
+ pdf_i32_t smallyear;
+ /* Convert 4-digit year to 2-digit year */
+ smallyear = calendar.year -1900;
+ while(smallyear > 99)
+ {
+ smallyear -= 100;
+ }
+
+ if(calendar.gmt_offset != 0)
+ {
+ pdf_i32_t offset_hours;
+ pdf_i32_t offset_minutes;
+
+ offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1))
* calendar.gmt_offset) / 3600;
+ offset_minutes = (((calendar.gmt_offset < 0) ? (-1) :
(1)) * calendar.gmt_offset) % 3600;
+ /* yymmddhhmmss+hhmm
+ * yymmddhhmmss-hhmm
+ */
+ sprintf((char *)str, "%s%d%s%d%s%d%s%d%s%d%s%d%c%s%d%s%d", \
+ (smallyear < 10 ? "0" : ""),
smallyear,
+ (calendar.month < 10 ? "0" : ""), calendar.month,
+ (calendar.day < 10 ? "0" : ""), calendar.day,
+ (calendar.hour < 10 ? "0" : ""), calendar.hour,
+ (calendar.minute < 10 ? "0" : ""), calendar.minute,
+ (calendar.second < 10 ? "0" : ""), calendar.second,
+ ((calendar.gmt_offset < 0) ? '-' : '+'),
+ (offset_hours < 10 ? "0" : ""), offset_hours,
+ (offset_minutes < 10 ? "0" : ""), offset_minutes);
+ }
+ else
+ {
+ /*
+ * yymmddhhmmssZ
+ */
+ sprintf((char *)str, "%s%d%s%d%s%d%s%d%s%d%s%dZ", \
+ (smallyear < 10 ? "0" : ""), smallyear,
+ (calendar.month < 10 ? "0" : ""), calendar.month,
+ (calendar.day < 10 ? "0" : ""), calendar.day,
+ (calendar.hour < 10 ? "0" : ""), calendar.hour,
+ (calendar.minute < 10 ? "0" : ""), calendar.minute,
+ (calendar.second < 10 ? "0" : ""), calendar.second);
+ }
+ }
+ else
+ {
+ PDF_DEBUG_BASE("Could not get local calendar from
pdf_time_t...");
+ pdf_dealloc(str);
+ str = NULL;
+ }
+ }
+
+ return str;
}
/* Get Date as a string in Generalized ASN1 format */
pdf_char_t *
pdf_time_to_string_generalized_asn1(const pdf_time_t time_var)
{
- return NULL;
+ pdf_char_t *str;
+ struct pdf_time_cal_s calendar;
+
+ str = (pdf_char_t
*)pdf_alloc(PDF_MAX_ISO8601_STR_LENGTH*sizeof(pdf_char_t));
+ if(str != NULL)
+ {
+ /* YYYYMMDDhhmmssTZD (eg 19970716192030+01:00) */
+ if(pdf_time_get_local_cal(time_var, &calendar) == PDF_OK)
+ {
+ if(calendar.gmt_offset != 0)
+ {
+ pdf_i32_t offset_hours;
+ pdf_i32_t offset_minutes;
+
+ offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1))
* calendar.gmt_offset) / 3600;
+ offset_minutes = (((calendar.gmt_offset < 0) ? (-1) :
(1)) * calendar.gmt_offset) % 3600;
+ sprintf((char *)str, "%4d%s%d%s%d%s%d%s%d%s%d%c%s%d%s%d", \
+ calendar.year,
+ (calendar.month < 10 ? "0" : ""), calendar.month,
+ (calendar.day < 10 ? "0" : ""), calendar.day,
+ (calendar.hour < 10 ? "0" : ""), calendar.hour,
+ (calendar.minute < 10 ? "0" : ""), calendar.minute,
+ (calendar.second < 10 ? "0" : ""), calendar.second,
+ ((calendar.gmt_offset < 0) ? '-' : '+'),
+ (offset_hours < 10 ? "0" : ""), offset_hours,
+ (offset_minutes < 10 ? "0" : ""), offset_minutes);
+ }
+ else
+ {
+ sprintf((char *)str, "%4d%s%d%s%d%s%d%s%d%s%dZ", \
+ calendar.year,
+ (calendar.month < 10 ? "0" : ""), calendar.month,
+ (calendar.day < 10 ? "0" : ""), calendar.day,
+ (calendar.hour < 10 ? "0" : ""), calendar.hour,
+ (calendar.minute < 10 ? "0" : ""), calendar.minute,
+ (calendar.second < 10 ? "0" : ""), calendar.second);
+ }
+ }
+ else
+ {
+ PDF_DEBUG_BASE("Could not get local calendar from
pdf_time_t...");
+ pdf_dealloc(str);
+ str = NULL;
+ }
+ }
+
+ return str;
}
/* Get Date as a string in ISO8601 format */
@@ -444,14 +821,14 @@
/* YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) */
if(pdf_time_get_local_cal(time_var, &calendar) == PDF_OK)
{
- if(calendar.gmt_offset != 0)
+ if(calendar.gmt_offset != 0)
{
pdf_i32_t offset_hours;
pdf_i32_t offset_minutes;
offset_hours = (((calendar.gmt_offset < 0) ? (-1) : (1))
* calendar.gmt_offset) / 3600;
offset_minutes = (((calendar.gmt_offset < 0) ? (-1) :
(1)) * calendar.gmt_offset) % 3600;
- sprintf((char *)str,
"%4d-%s%d-%s%dT%s%d:%s%d:%s%d.00%c%s%d:%s%d", \
+ sprintf((char *)str,
"%4d-%s%d-%s%dT%s%d:%s%d:%s%d%c%s%d:%s%d", \
calendar.year,
(calendar.month < 10 ? "0" : ""), calendar.month,
(calendar.day < 10 ? "0" : ""), calendar.day,
@@ -464,7 +841,7 @@
}
else
{
- sprintf((char *)str, "%4d-%s%d-%s%dT%s%d:%s%d:%s%d.00Z", \
+ sprintf((char *)str, "%4d-%s%d-%s%dT%s%d:%s%d:%s%dZ", \
calendar.year,
(calendar.month < 10 ? "0" : ""), calendar.month,
(calendar.day < 10 ? "0" : ""), calendar.day,
=== modified file 'src/base/pdf-time-string.h'
--- src/base/pdf-time-string.h 2008-06-25 01:22:56 +0000
+++ src/base/pdf-time-string.h 2008-07-25 17:23:24 +0000
@@ -38,8 +38,8 @@
/* Set time object contents based on Date in ASN1 format */
pdf_status_t
-pdf_time_from_string_asn1(pdf_time_t time_var,
- const pdf_char_t *time_str);
+pdf_time_from_string_utc_asn1(pdf_time_t time_var,
+ const pdf_char_t *time_str);
/* Set time object contents based on Date in Generalized ASN1 format */
pdf_status_t
@@ -58,9 +58,9 @@
pdf_time_to_string_pdf(const pdf_time_t time_var);
-/* Get Date as a string in ASN1 format */
+/* Get Date as a string in UTC-ASN1 format */
pdf_char_t *
-pdf_time_to_string_asn1(const pdf_time_t time_var);
+pdf_time_to_string_utc_asn1(const pdf_time_t time_var);
/* Get Date as a string in Generalized ASN1 format */
pdf_char_t *
=== modified file 'src/base/pdf-time.c'
--- src/base/pdf-time.c 2008-07-09 14:03:51 +0000
+++ src/base/pdf-time.c 2008-07-25 16:46:50 +0000
@@ -152,9 +152,9 @@
{
/* Based on glibc's __offtime function */
- pdf_i64_t days;
+ pdf_i32_t days;
pdf_i64_t aux64;
- pdf_i64_t remaining;
+ pdf_i32_t remaining;
pdf_i32_t years;
pdf_i32_t months;
pdf_time_t new_time_var;
@@ -173,62 +173,55 @@
/* Modify time in the time object */
delta = pdf_time_span_new();
pdf_time_span_set_from_i32(&delta, time_var->gmt_offset);
- pdf_time_add_span(time_var, delta);
+ pdf_time_add_span(new_time_var, delta);
pdf_time_span_destroy(&delta);
}
- days = pdf_i64_new(0,0);
aux64 = pdf_i64_new(0,0);
- remaining = pdf_i64_new(0,0);
/* Get date as days */
- pdf_i64_div_i32_divisor(&days, new_time_var->seconds,
PDF_SECS_PER_DAY, &p_status);
+ pdf_i64_div_i32_divisor(&aux64, new_time_var->seconds,
PDF_SECS_PER_DAY, &p_status);
+ days = pdf_i64_to_i32(aux64);
/* Get time in seconds */
- pdf_i64_mod_i32_divisor(&remaining, new_time_var->seconds,
PDF_SECS_PER_DAY, &p_status);
-
+ pdf_i64_mod_i32_divisor(&aux64, new_time_var->seconds,
PDF_SECS_PER_DAY, &p_status);
+ remaining = pdf_i64_to_i32(aux64);
/* Get hours */
- pdf_i64_div_i32_divisor(&aux64, remaining, PDF_SECS_PER_HOUR, &p_status);
- p_cal_time->hour = pdf_i64_to_i32(aux64);
-
+ p_cal_time->hour = remaining / PDF_SECS_PER_HOUR;
/* Get remaining */
- pdf_i64_mod_i32_divisor(&remaining, remaining, PDF_SECS_PER_HOUR,
&p_status);
-
+ remaining = remaining % PDF_SECS_PER_HOUR;
/* Get minutes */
- pdf_i64_div_i32_divisor(&aux64, remaining, PDF_MINS_PER_HOUR, &p_status);
- p_cal_time->minute = pdf_i64_to_i32(aux64);
+ p_cal_time->minute = remaining / PDF_MINS_PER_HOUR;
/* Get seconds */
- pdf_i64_mod_i32_divisor(&aux64, remaining, PDF_MINS_PER_HOUR, &p_status);
- p_cal_time->second = pdf_i64_to_i32(aux64);
-
+ p_cal_time->second = remaining % PDF_MINS_PER_HOUR;
/* Seems that Unix origin time was thursday */
- pdf_i64_add_i32(&aux64, days, 4, &p_status);
- pdf_i64_mod_i32_divisor(&aux64, aux64, 7, &p_status);
- p_cal_time->dow = pdf_i64_to_i32(aux64);
+ p_cal_time->dow = ((days+4)%7);
-
years = 1970;
- /* while (days < 0 || days >= (__isleap (y) ? 366 : 365)) */
- while((pdf_i64_cmp_i32(days, 0) < 0) || \
- (pdf_i64_cmp_i32(days, \
- (pdf_time_is_leap_year_p(years) ? \
- PDF_DAYS_IN_YEAR+1 : \
- PDF_DAYS_IN_YEAR)) >= 0))
+
+
+ while((days < 0) || \
+ (days >= (pdf_time_is_leap_year_p(years) ? \
+ (PDF_DAYS_IN_YEAR+1) : \
+ (PDF_DAYS_IN_YEAR))))
{
pdf_i32_t yg;
yg = years;
- pdf_i64_div_i32_divisor(&aux64, days, PDF_DAYS_IN_YEAR, &p_status);
- yg += pdf_i64_to_i32(aux64);
- pdf_i64_mod_i32_divisor(&aux64, days, PDF_DAYS_IN_YEAR, &p_status);
- yg -= (pdf_i64_cmp_i32(aux64, 0) < 0);
+ /* Compute number of years (assuming all years of 365 days)
between the
+ * origin and our date */
+ yg += (days / PDF_DAYS_IN_YEAR);
+ /* Get number of remaining days after having added the fixed-size
years
+ /* If the number of remaining days is less than zero, go down 1
year */
+ yg -= ((days % PDF_DAYS_IN_YEAR) < 0);
+
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
-
- pdf_i64_subtraction_i32_sub(&days, days, ((yg -
years)*PDF_DAYS_IN_YEAR + \
- LEAPS_THRU_END_OF (yg -
1) - \
- LEAPS_THRU_END_OF
(years - 1) ), &p_status);
+ /* Remove number of days due to the leap years */
+ days -= (((yg - years)*PDF_DAYS_IN_YEAR) + \
+ (LEAPS_THRU_END_OF (yg - 1)) - \
+ (LEAPS_THRU_END_OF (years - 1)));
years = yg;
}
@@ -236,17 +229,15 @@
p_cal_time->year = years;// - 1900;
for (months = 11; \
- pdf_i64_to_i32(days) <
pdf_time_get_days_before_month(p_cal_time->year,months); \
+ days < pdf_time_get_days_before_month(p_cal_time->year,months); \
--months)
continue;
- pdf_i64_subtraction_i32_sub(&days, \
- days, \
-
pdf_time_get_days_before_month(p_cal_time->year,months), &p_status);
+ days -= pdf_time_get_days_before_month(p_cal_time->year,months);
/* Set month and day of month */
p_cal_time->month = months;
- p_cal_time->day = pdf_i64_to_i32(days) + 1;
+ p_cal_time->day = days + 1;
/* Finally, set gmt offset */
p_cal_time->gmt_offset = new_time_var->gmt_offset;
@@ -846,8 +837,8 @@
return pdf_time_to_string_pdf(time_var);
case PDF_TIME_FORMAT_ISO_8601:
return pdf_time_to_string_iso8601(time_var);
- case PDF_TIME_FORMAT_ASN1:
- return pdf_time_to_string_asn1(time_var);
+ case PDF_TIME_FORMAT_UTC_ASN1:
+ return pdf_time_to_string_utc_asn1(time_var);
case PDF_TIME_FORMAT_GENERALIZED_ASN1:
return pdf_time_to_string_generalized_asn1(time_var);
default:
@@ -870,8 +861,8 @@
return pdf_time_from_string_pdf(time_var, time_str);
case PDF_TIME_FORMAT_ISO_8601:
return pdf_time_from_string_iso8601(time_var, time_str);
- case PDF_TIME_FORMAT_ASN1:
- return pdf_time_from_string_asn1(time_var, time_str);
+ case PDF_TIME_FORMAT_UTC_ASN1:
+ return pdf_time_from_string_utc_asn1(time_var, time_str);
case PDF_TIME_FORMAT_GENERALIZED_ASN1:
return pdf_time_from_string_generalized_asn1(time_var, time_str);
default:
=== modified file 'src/base/pdf-time.h'
--- src/base/pdf-time.h 2008-07-02 00:41:19 +0000
+++ src/base/pdf-time.h 2008-07-16 20:08:02 +0000
@@ -90,7 +90,7 @@
enum pdf_time_format_e {
PDF_TIME_FORMAT_PDF,
PDF_TIME_FORMAT_ISO_8601,
- PDF_TIME_FORMAT_ASN1,
+ PDF_TIME_FORMAT_UTC_ASN1,
PDF_TIME_FORMAT_GENERALIZED_ASN1
};
# Begin bundle
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
7/+LuSKcKEgyz0hpgA==
- [pdf-devel] Time module,
Aleksander Morgado <=