pdf-devel
[Top][All Lists]
Advanced

[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, &current_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==






reply via email to

[Prev in Thread] Current Thread [Next in Thread]