commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. release-2.2-497-g9371735


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-497-g9371735
Date: Mon, 05 Dec 2011 18:31:04 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=937173596bd77d773c3fc8330a1ebbe2c8c13df6

The branch, master has been updated
       via  937173596bd77d773c3fc8330a1ebbe2c8c13df6 (commit)
       via  025c888ab07458c7b2a1e73d6b607cc9ce26cca6 (commit)
      from  217ae7a2228c6f7054b4e8e1731ec5e17f0a86a4 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 937173596bd77d773c3fc8330a1ebbe2c8c13df6
Author: Sergey Poznyakoff <address@hidden>
Date:   Mon Dec 5 20:09:36 2011 +0200

    Implement mu_scan_datetime.
    
    * include/mailutils/stream.h (mu_fixed_memory_stream_create): New proto.
    * libmailutils/stream/memory_stream.c
    (mu_fixed_memory_stream_create): New function.
    
    * include/mailutils/util.h (mu_parse_imap_date_time)
    (mu_parse_ctime_date_time): Remove.
    (mu_scan_datetime): New proto.
    (mu_strftime): Remove const from the last arg.
    (MU_DATETIME_FROM,MU_DATETIME_IMAP)
    (MU_DATETIME_IMAP_SEARCH,MU_DATETIME_INTERNALDATE): New defines.
    * libmailutils/base/strftime.c: New file.
    * libmailutils/base/Makefile.am (libbase_la_SOURCES): Add strftime.c.
    * libmailutils/base/date.c (mu_scan_datetime): New function.
    * libmailutils/base/mutil.c (mu_strftime): Remove.
    
    * libmailutils/tests/scantime.at: New file.
    * libmailutils/tests/scantime.c: New file.
    * libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add scantime.
    (TESTSUITE_AT): Add scantime.at.
    * libmailutils/tests/strftime.c (main): Call mu_set_program_name.
    * libmailutils/tests/testsuite.at: Include scantime.at
    
    * libmu_sieve/actions.c (mime_create_reason): Use mu_c_streamftime.
    
    * imap4d/fetch.c (_frt_internaldate): Use mu_scan_datetime.
    * imap4d/util.c (util_parse_internal_date): Likewise.
    * libmu_scm/mu_message.c (mu-message-get-envelope-date): Likewise.
    * libproto/imap/fetch.c (_date_mapper): Likewise.
    * mail/from.c (hdr_date): Use mu_scan_datetime.

commit 025c888ab07458c7b2a1e73d6b607cc9ce26cca6
Author: Sergey Poznyakoff <address@hidden>
Date:   Sat Dec 3 04:44:34 2011 +0200

    Implement locale-independent strftime function.
    
    * include/mailutils/util.h (mu_c_streamftime): New prototype.
    * libmailutils/base/date.c (mu_c_streamftime): New function.
    * libmailutils/tests/.gitignore: Update.
    * libmailutils/tests/strftime.at: New test script.
    * libmailutils/tests/strftime.c: New test program.
    * libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add strftime.
    (TESTSUITE_AT): Add strftime.at.
    * libmailutils/tests/testsuite.at: Include strftime.at.

-----------------------------------------------------------------------

Summary of changes:
 imap4d/fetch.c                                     |   17 +-
 imap4d/util.c                                      |    5 +-
 include/mailutils/stream.h                         |    2 +
 include/mailutils/util.h                           |   19 +-
 libmailutils/base/Makefile.am                      |    1 +
 libmailutils/base/date.c                           |  856 ++++++++--
 libmailutils/base/mutil.c                          |   12 -
 libmu_dbm/create.c => libmailutils/base/strftime.c |   36 +-
 libmailutils/stream/memory_stream.c                |   57 +
 libmailutils/tests/.gitignore                      |    1 +
 libmailutils/tests/Makefile.am                     |    4 +
 libmailutils/tests/scantime.at                     |   64 +
 libmailutils/tests/{imapio.c => scantime.c}        |   82 +-
 libmailutils/tests/strftime.at                     | 1921 ++++++++++++++++++++
 libmailutils/tests/{imapio.c => strftime.c}        |   97 +-
 libmailutils/tests/testsuite.at                    |    7 +-
 libmu_scm/mu_message.c                             |    4 +-
 libmu_sieve/actions.c                              |   12 +-
 libproto/imap/fetch.c                              |   13 +-
 mail/from.c                                        |    2 +-
 20 files changed, 2951 insertions(+), 261 deletions(-)
 copy libmu_dbm/create.c => libmailutils/base/strftime.c (60%)
 create mode 100644 libmailutils/tests/scantime.at
 copy libmailutils/tests/{imapio.c => scantime.c} (51%)
 create mode 100644 libmailutils/tests/strftime.at
 copy libmailutils/tests/{imapio.c => strftime.c} (50%)

diff --git a/imap4d/fetch.c b/imap4d/fetch.c
index bf6ad9e..cd7baa6 100644
--- a/imap4d/fetch.c
+++ b/imap4d/fetch.c
@@ -960,16 +960,12 @@ _frt_internaldate (struct fetch_function_closure *ffc,
   mu_envelope_t env = NULL;
   struct tm tm, *tmp = NULL;
   mu_timezone tz;
-  char datebuf[sizeof ("13-Jul-2002 00:00:00")];
-  int tzoff;
-  int tzh = 0, tzm = 0;
   
   mu_message_get_envelope (frt->msg, &env);
   if (mu_envelope_sget_date (env, &date) == 0
-      && mu_parse_ctime_date_time (&date, &tm, &tz) == 0)
+      && mu_scan_datetime (date, MU_DATETIME_FROM, &tm, &tz, NULL) == 0)
     {
       tmp = &tm;
-      tzoff = tz.utc_offset;
     }
   else
     {
@@ -977,18 +973,13 @@ _frt_internaldate (struct fetch_function_closure *ffc,
       struct timeval stv;
       struct timezone stz;
       
-      gettimeofday(&stv, &stz);
+      gettimeofday (&stv, &stz);
       t = stv.tv_sec;
-      tzoff = - stz.tz_minuteswest;
+      tz.utc_offset = - stz.tz_minuteswest;
       tmp = localtime (&t);
     }
-  tzh = tzoff / 3600;
-  if (tzoff < 0)
-    tzoff = - tzoff;
-  tzm = tzoff % 3600;
-  mu_strftime (datebuf, sizeof (datebuf), "%d-%b-%Y %H:%M:%S", tmp);
   io_sendf ("%s", ffc->name);
-  io_sendf (" \"%s %+03d%02d\"", datebuf, tzh, tzm);
+  mu_c_streamftime (iostream, " \"%d-%b-%Y %H:%M:%S %z\"", tmp, &tz);
   return 0;
 }
 
diff --git a/imap4d/util.c b/imap4d/util.c
index 4e960b2..24b0aec 100644
--- a/imap4d/util.c
+++ b/imap4d/util.c
@@ -373,9 +373,8 @@ util_parse_internal_date (char *date, time_t *timep,
   struct tm tm;
   mu_timezone tz;
   time_t time;
-  char **datep = &date;
 
-  if (mu_parse_imap_date_time ((const char **) datep, &tm, &tz))
+  if (mu_scan_datetime (date, MU_DATETIME_IMAP_SEARCH, &tm, &tz, NULL))
     return 1;
 
   adjust_tm (&tm, &tz, flag);
@@ -412,7 +411,7 @@ util_parse_ctime_date (const char *date, time_t *timep,
   struct tm tm;
   mu_timezone tz;
 
-  if (mu_parse_ctime_date_time (&date, &tm, &tz) == 0)
+  if (mu_scan_datetime (date, MU_DATETIME_FROM, &tm, &tz, NULL) == 0)
     {
       adjust_tm (&tm, &tz, flag);
       *timep = mu_tm2time (&tm, &tz);
diff --git a/include/mailutils/stream.h b/include/mailutils/stream.h
index 8bea907..dbd6654 100644
--- a/include/mailutils/stream.h
+++ b/include/mailutils/stream.h
@@ -305,6 +305,8 @@ int mu_stdio_stream_create (mu_stream_t *pstream, int fd, 
int flags);
 int mu_memory_stream_create (mu_stream_t *pstream, int flags);
 int mu_static_memory_stream_create (mu_stream_t *pstream, const void *mem,
                                    size_t size);
+int mu_fixed_memory_stream_create (mu_stream_t *pstream, void *mem,
+                                  size_t size, int flags);
 
 int mu_mapfile_stream_create (mu_stream_t *pstream, const char *filename,
                              int flags);
diff --git a/include/mailutils/util.h b/include/mailutils/util.h
index d0ffd63..88e561a 100644
--- a/include/mailutils/util.h
+++ b/include/mailutils/util.h
@@ -61,15 +61,22 @@ struct mu_timezone
 typedef struct mu_timezone mu_timezone;
 
 int mu_parse_date (const char *p, time_t *rettime, const time_t *now);
-int mu_parse_imap_date_time (const char **p, struct tm *tm,
-                            mu_timezone *tz);
-int mu_parse_ctime_date_time (const char **p, struct tm *tm,
-                             mu_timezone *tz);
 
 time_t mu_utc_offset (void);
 time_t mu_tm2time (struct tm *timeptr, mu_timezone *tz);
-size_t mu_strftime (char *s, size_t max, const char *format,
-                   const struct tm *tm);
+size_t mu_strftime (char *s, size_t max, const char *format, struct tm *tm);
+
+int mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *tm,
+                     struct mu_timezone *tz);
+int mu_scan_datetime (const char *input, const char *fmt, struct tm *tm,
+                     struct mu_timezone *tz, char **endp);
+
+  /* Common datetime formats: */
+#define MU_DATETIME_FROM         "%a %b %e %H:%M:%S %Y"
+#define MU_DATETIME_IMAP         "%d-%b-%Y %H:%M:%S %z"
+#define MU_DATETIME_IMAP_SEARCH  "%d-%b-%Y%? %H:%M:%S %z"
+#define MU_DATETIME_INTERNALDATE "%a, %e %b %Y %H:%M:%S %z"
+
   
   /* ----------------------- */
   /* File & path names.      */
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am
index cb6c521..a2e5ac5 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -61,6 +61,7 @@ libbase_la_SOURCES = \
  sha1.c\
  secret.c\
  spawnvp.c\
+ strftime.c\
  symlink.c\
  tempfile.c\
  ticket.c\
diff --git a/libmailutils/base/date.c b/libmailutils/base/date.c
index e1e03fb..f784bea 100644
--- a/libmailutils/base/date.c
+++ b/libmailutils/base/date.c
@@ -23,16 +23,25 @@
 #include <stdlib.h>
 #include <string.h>
 #include <mailutils/util.h>
+#include <mailutils/stream.h>
+#include <mailutils/errno.h>
 #include <mailutils/cstr.h>
+#include <mailutils/cctype.h>
 
 #define SECS_PER_DAY 86400
 #define ADJUSTMENT -719162L
 
-static time_t
+/* Julian day is the number of days since Jan 1, 4713 BC (ouch!).
+   Eg. Jan 1, 1900 is: */
+#define  JULIAN_1900    1721425L
+
+/* Computes the number of days elapsed since January, 1 1900 to the
+   January, 1 of the given year */
+static unsigned
 jan1st (int year)
 {
   year--;               /* Do not consider the current year */
-  return  year*365L
+  return  year * 365L
                + year/4L    /* Years divisible by 4 are leap years */
                + year/400L  /* Years divisible by 400 are always leap years */
                - year/100L; /* Years divisible by 100 but not 400 aren't */
@@ -48,7 +57,7 @@ static int  month_start[]=
 #define leap_year(y) ((y) % 4 == 0 && (y) % 100 != 0 || (y) % 400 == 0)
 
 static int
-dayofyear (time_t *pday, int year, int month, int day)
+dayofyear (int year, int month, int day)
 {
   int  leap, month_days;
 
@@ -66,10 +75,50 @@ dayofyear (time_t *pday, int year, int month, int day)
   if (month <= 2)
     leap = 0;
 
-  *pday = month_start[month] + day + leap;
+  return month_start[month] + day + leap;
+}
+
+/* Returns number of days in a given year */
+static int
+year_days (int year)
+{
+  return dayofyear (year, 11, 31);
+}
+
+static int
+julianday (unsigned *pd, int year, int month, int day)
+{
+  int total = dayofyear (year, month, day);
+  if (total == -1)
+    return -1;
+  *pd = JULIAN_1900 + total + jan1st (year);
   return 0;
 }
 
+static int
+dayofweek (int year, int month, int day)
+{
+  unsigned jd;
+
+  if (julianday (&jd, year, month, day))
+    return -1;
+
+  /* January 1, 1900 was Monday, hence +1 */
+  return (jd + 1) % 7;
+}
+
+#define ISO_8601_START_WDAY 1 /* Monday */
+#define ISO_8601_MAX_WDAY   4 /* Thursday */
+#define MAXDAYS 366 /* Max. number of days in a year */
+#define ISO_8601_OFF ((MAXDAYS / 7 + 2) * 7)
+
+int
+ISO_8601_weekdays (int yday, int wday)
+{
+  return (yday
+         - (yday - wday + ISO_8601_MAX_WDAY + ISO_8601_OFF) % 7
+         + ISO_8601_MAX_WDAY - ISO_8601_START_WDAY);
+}
 
 /* Convert struct tm into time_t, taking into account timezone offset. */
 /* FIXME: It does not take DST into account */
@@ -77,12 +126,14 @@ time_t
 mu_tm2time (struct tm *tm, mu_timezone *tz)
 {
   time_t t;
+  int day;
   
-  if (dayofyear (&t, tm->tm_year, tm->tm_mon, tm->tm_mday - 1))
+  day = dayofyear (tm->tm_year, tm->tm_mon, tm->tm_mday - 1);
+  if (day == -1)
     return -1;
-  t = (t + ADJUSTMENT + jan1st (1900 + tm->tm_year)) * SECS_PER_DAY
+  t = (day + ADJUSTMENT + jan1st (1900 + tm->tm_year)) * SECS_PER_DAY
             + (tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec
-      - tz->utc_offset;
+       - (tz ? tz->utc_offset : 0);
   return t;
 }
 
@@ -97,151 +148,730 @@ mu_utc_offset (void)
   return - mktime (tm);
 }
 
-static const char *months[] =
+static const char *short_month[] =
 {
   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
+  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 };
 
-static const char *wdays[] =
+static const char *full_month[] =
 {
-  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
+  "January", "February", "March", "April",
+  "May", "June", "July", "August",
+  "September", "October", "November", "December"
 };
 
-int
-mu_parse_imap_date_time (const char **p, struct tm *tm, mu_timezone *tz)
-{
-  int year, mon, day, hour, min, sec;
-  char zone[6] = "+0000";      /* ( "+" / "-" ) hhmm */
-  char month[5] = "";
-  int hh = 0;
-  int mm = 0;
-  int sign = 1;
-  int scanned = 0, scanned3;
-  int i;
-  int tzoffset;
-
-  day = mon = year = hour = min = sec = 0;
+static const char *short_wday[] =
+{
+  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
 
-  memset (tm, 0, sizeof (*tm));
+static const char *full_wday[] =
+{
+  "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+  "Friday", "Saturday"
+};
 
-  switch (sscanf (*p,
-                 "%2d-%3s-%4d%n %2d:%2d:%2d %5s%n",
-                 &day, month, &year, &scanned3, &hour, &min, &sec, zone,
-                 &scanned))
+int
+mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *input_tm,
+                 struct mu_timezone *tz)
+{
+  int rc = 0;
+  struct tm tm;
+
+  /* Copy input TM because it might have been received from gmtime and
+     the fmt might result in further calls to gmtime which will clobber
+     it. */
+  tm = *input_tm;
+  while (*fmt && rc == 0)
     {
-    case 3:
-      scanned = scanned3;
-      break;
-    case 7:
-      break;
-    default:
-      return -1;
-    }
+      size_t len = strcspn (fmt, "%");
+      if (len)
+       {
+         rc = mu_stream_write (str, fmt, len, NULL);
+         if (rc)
+           break;
+       }
 
-  tm->tm_sec = sec;
-  tm->tm_min = min;
-  tm->tm_hour = hour;
-  tm->tm_mday = day;
+      fmt += len;
 
-  for (i = 0; i < 12; i++)
-    {
-      if (mu_c_strncasecmp (month, months[i], 3) == 0)
+    restart:
+      if (!*fmt || !*++fmt)
+       break;
+
+      switch (*fmt)
        {
-         mon = i;
+       case 'a':
+         /* The abbreviated weekday name. */
+         if (tm.tm_wday < 0 || tm.tm_wday > 6)
+           rc = ERANGE;
+         else
+           rc = mu_stream_write (str, short_wday[tm.tm_wday],
+                                 strlen (short_wday[tm.tm_wday]), NULL);
+         break;
+         
+       case 'A':
+         /* The full weekday name. */
+         if (tm.tm_wday < 0 || tm.tm_wday > 6)
+           rc = ERANGE;
+         else
+           rc = mu_stream_write (str, full_wday[tm.tm_wday],
+                                 strlen (full_wday[tm.tm_wday]), NULL);
+         break;
+         
+       case 'b':
+       case 'h':
+         /* The abbreviated month name. */
+         if (tm.tm_mon < 0 || tm.tm_mon > 11)
+           rc = ERANGE;
+         else
+           rc = mu_stream_write (str, short_month[tm.tm_mon],
+                                 strlen (short_month[tm.tm_mon]), NULL);
+         break;
+         
+       case 'B':
+         /* The full month name. */
+         if (tm.tm_mon < 0 || tm.tm_mon > 11)
+           rc = ERANGE;
+         else
+           rc = mu_stream_write (str, full_month[tm.tm_mon],
+                                 strlen (full_month[tm.tm_mon]), NULL);
+         break;
+         
+       case 'c':
+         /* The preferred date and time representation. */
+         rc = mu_c_streamftime (str, "%a %b %e %H:%M:%S %Y", &tm, tz);
+         break;
+           
+       case 'C':
+         /* The century number (year/100) as a 2-digit integer. */
+         rc = mu_stream_printf (str, "%02d", (tm.tm_year + 1900) / 100);
+         break;
+         
+       case 'd':
+         /* The day of the month as a decimal number (range 01 to 31). */
+         if (tm.tm_mday < 1 || tm.tm_mday > 31)
+           rc = ERANGE;
+         else
+           rc = mu_stream_printf (str, "%02d", tm.tm_mday);
+         break;
+         
+       case 'D':
+         /* Equivalent to %m/%d/%y. */
+         rc = mu_c_streamftime (str, "%m/%d/%y", &tm, tz);
+         break;
+         
+       case 'e':
+         /* Like %d, the day of the month as a decimal number, but a leading
+            zero is replaced by a space. */
+         if (tm.tm_mday < 1 || tm.tm_mday > 31)
+           rc = ERANGE;
+         else
+           rc = mu_stream_printf (str, "%2d", tm.tm_mday);
+         break;
+         
+       case 'E':
+         /* Modifier. The Single Unix Specification mentions %Ec, %EC, %Ex,
+            %EX, %Ey, and %EY, which are supposed to use a corresponding
+            locale-dependent alternative representation.
+            A no-op in POSIX locale */
+         goto restart;
+         
+       case 'F':
+         /* Equivalent to %Y-%m-%d (the ISO 8601 date format). */
+         rc = mu_c_streamftime (str, "%Y-%m-%d", &tm, tz);
+         break;
+                 
+       case 'V':
+         /* The ISO 8601:1988 week number of the current year as a decimal
+            number range 01 to 53, where week 1 is the first week that has
+            at least 4 days in the current year, and with Monday as the
+            first day of the week.
+         */
+       case 'G':
+         /* The ISO 8601 year with century as a decimal number.  The 4-digit
+            year corresponding to the ISO week number (see  %V).  This has
+            the same format and value as %y, except that if the ISO week
+            number belongs to the previous or next year, that year is used
+            instead.
+         */
+       case 'g':
+         /* Like  %G, but without century, that is, with a 2-digit year
+            (00-99). */
+         {
+           int year = tm.tm_year + 1900;
+           int days = ISO_8601_weekdays (tm.tm_yday, tm.tm_wday);
+
+           if (days < 0)
+             {
+               days = ISO_8601_weekdays (tm.tm_yday + year_days (year - 1),
+                                         tm.tm_wday);
+               year--;
+             }
+           else
+             {
+               int d = ISO_8601_weekdays (tm.tm_yday - year_days (year),
+                                          tm.tm_wday);
+               if (d >= 0)
+                 {
+                   year++;
+                   days = d;
+                 }
+             }
+
+           switch (*fmt)
+             {
+             case 'V':
+               rc = mu_stream_printf (str, "%02d", days / 7 + 1);
+               break;
+
+             case 'G':
+               rc = mu_stream_printf (str, "%4d", year);
+               break;
+
+             case 'g':
+               rc = mu_stream_printf (str, "%02d", year % 100);
+             }
+         }
+         break;
+         
+       case 'H':
+         /* The hour as a decimal number using a 24-hour clock (range 00 to
+            23). */
+         rc = mu_stream_printf (str, "%02d", tm.tm_hour);
+         break;
+         
+       case 'I':
+         /* The hour as a decimal number using a 12-hour clock (range 01 to
+            12). */
+         {
+           unsigned n = tm.tm_hour % 12;
+           rc = mu_stream_printf (str, "%02d", n == 0 ? 12 : n);
+         }
          break;
-       }
-    }
-  tm->tm_mon = mon;
-  tm->tm_year = (year > 1900) ? year - 1900 : year;
-  tm->tm_yday = 0;             /* unknown. */
-  tm->tm_wday = 0;             /* unknown. */
-#if HAVE_STRUCT_TM_TM_ISDST
-  tm->tm_isdst = -1;           /* unknown. */
-#endif
 
-  hh = (zone[1] - '0') * 10 + (zone[2] - '0');
-  mm = (zone[3] - '0') * 10 + (zone[4] - '0');
-  sign = (zone[0] == '-') ? -1 : +1;
-  tzoffset = sign * (hh * 60 * 60 + mm * 60);
+       case 'j':
+         /* The day of the year as a decimal number (range 001 to 366). */
+         rc = mu_stream_printf (str, "%03d", tm.tm_yday + 1);
+         break;
+         
+       case 'k':
+         /* The hour (24-hour clock) as a decimal number (range 0 to 23);
+            single digits are preceded by a blank. */
+         rc = mu_stream_printf (str, "%2d", tm.tm_hour);
+         break;
+         
+       case 'l':
+         /* The hour (12-hour clock) as a decimal number (range 1 to 12);
+            single digits are preceded by a blank. */
+         {
+           unsigned n = tm.tm_hour % 12;
+           rc = mu_stream_printf (str, "%2d", n == 0 ? 12 : n);
+         }
+         break;
+         
+       case 'm':
+         /* The month as a decimal number (range 01 to 12). */
+         rc = mu_stream_printf (str, "%02d", tm.tm_mon + 1);
+         break;
+                                  
+       case 'M':
+         /* The minute as a decimal number (range 00 to 59). */
+         rc = mu_stream_printf (str, "%02d", tm.tm_min);
+         break;
+         
+       case 'n':
+         /* A newline character. */
+         rc = mu_stream_write (str, "\n", 1, NULL);
+         break;
+         
+       case 'O':
+         /* Modifier.  The Single Unix Specification mentions %Od, %Oe, %OH,
+            %OI, %Om, %OM, %OS, %Ou, %OU, %OV, %Ow, %OW, and %Oy, which are
+            supposed to use alternative numeric symbols.
+
+            Hardly of any use for our purposes, hence a no-op. */
+         goto restart;
+         
+       case 'p':
+         /* Either "AM" or "PM" according to the given time value.
+            Noon is treated as "PM" and midnight as "AM". */
+         rc = mu_stream_write (str,
+                               tm.tm_hour < 12 ? "AM" : "PM",
+                               2, NULL);
+         break;
+                               
+       case 'P':
+         /* Like %p but in lowercase: "am" or "pm". */
+         rc = mu_stream_write (str,
+                               tm.tm_hour < 12 ? "am" : "pm",
+                               2, NULL);
+         break;
+         
+       case 'r':
+         /* The time in a.m. or p.m. notation, i.e. %I:%M:%S %p. */
+         rc = mu_c_streamftime (str, "%I:%M:%S %p", &tm, tz);
+         break;
+         
+       case 'R':
+         /* The time in 24-hour notation (%H:%M) */
+         rc = mu_c_streamftime (str, "%H:%M", &tm, tz);
+         break;
+         
+       case 's':
+         /* The number of seconds since the Epoch */
+         rc = mu_stream_printf (str, "%lu",
+                                (unsigned long) mu_tm2time (&tm, tz));
+         break;
+         
+       case 'S':
+         /* The second as a decimal number (range 00 to 60) */
+         rc = mu_stream_printf (str, "%02d", tm.tm_sec);
+         break;
+         
+       case 't':
+         /* A tab character. */
+         rc = mu_stream_write (str, "\t", 1, NULL);
+         break;
+         
+       case 'T':
+         /* The time in 24-hour notation (%H:%M:%S) */
+         rc = mu_c_streamftime (str, "%H:%M:%S", &tm, tz);
+         break;
+         
+       case 'u':
+         /* The day of the week as a decimal, range 1 to 7, Monday being 1.
+          */
+         rc = mu_stream_printf (str, "%1d",
+                                tm.tm_wday == 0 ? 7 : tm.tm_wday);
+         break;
+         
+       case 'U':
+         /* The week number of the current year as a decimal number, range
+            00 to 53, starting with the first Sunday as the first day of
+            week 01.
+         */
+         rc = mu_stream_printf (str, "%02d",
+                                (tm.tm_yday - tm.tm_wday + 7) / 7);
+         break;
+         
+       case 'w':
+         /* The day of the week as a decimal, range 0 to 6, Sunday being 0.
+          */
+         rc = mu_stream_printf (str, "%01d", tm.tm_wday);
+         break;
+         
+       case 'W':
+         /* The week number of the current year as a decimal number, range
+            00 to 53, starting with the first Monday as the first day of
+            week 01. */
+         rc = mu_stream_printf (str, "%02d", 
+                        (tm.tm_yday - (tm.tm_wday - 1 + 7) % 7 + 7) / 7);
+         break;
 
-#if HAVE_STRUCT_TM_TM_GMTOFF
-  tm->tm_gmtoff = tzoffset;
-#endif
+         /* The preferred date representation without the time:
+            equivalent to %D */
+       case 'x':
+         rc = mu_c_streamftime (str, "%m/%d/%y", &tm, tz);
+         break;
+         
+       case 'X':
+         /* The preferred date representation without the date */
+         rc = mu_c_streamftime (str, "%H:%M:%S", &tm, tz);
+         break;
 
-  if (tz)
-    {
-      tz->utc_offset = tzoffset;
-      tz->tz_name = NULL;
+       case 'y':
+         /* The year as a decimal number without a century (range 00 to 99).
+          */
+         rc = mu_stream_printf (str, "%02d", (tm.tm_year + 1900) % 100);
+         break;
+         
+       case 'Y':
+         /* The year as a decimal number including the century. */
+         rc = mu_stream_printf (str, "%d", tm.tm_year + 1900);
+         break;
+         
+       case 'z':
+       case 'Z':
+         /* The time-zone as hour offset from GMT, for formatting RFC-822
+            dates (e.g. "%a, %d %b %Y %H:%M:%S %z") */
+         {
+           int utc_off = tz ? tz->utc_offset : mu_utc_offset ();
+           int sign;
+           if (utc_off < 0)
+             {
+               sign = '-';
+               utc_off = - utc_off;
+             }
+           else
+             sign = '+';
+           utc_off /= 60;
+           rc = mu_stream_printf (str, "%c%02u%02u", sign,
+                                  utc_off / 60, utc_off % 60);
+         }
+         break;
+         
+       case '%':
+         /* A literal '%' character. */
+         rc = mu_stream_write (str, "%", 1, NULL);
+         break;
+         
+       case '+':
+         /* Not supported (date and time in date(1) format. */
+       default:
+         rc = mu_stream_write (str, fmt-1, 2, NULL);
+         break;
+       }
+      fmt++;
     }
-
-  *p += scanned;
-
-  return 0;
+  /* Restore input tm */
+  *input_tm = tm;
+  return rc;
 }
 
-/* "ctime" format is: Thu Jul 01 15:58:27 1999, with no trailing \n.  */
-int
-mu_parse_ctime_date_time (const char **p, struct tm *tm, mu_timezone *tz)
-{
-  int wday = 0;
-  int year = 0;
-  int mon = 0;
-  int day = 0;
-  int hour = 0;
-  int min = 0;
-  int sec = 0;
-  int n = 0;
+static int
+_mu_short_weekday_string (const char *str)
+{
   int i;
-  char weekday[5] = "";
-  char month[5] = "";
-
-  if (sscanf (*p, "%3s %3s %2d %2d:%2d:%2d %d%n\n",
-       weekday, month, &day, &hour, &min, &sec, &year, &n) != 7)
-    return -1;
-
-  *p += n;
+  
+  for (i = 0; i < 7; i++)
+    {
+      if (mu_c_strncasecmp (str, short_wday[i], 3) == 0)
+       return i;
+    }
+  return -1;
+}
 
+static int
+_mu_full_weekday_string (const char *str, char **endp)
+{
+  int i;
+  
   for (i = 0; i < 7; i++)
     {
-      if (mu_c_strncasecmp (weekday, wdays[i], 3) == 0)
+      if (mu_c_strcasecmp (str, full_wday[i]) == 0)
        {
-         wday = i;
-         break;
+         if (endp)
+           *endp = (char*) (str + strlen (full_wday[i]));
+         return i;
        }
     }
+  return -1;
+}
+
 
+static int
+_mu_short_month_string (const char *str)
+{  
+  int i;
+  
   for (i = 0; i < 12; i++)
     {
-      if (mu_c_strncasecmp (month, months[i], 3) == 0)
+      if (mu_c_strncasecmp (str, short_month[i], 3) == 0)
+       return i;
+    }
+  return -1;
+}
+
+static int
+_mu_full_month_string (const char *str, char **endp)
+{  
+  int i;
+  
+  for (i = 0; i < 12; i++)
+    {
+      if (mu_c_strcasecmp (str, full_month[i]) == 0)
        {
-         mon = i;
-         break;
+         if (endp)
+           *endp = (char*) (str + strlen (full_month[i]));
+         return i;
        }
     }
+  return -1;
+}
 
-  if (tm)
-    {
-      memset (tm, 0, sizeof (struct tm));
-
-      tm->tm_sec = sec;
-      tm->tm_min = min;
-      tm->tm_hour = hour;
-      tm->tm_mday = day;
-      tm->tm_wday = wday;
-      tm->tm_mon = mon;
-      tm->tm_year = (year > 1900) ? year - 1900 : year;
+int
+get_num (const char *str, char **endp, int ndig, int minval, int maxval,
+        int *pn)
+{
+  int x = 0;
+  int i;
+  
+  errno = 0;
+  for (i = 0; i < ndig && *str && mu_isdigit (*str); str++, i++)
+    x = x * 10 + *str - '0';
+
+  *endp = (char*) str;
+  if (i == 0)
+    return -1;
+  else if (pn)
+    *pn = i;
+  else if (i != ndig)
+    return -1;
+  if (x < minval || x > maxval)
+    return -1;
+  return x;
+}
+
+#define DT_YEAR  0x01
+#define DT_MONTH 0x02
+#define DT_MDAY  0x04
+#define DT_WDAY  0x08
+#define DT_HOUR  0x10
+#define DT_MIN   0x20
+#define DT_SEC   0x40
+
+int
+mu_scan_datetime (const char *input, const char *fmt,
+                 struct tm *tm, struct mu_timezone *tz, char **endp)
+{
+  int rc = 0;
+  char *p;
+  int n;
+  int eof_ok = 0;
+  int datetime_parts = 0;
+  
+  memset (tm, 0, sizeof *tm);
 #ifdef HAVE_STRUCT_TM_TM_ISDST
-      tm->tm_isdst = -1;       /* unknown. */
+  tm->tm_isdst = -1;   /* unknown. */
 #endif
-    }
-
-  /* ctime has no timezone information, set tz to local TZ if they ask. */
+  /* provide default timezone, in case it is not supplied in input */
   if (tz)
     {
+      memset (tz, 0, sizeof *tz);
       tz->utc_offset = mu_utc_offset ();
-      tz->tz_name = NULL;
     }
+
+  /* Skip leading whitespace */
+  input = mu_str_skip_class (input, MU_CTYPE_BLANK);
+  for (; *fmt && rc == 0; fmt++)
+    {
+      if (mu_isspace (*fmt))
+       {
+         fmt = mu_str_skip_class (fmt, MU_CTYPE_BLANK);
+         input = mu_str_skip_class (input, MU_CTYPE_BLANK);
+         if (!*fmt)
+           break;
+       }
+      eof_ok = 0;
+      
+      if (*fmt == '%')
+       {
+         switch (*++fmt)
+           {
+           case 'a':
+             /* The abbreviated weekday name. */
+             n = _mu_short_weekday_string (input);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_wday = n;
+                 datetime_parts |= DT_WDAY;
+                 input += 3;
+               }
+             break;
+                 
+           case 'A':
+             /* The full weekday name. */
+             n = _mu_full_weekday_string (input, &p);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_wday = n;
+                 datetime_parts |= DT_WDAY;
+                 input = p;
+               }
+             break;
+             
+           case 'b':
+             /* The abbreviated month name. */
+             n = _mu_short_month_string (input);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_mon = n;
+                 datetime_parts |= DT_MONTH;
+                 input += 3;
+               }
+             break;
+
+           case 'B':
+             /* The full month name. */
+             n = _mu_full_month_string (input, &p);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_mon = n;
+                 datetime_parts |= DT_MONTH;
+                 input = p;
+               }
+             break;
+             
+           case 'd':
+             /* The day of the month as a decimal number (range 01 to 31). */
+             n = get_num (input, &p, 2, 1, 31, NULL);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_mday = n;
+                 datetime_parts |= DT_MDAY;
+                 input = p;
+               }
+             break;
+             
+           case 'e':
+             /* Like %d, the day of the month as a decimal number, but a
+                leading zero is replaced by a space. */
+             {
+               int ndig;
+               
+               n = get_num (input, &p, 2, 1, 31, &ndig);
+               if (n == -1)
+                 rc = MU_ERR_PARSE;
+               else
+                 {
+                   tm->tm_mday = n;
+                   datetime_parts |= DT_MDAY;
+                   input = p;
+                 }
+             }
+             break;
+             
+           case 'H':
+             /* The hour as a decimal number using a 24-hour clock (range
+                00 to 23). */
+             n = get_num (input, &p, 2, 0, 23, NULL);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_hour = n;
+                 datetime_parts |= DT_HOUR;
+                 input = p;
+               }
+             break;
+             
+           case 'm':
+             /* The month as a decimal number (range 01 to 12). */
+             n = get_num (input, &p, 2, 1, 12, NULL);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_mon = n - 1;
+                 datetime_parts |= DT_MONTH;
+                 input = p;
+               }
+             break;
+             
+           case 'M':
+             /* The minute as a decimal number (range 00 to 59). */
+             n = get_num (input, &p, 2, 0, 59, NULL);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_min = n;
+                 datetime_parts |= DT_MIN;
+                 input = p;
+               }
+             break;
+             
+           case 'S':
+             /* The second as a decimal number (range 00 to 60) */
+             n = get_num (input, &p, 2, 0, 60, NULL);
+             if (n == -1)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_sec = n;
+                 datetime_parts |= DT_SEC;
+                 input = p;
+               }
+             break;
+             
+           case 'Y':
+             /* The year as a decimal number including the century. */
+             errno = 0;
+             n = strtoul (input, &p, 10);
+             if (errno || p == input)
+               rc = MU_ERR_PARSE;
+             else
+               {
+                 tm->tm_year = n - 1900;
+                 datetime_parts |= DT_YEAR;
+                 input = p;
+               }
+             break;
+                 
+           case 'z':
+             /* The time-zone as hour offset from GMT */
+             {
+               int sign = 1;
+               int hr;
+               
+               if (*input == '+')
+                 input++;
+               else if (*input == '-')
+                 {
+                   input++;
+                   sign = -1;
+                 }
+               n = get_num (input, &p, 2, 0, 11, NULL);
+               if (n == -1)
+                 rc = MU_ERR_PARSE;
+               else
+                 {
+                   input = p;
+                   hr = n;
+                   n = get_num (input, &p, 2, 0, 59, NULL);
+                   if (n == -1)
+                     rc = MU_ERR_PARSE;
+                   else
+                     {
+                       input = p;
+                       if (tz)
+                         tz->utc_offset = sign * (hr * 60 + n) * 60;
+                     }
+                 }
+             }
+             break;
+                     
+           case '%':
+             if (*input == '%')
+               input++;
+             else
+               rc = MU_ERR_PARSE;
+             break;
+             
+           case '?':
+             eof_ok = 1;
+             break;
+           }
+         if (eof_ok && rc == 0 && *input == 0)
+           break;
+       }
+      else if (*input != *fmt)
+       rc = MU_ERR_PARSE;
+      else
+       input++;
+    }
+
+  if (!eof_ok && rc == 0 && *input == 0 && *fmt)
+    rc = MU_ERR_PARSE;
+
+  if (!(datetime_parts & DT_WDAY) &&
+      (datetime_parts & (DT_YEAR|DT_MONTH|DT_MDAY)) ==
+      (DT_YEAR|DT_MONTH|DT_MDAY))
+    tm->tm_wday = dayofweek (tm->tm_year + 1900, tm->tm_mon, tm->tm_mday);
   
-  return 0;
+  if (endp)
+    *endp = (char*) input;
+  
+  return rc;
 }
diff --git a/libmailutils/base/mutil.c b/libmailutils/base/mutil.c
index 87b2586..6cf4cfb 100644
--- a/libmailutils/base/mutil.c
+++ b/libmailutils/base/mutil.c
@@ -56,18 +56,6 @@ mu_mh_delim (const char *str)
   return str[0] == '\n';
 }
 
-/* A locale-independent version of strftime */
-size_t
-mu_strftime (char *s, size_t max, const char *format, const struct tm *tm)
-{
-  size_t size;
-  mu_set_locale ("C");
-  size = strftime (s, max, format, tm);
-  mu_restore_locale ();
-  return size;
-}
-  
-
 static void
 assoc_str_free (void *data)
 {
diff --git a/libmu_dbm/create.c b/libmailutils/base/strftime.c
similarity index 60%
copy from libmu_dbm/create.c
copy to libmailutils/base/strftime.c
index 44c161d..5c1d89e 100644
--- a/libmu_dbm/create.c
+++ b/libmailutils/base/strftime.c
@@ -18,27 +18,27 @@
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <mailutils/types.h>
-#include <mailutils/list.h>
-#include <mailutils/url.h>
-#include <mailutils/dbm.h>
-#include <mailutils/errno.h>
 #include <mailutils/util.h>
-#include "mudbm.h"
+#include <mailutils/stream.h>
+#include <mailutils/errno.h>
+#include <mailutils/cstr.h>
 
-int
-mu_dbm_create (char *name, mu_dbm_file_t *db, int defsafety)
+/* A locale-independent version of strftime */
+size_t
+mu_strftime (char *buf, size_t size, const char *format, struct tm *tm)
 {
   int rc;
-  mu_url_t url;
+  mu_stream_t str;
+  mu_stream_stat_buffer stat;
 
-  mu_dbm_init ();
-  rc = mu_url_create_hint (&url, name, 0, mu_dbm_hint);
-  if (rc)
-    return rc;
-  rc = mu_dbm_create_from_url (url, db, defsafety);
-  mu_url_destroy (&url);
-  return rc;
+  if (mu_fixed_memory_stream_create (&str, buf, size, MU_STREAM_WRITE))
+    return 0;
+  mu_stream_set_stat (str, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT), stat);
+  rc = mu_c_streamftime (str, format, tm, NULL);
+  if (rc == 0)
+    rc = mu_stream_write (str, "", 1, NULL);
+  mu_stream_unref (str);
+  return rc ? 0 : stat[MU_STREAM_STAT_OUT] - 1;
 }
+  
+
diff --git a/libmailutils/stream/memory_stream.c 
b/libmailutils/stream/memory_stream.c
index 8bd260f..70e009d 100644
--- a/libmailutils/stream/memory_stream.c
+++ b/libmailutils/stream/memory_stream.c
@@ -88,6 +88,27 @@ _memory_write (mu_stream_t stream, const char *iptr, size_t 
isize,
 }
 
 static int
+_fixed_size_memory_write (mu_stream_t stream, const char *iptr, size_t isize,
+                         size_t *nbytes)
+{
+  struct _mu_memory_stream *mfs = (struct _mu_memory_stream *) stream;
+  
+  if (mfs->capacity < mfs->offset + isize)
+    isize = mfs->capacity - mfs->offset;
+
+  memcpy (mfs->ptr + mfs->offset, iptr, isize);
+  
+  mfs->offset += isize;
+
+  if (mfs->offset > mfs->size)
+    mfs->size = mfs->offset;
+
+  if (nbytes)
+    *nbytes = isize;
+  return 0;
+}
+
+static int
 _memory_truncate (mu_stream_t stream, mu_off_t len)
 {
   struct _mu_memory_stream *mfs = (struct _mu_memory_stream *) stream;
@@ -271,3 +292,39 @@ mu_static_memory_stream_create (mu_stream_t *pstream, 
const void *mem,
   return 0;
 }
   
+int
+mu_fixed_memory_stream_create (mu_stream_t *pstream, void *mem,
+                              size_t size, int flags)
+{
+  mu_stream_t stream;
+  struct _mu_memory_stream *str;
+
+  flags &= (MU_STREAM_READ|MU_STREAM_WRITE);
+  if (!flags)
+    return EINVAL;
+  
+  str = (struct _mu_memory_stream *)
+    _mu_stream_create (sizeof (*str), flags | MU_STREAM_SEEK);
+  
+  if (!str)
+    return ENOMEM;
+
+  str->ptr = (void*) mem;
+  str->size = size;
+  str->offset = 0;
+  str->capacity = size;
+
+  str->stream.flags |= _MU_STR_OPEN;
+  if (flags & MU_STREAM_READ)
+    str->stream.read = _memory_read;
+  if (flags & MU_STREAM_WRITE)
+    str->stream.write = _fixed_size_memory_write;
+  str->stream.size = _memory_size;
+  str->stream.ctl = _memory_ioctl;
+  str->stream.seek = _memory_seek;
+  
+  stream = (mu_stream_t) str;
+  *pstream = stream;
+
+  return 0;
+}
diff --git a/libmailutils/tests/.gitignore b/libmailutils/tests/.gitignore
index 444e39e..ca22f53 100644
--- a/libmailutils/tests/.gitignore
+++ b/libmailutils/tests/.gitignore
@@ -16,6 +16,7 @@ imapio
 listop
 mailcap
 prop
+strftime
 url-comp
 url-parse
 wicket
diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am
index 8463ffe..01c01ac 100644
--- a/libmailutils/tests/Makefile.am
+++ b/libmailutils/tests/Makefile.am
@@ -51,6 +51,8 @@ noinst_PROGRAMS = \
  listop\
  mailcap\
  prop\
+ scantime\
+ strftime\
  tempfile\
  url-comp\
  url-parse\
@@ -81,6 +83,8 @@ TESTSUITE_AT = \
  list.at\
  mailcap.at\
  prop.at\
+ scantime.at\
+ strftime.at\
  testsuite.at\
  url.at\
  url-comp.at\
diff --git a/libmailutils/tests/scantime.at b/libmailutils/tests/scantime.at
new file mode 100644
index 0000000..e4d7e2d
--- /dev/null
+++ b/libmailutils/tests/scantime.at
@@ -0,0 +1,64 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# GNU Mailutils is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3, or (at
+# your option) any later version.
+#
+# GNU Mailutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.
+
+AT_BANNER(mu_scan_datetime)
+
+dnl ---------------------------------------------------------------------
+dnl SCANTIME([NAME], [KW], [FMT], [INPUT], [STDOUT = `'], [STDERR = `'])
+dnl
+m4_pushdef([SCANTIME],[
+m4_pushdef([MU_TEST_GROUP],[scantime])
+m4_pushdef([MU_TEST_KEYWORDS],[scantime mu_scan_datetime])
+m4_pushdef([MU_TEST_COMMAND],[TZ=0 scantime -format='$3'])
+MU_GENERIC_TEST([$1],[$2],[$4],[],[$5],[$6])
+m4_popdef([MU_TEST_COMMAND])
+m4_popdef([MU_TEST_KEYWORDS])
+m4_popdef([MU_TEST_GROUP])
+])
+dnl ---------------------------------------------------------------------
+
+SCANTIME([RFC-822 time],[rfc822],
+[%a %b %e %H:%M:%S %Y],
+[Tue May  3 13:25:26 2011
+Fri Nov 11 11:55:01 2011],
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,tz=0
+sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,tz=0
+])
+
+SCANTIME([IMAP time format],[imap],
+[%d-%b-%Y %H:%M:%S %z],
+[03-May-2011 13:25:26 +0100
+11-Nov-2011 11:55:01 +0100],
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,tz=3600
+sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,tz=3600
+])
+
+SCANTIME([IMAP search time format],[imap-search],
+[%d-%b-%Y%? %H:%M:%S %z],
+[03-May-2011 13:25:26 +0100
+03-May-2011],
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,tz=3600
+sec=0,min=0,hour=0,mday=3,mon=4,year=111,wday=2,tz=0
+])
+
+SCANTIME([IMAP INTERNALDATE],[imap-internaldate],
+[%a, %d %b %Y %H:%M:%S %z],
+[Tue, 03 May 2011 13:25:26 +0200],
+[sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,tz=7200
+])
+
+m4_popdef([SCANTIME])
+dnl ---------------------------------------------------------------------
diff --git a/libmailutils/tests/imapio.c b/libmailutils/tests/scantime.c
similarity index 51%
copy from libmailutils/tests/imapio.c
copy to libmailutils/tests/scantime.c
index 9783115..58464ac 100644
--- a/libmailutils/tests/imapio.c
+++ b/libmailutils/tests/scantime.c
@@ -1,5 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 2011 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2007, 2009, 2010, 2011 Free Software
+   Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -16,20 +17,22 @@
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
-#endif 
+#endif
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <mailutils/imapio.h>
-#include <mailutils/errno.h>
 #include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/util.h>
 #include <mailutils/stream.h>
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
 #include <mailutils/stdstream.h>
 
 void
 usage ()
 {
-  mu_stream_printf (mu_strout, "usage: %s [debug=SPEC] [-transcript] 
[-server]\n",
+  mu_stream_printf (mu_strout, "usage: %s [-format=FMT] [-tz=TZ]\n",
                    mu_program_name);
   exit (0);
 }
@@ -37,24 +40,22 @@ usage ()
 int
 main (int argc, char **argv)
 {
-  int i, rc;
-  int transcript = 0;
-  mu_imapio_t io;
-  mu_stream_t str;
-  int imapio_mode = MU_IMAPIO_CLIENT;
+  int rc, i;
+  char *format = "%d-%b-%Y%? %H:%M:%S %z";
+  char *buf = NULL;
+  size_t size = 0;
+  size_t n;
+
+  mu_set_program_name (argv[0]);
   
   mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
-  
+
   for (i = 1; i < argc; i++)
     {
       char *opt = argv[i];
 
-      if (strncmp (opt, "debug=", 6) == 0)
-       mu_debug_parse_spec (opt + 6);
-      else if (strcmp (opt, "-transcript") == 0)
-       transcript = 1;
-      else if (strcmp (opt, "-server") == 0)
-       imapio_mode = MU_IMAPIO_SERVER;
+      if (strncmp (opt, "-format=", 8) == 0)
+       format = opt + 8;
       else if (strcmp (opt, "-h") == 0)
        usage ();
       else
@@ -64,34 +65,37 @@ main (int argc, char **argv)
        }
     }
 
-  MU_ASSERT (mu_iostream_create (&str, mu_strin, mu_strout));
-
-  
-  MU_ASSERT (mu_imapio_create (&io, str, imapio_mode));
-
-  if (transcript)
-    mu_imapio_trace_enable (io);
-  mu_stream_unref (str);
-
-  while ((rc = mu_imapio_getline (io)) == 0)
+  while ((rc = mu_stream_getline (mu_strin, &buf, &size, &n)) == 0 && n > 0)
     {
-      size_t wc;
-      char **wv;
+      char *endp;
+      struct tm tm;
+      struct mu_timezone tz;
 
-      MU_ASSERT (mu_imapio_get_words (io, &wc, &wv));
-      if (wc == 0)
-       break;
+      mu_rtrim_class (buf, MU_CTYPE_ENDLN);
 
-      mu_stream_printf (mu_strerr, "%d\n", wc);
-      for (i = 0; i < wc; i++)
+      rc = mu_scan_datetime (buf, format, &tm, &tz, &endp);
+      if (rc)
        {
-         mu_stream_printf (mu_strerr, "%d: '%s'\n", i, wv[i]);
+         if (*endp)
+           mu_error ("parse failed near %s", endp);
+         else
+           mu_error ("parse failed at end of input");
+         continue;
        }
+      if (*endp)
+       mu_printf ("# stopped at %s\n", endp);
+      /* FIXME: add tm_yday? */
+      mu_printf 
("sec=%d,min=%d,hour=%d,mday=%d,mon=%d,year=%d,wday=%d,tz=%d\n",
+                tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mday, tm.tm_mon,
+                tm.tm_year, tm.tm_wday, tz.utc_offset);
+                
+      //mu_c_streamftime (mu_strout, "%c %z%n", &tm, &tz);
     }
-
+  
   if (rc)
-    mu_error ("mu_imap_getline: %s", mu_strerror (rc));
-
-  mu_imapio_free (io);
+    {
+      mu_error ("%s", mu_strerror (rc));
+      return 1;
+    }
   return 0;
 }
diff --git a/libmailutils/tests/strftime.at b/libmailutils/tests/strftime.at
new file mode 100644
index 0000000..bba6351
--- /dev/null
+++ b/libmailutils/tests/strftime.at
@@ -0,0 +1,1921 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# GNU Mailutils is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3, or (at
+# your option) any later version.
+#
+# GNU Mailutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.
+
+AT_BANNER(mu_c_streamftime)
+
+m4_pushdef([STRFTIME_TZ],[0])
+
+dnl ------------------------------------------------------------------
+dnl STRFTIME([FMT], [INPUT], [STDOUT = `'], [STDERR = `'])
+dnl
+m4_pushdef([STRFTIME],[
+m4_pushdef([MU_TEST_GROUP],[streamftime])
+m4_pushdef([MU_TEST_KEYWORDS],[strftime strftime-$1])
+m4_pushdef([MU_TEST_COMMAND],[strftime -tz=STRFTIME_TZ -format='$1'])
+MU_GENERIC_TEST([$1],[],[$2],[],[$3],[$4])
+m4_popdef([MU_TEST_COMMAND])
+m4_popdef([MU_TEST_KEYWORDS])
+m4_popdef([MU_TEST_GROUP])
+])
+
+dnl ------------------------------------------------------------
+# Some test data
+
+#    January 1970
+# Su Mo Tu We Th Fr Sa
+#              1  2  3
+#  4  5  6  7  8  9 10
+# 11 12 13 14 15 16 17
+# 18 19 20 21 22 23 24
+# 25 26 27 28 29 30 31
+#
+#  1 0
+#  2 86400
+#  3 172800
+#  4 259200
+#  5 345600
+#  6 432000
+#  7 518400
+#  8 604800
+#  9 691200
+# 10 777600
+# 11 864000
+# 12 950400
+# 13 1036800
+# 14 1123200
+# 15 1209600
+# 16 1296000
+# 17 1382400
+# 18 1468800
+# 19 1555200
+# 20 1641600
+# 21 1728000
+# 22 1814400
+# 23 1900800
+# 24 1987200
+# 25 2073600
+# 26 2160000
+# 27 2246400
+# 28 2332800
+# 29 2419200
+# 30 2505600
+# 31 2592000
+
+#    November 1970
+# Su Mo Tu We Th Fr Sa
+#  1  2  3  4  5  6  7
+#  8  9 10 11 12 13 14
+# 15 16 17 18 19 20 21
+# 22 23 24 25 26 27 28
+# 29 30
+ 
+# 1 26265600
+# 2 26352000
+# 3 26438400
+# 4 26524800
+# 5 26611200
+# 6 26697600
+# 7 26784000
+
+# Months
+# Jan   0
+# Feb   2678400 
+# Mar  5097600 
+# Apr  7776000 
+# May  10368000
+# Jun  13046400
+# Jul  15638400
+# Aug  18316800
+# Sep  20995200
+# Oct  23587200
+# Nov  26265600
+# Dec  28857600
+#
+dnl ------------------------------------------------------------
+# The abbreviated weekday name.
+STRFTIME([%a],
+[26265600
+26352000
+26438400
+26524800
+26611200
+26697600
+26784000],
+[Sun
+Mon
+Tue
+Wed
+Thu
+Fri
+Sat
+])
+
+# The full weekday name.
+STRFTIME([%A],
+[26265600
+26352000
+26438400
+26524800
+26611200
+26697600
+26784000],
+[Sunday
+Monday
+Tuesday
+Wednesday
+Thursday
+Friday
+Saturday
+])
+
+The abbreviated month name.
+STRFTIME([%b],
+[0
+2678400 
+5097600 
+7776000 
+10368000
+13046400
+15638400
+18316800
+20995200
+23587200
+26265600
+28857600],
+[Jan
+Feb
+Mar
+Apr
+May
+Jun
+Jul
+Aug
+Sep
+Oct
+Nov
+Dec
+])
+
+# The abbreviated month name.
+STRFTIME([%h],
+[0
+2678400 
+5097600 
+7776000 
+10368000
+13046400
+15638400
+18316800
+20995200
+23587200
+26265600
+28857600],
+[Jan
+Feb
+Mar
+Apr
+May
+Jun
+Jul
+Aug
+Sep
+Oct
+Nov
+Dec
+])
+
+STRFTIME([%B],
+[0
+2678400 
+5097600 
+7776000 
+10368000
+13046400
+15638400
+18316800
+20995200
+23587200
+26265600
+28857600],
+[January
+February
+March
+April
+May
+June
+July
+August
+September
+October
+November
+December
+])
+
+# The century number (year/100) as a 2-digit integer.
+STRFTIME([%C],
+[26697600
+1322872282],
+[19
+20
+])
+
+# The day of the month as a decimal number (range 01 to 31).
+STRFTIME([%d],
+[26265600
+28771200],
+[01
+30
+])
+
+# Like %d, the day of the month as a decimal number, but a leading
+# zero is replaced by a space.
+STRFTIME([%e],
+[26265600
+28771200],
+[ 1
+30
+])
+
+# A no-op modifier.
+STRFTIME([%EC],
+[26697600
+1322872282],
+[19
+20
+])
+
+# The ISO 8601 year with century as a decimal number.  The 4-digit
+# year corresponding to the ISO week number (see %V).  This has
+# the same format and value as %y, except that if the ISO week
+# number belongs to the previous or next year, that year is used
+# instead.
+
+# Test data:
+#   Date       TS              %V      %G
+# 1972-12-31   94608000        52      1972
+#
+# 1973-01-01   94694400        01      1973
+# 1973-12-01   123552000       48      1973
+# 1973-12-30   126057600       52      1973
+# 1973-12-31   126144000       01      1974
+#
+# 1975-01-01   157766400       01      1975
+# 1975-12-28   188956800       52      1975
+# 1975-12-29   189043200       01      1976
+# 1975-12-31   189216000       01      1976
+#
+# 1977-01-01   220924800       53      1976
+# 1977-01-02   221011200       53      1976
+# 1977-01-03   221097600       01      1977
+# 1977-12-25   251856000       51      1977
+# 1977-12-26   251942400       52      1977
+# 1977-12-31   252374400       52      1977
+
+STRFTIME([%G],
+[;1972
+94608000
+;1973
+94694400
+123552000
+126057600
+126144000
+;1975
+157766400
+188956800
+189043200
+189216000
+;1977
+220924800
+221011200
+221097600
+251856000
+251942400
+252374400],
+[;1972
+1972
+;1973
+1973
+1973
+1973
+1974
+;1975
+1975
+1975
+1976
+1976
+;1977
+1976
+1976
+1977
+1977
+1977
+1977
+])
+
+# Like %G, but without century, that is, with a 2-digit year (00-99).
+STRFTIME([%g],
+[;1972
+94608000
+;1973
+94694400
+123552000
+126057600
+126144000
+;1975
+157766400
+188956800
+189043200
+189216000
+;1977
+220924800
+221011200
+221097600
+251856000
+251942400
+252374400],
+[;1972
+72
+;1973
+73
+73
+73
+74
+;1975
+75
+75
+76
+76
+;1977
+76
+76
+77
+77
+77
+77
+])
+
+# The hour as a decimal number using a 24-hour clock (range 00 to 23).
+STRFTIME([%H],
+[1322870400
+1322874000
+1322877600
+1322881200
+1322884800
+1322888400
+1322892000
+1322895600
+1322899200
+1322902800
+1322906400
+1322910000
+1322913600
+1322917200
+1322920800
+1322924400
+1322928000
+1322931600
+1322935200
+1322938800
+1322942400
+1322946000
+1322949600
+1322953200],
+[00
+01
+02
+03
+04
+05
+06
+07
+08
+09
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+])
+
+# The hour as a decimal number using a 12-hour clock (range 01 to 12). 
+STRFTIME([%I],
+[1322870400
+1322874000
+1322877600
+1322881200
+1322884800
+1322888400
+1322892000
+1322895600
+1322899200
+1322902800
+1322906400
+1322910000
+1322913600
+1322917200
+1322920800
+1322924400
+1322928000
+1322931600
+1322935200
+1322938800
+1322942400
+1322946000
+1322949600
+1322953200],
+[12
+01
+02
+03
+04
+05
+06
+07
+08
+09
+10
+11
+12
+01
+02
+03
+04
+05
+06
+07
+08
+09
+10
+11
+])
+       
+# The day of the year as a decimal number (range 001 to 366).
+STRFTIME([%j],
+[1322872282
+1204329600],
+[337
+061
+])
+
+# The hour (24-hour clock) as a decimal number (range 0 to 23);
+# single digits are preceded by a blank.
+STRFTIME([%k],
+[1322870400
+1322874000
+1322877600
+1322881200
+1322884800
+1322888400
+1322892000
+1322895600
+1322899200
+1322902800
+1322906400
+1322910000
+1322913600
+1322917200
+1322920800
+1322924400
+1322928000
+1322931600
+1322935200
+1322938800
+1322942400
+1322946000
+1322949600
+1322953200],
+[ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+])
+
+# The hour (12-hour clock) as a decimal number (range 1 to 12);
+# single digits are preceded by a blank.
+STRFTIME([%l],
+[1322870400
+1322874000
+1322877600
+1322881200
+1322884800
+1322888400
+1322892000
+1322895600
+1322899200
+1322902800
+1322906400
+1322910000
+1322913600
+1322917200
+1322920800
+1322924400
+1322928000
+1322931600
+1322935200
+1322938800
+1322942400
+1322946000
+1322949600
+1322953200],
+[12
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+])
+
+# The month as a decimal number (range 01 to 12)
+STRFTIME([%m],
+[0
+2678400 
+5097600 
+7776000 
+10368000
+13046400
+15638400
+18316800
+20995200
+23587200
+26265600
+28857600],
+[01
+02
+03
+04
+05
+06
+07
+08
+09
+10
+11
+12
+])
+
+# The minute as a decimal number (range 00 to 59).
+STRFTIME([%M],
+[1322870400
+1322870460
+1322870520
+1322870580
+1322870640
+1322870700
+1322870760
+1322870820
+1322870880
+1322870940
+1322871000
+1322871060
+1322871120
+1322871180
+1322871240
+1322871300
+1322871360
+1322871420
+1322871480
+1322871540
+1322871600
+1322871660
+1322871720
+1322871780
+1322871840
+1322871900
+1322871960
+1322872020
+1322872080
+1322872140
+1322872200
+1322872260
+1322872320
+1322872380
+1322872440
+1322872500
+1322872560
+1322872620
+1322872680
+1322872740
+1322872800
+1322872860
+1322872920
+1322872980
+1322873040
+1322873100
+1322873160
+1322873220
+1322873280
+1322873340
+1322873400
+1322873460
+1322873520
+1322873580
+1322873640
+1322873700
+1322873760
+1322873820
+1322873880
+1322873940],
+[00
+01
+02
+03
+04
+05
+06
+07
+08
+09
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+])
+
+# A newline character
+STRFTIME([<%n>],
+[0],
+[<
+>
+])
+
+# %O - a no-op modifier
+STRFTIME([%OC],
+[26697600
+1322872282],
+[19
+20
+])
+
+# Either "AM" or "PM" according to the given time value.
+# Noon is treated as "PM" and midnight as "AM".
+STRFTIME([%p],
+[1322870400
+1322910000
+1322913600
+1322917200],
+[AM
+AM
+PM
+PM
+])
+
+# Like %p but in lowercase: "am" or "pm".
+STRFTIME([%P],
+[1322870400
+1322910000
+1322913600
+1322917200],
+[am
+am
+pm
+pm
+])
+
+# The number of seconds since the Epoch
+STRFTIME([%s],
+[0
+1322913600],
+[0
+1322913600
+])
+
+# The second as a decimal number (range 00 to 60)
+STRFTIME([%S],
+[1322870400
+1322870401
+1322870402
+1322870403
+1322870404
+1322870405
+1322870406
+1322870407
+1322870408
+1322870409
+1322870410
+1322870411
+1322870412
+1322870413
+1322870414
+1322870415
+1322870416
+1322870417
+1322870418
+1322870419
+1322870420
+1322870421
+1322870422
+1322870423
+1322870424
+1322870425
+1322870426
+1322870427
+1322870428
+1322870429
+1322870430
+1322870431
+1322870432
+1322870433
+1322870434
+1322870435
+1322870436
+1322870437
+1322870438
+1322870439
+1322870440
+1322870441
+1322870442
+1322870443
+1322870444
+1322870445
+1322870446
+1322870447
+1322870448
+1322870449
+1322870450
+1322870451
+1322870452
+1322870453
+1322870454
+1322870455
+1322870456
+1322870457
+1322870458
+1322870459],
+[00
+01
+02
+03
+04
+05
+06
+07
+08
+09
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+])
+
+# A tab character
+STRFTIME([<%t>],
+[0],
+[<     >
+])
+
+# The day of the week as a decimal, range 1 to 7, Monday being 1.
+STRFTIME([%u],
+[26265600
+26352000
+26438400
+26524800
+26611200
+26697600
+26784000],
+[7
+1
+2
+3
+4
+5
+6
+])
+
+# The week number of the current year as a decimal number, range
+# 00 to 53, starting with the first Sunday as the first day of
+# week 01.
+#
+# Test data are days of January of years from 1973 to 1978:
+# Year       Day of Jan 1st    
+# 1973         Mon             
+# 1974         Tue             
+# 1975         Wed             
+# 1976         Thu             
+# 1977         Sat
+# 1978         Sun
+STRFTIME([%U],
+[; 1973 - Monday
+94694400
+94780800
+94867200
+94953600
+95040000
+95126400
+95212800
+95299200
+95385600
+95472000
+95558400
+95644800
+95731200
+95817600
+95904000
+95990400
+96076800
+96163200
+96249600
+96336000
+96422400
+96508800
+96595200
+96681600
+96768000
+96854400
+96940800
+97027200
+97113600
+97200000
+97286400
+; 1974 - Tuesday
+126230400
+126316800
+126403200
+126489600
+126576000
+126662400
+126748800
+126835200
+126921600
+127008000
+127094400
+127180800
+127267200
+127353600
+127440000
+127526400
+127612800
+127699200
+127785600
+127872000
+127958400
+128044800
+128131200
+128217600
+128304000
+128390400
+128476800
+128563200
+128649600
+128736000
+128822400
+; 1975 - Wednesday
+157766400
+157852800
+157939200
+158025600
+158112000
+158198400
+158284800
+158371200
+158457600
+158544000
+158630400
+158716800
+158803200
+158889600
+158976000
+159062400
+159148800
+159235200
+159321600
+159408000
+159494400
+159580800
+159667200
+159753600
+159840000
+159926400
+160012800
+160099200
+160185600
+160272000
+160358400
+; 1976 - Thursday
+189302400
+189388800
+189475200
+189561600
+189648000
+189734400
+189820800
+189907200
+189993600
+190080000
+190166400
+190252800
+190339200
+190425600
+190512000
+190598400
+190684800
+190771200
+190857600
+190944000
+191030400
+191116800
+191203200
+191289600
+191376000
+191462400
+191548800
+191635200
+191721600
+191808000
+191894400
+; 1977 - Saturday
+220924800
+221011200
+221097600
+221184000
+221270400
+221356800
+221443200
+221529600
+221616000
+221702400
+221788800
+221875200
+221961600
+222048000
+222134400
+222220800
+222307200
+222393600
+222480000
+222566400
+222652800
+222739200
+222825600
+222912000
+222998400
+223084800
+223171200
+223257600
+223344000
+223430400
+223516800
+; 1978 - Sunday
+252460800
+252547200
+252633600
+252720000
+252806400
+252892800
+252979200
+253065600
+253152000
+253238400
+253324800
+253411200
+253497600
+253584000
+253670400
+253756800
+253843200
+253929600
+254016000
+254102400
+254188800
+254275200
+254361600
+254448000
+254534400
+254620800
+254707200
+254793600
+254880000
+254966400
+255052800],
+[; 1973 - Monday
+00
+00
+00
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+; 1974 - Tuesday
+00
+00
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+; 1975 - Wednesday
+00
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+; 1976 - Thursday
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+04
+; 1977 - Saturday
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+04
+05
+05
+; 1978 - Sunday
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+04
+05
+05
+05
+])
+
+# The ISO 8601:1988 week number of the current year as a decimal
+# number range 01 to 53, where week 1 is the first week that has
+# at least 4 days in the current year, and with Monday as the
+# first day of the week.
+#
+# Test data:
+#   Date       TS              %V      %G
+# 1972-12-31   94608000        52      1972
+#
+# 1973-01-01   94694400        01      1973
+# 1973-12-01   123552000       48      1973
+# 1973-12-30   126057600       52      1973
+# 1973-12-31   126144000       01      1974
+#
+# 1975-01-01   157766400       01      1975
+# 1975-12-28   188956800       52      1975
+# 1975-12-29   189043200       01      1976
+# 1975-12-31   189216000       01      1976
+#
+# 1977-01-01   220924800       53      1976
+# 1977-01-02   221011200       53      1976
+# 1977-01-03   221097600       01      1977
+# 1977-12-25   251856000       51      1977
+# 1977-12-26   251942400       52      1977
+# 1977-12-31   252374400       52      1977
+STRFTIME([%V],
+[;1972
+94608000
+;1973
+94694400
+123552000
+126057600
+126144000
+;1975
+157766400
+188956800
+189043200
+189216000
+;1977
+220924800
+221011200
+221097600
+251856000
+251942400
+252374400],
+[;1972
+52
+;1973
+01
+48
+52
+01
+;1975
+01
+52
+01
+01
+;1977
+53
+53
+01
+51
+52
+52
+])
+
+# The day of the week as a decimal, range 0 to 6, Sunday being 0.
+STRFTIME([%w],
+[26265600
+26352000
+26438400
+26524800
+26611200
+26697600
+26784000],
+[0
+1
+2
+3
+4
+5
+6
+])
+
+# The week number of the current year as a decimal number, range
+# 00 to 53, starting with the first Monday as the first day of
+# week 01.
+STRFTIME([%W],
+[; 1973 - Monday
+94694400
+94780800
+94867200
+94953600
+95040000
+95126400
+95212800
+95299200
+95385600
+95472000
+95558400
+95644800
+95731200
+95817600
+95904000
+95990400
+96076800
+96163200
+96249600
+96336000
+96422400
+96508800
+96595200
+96681600
+96768000
+96854400
+96940800
+97027200
+97113600
+97200000
+97286400
+; 1974 - Tuesday
+126230400
+126316800
+126403200
+126489600
+126576000
+126662400
+126748800
+126835200
+126921600
+127008000
+127094400
+127180800
+127267200
+127353600
+127440000
+127526400
+127612800
+127699200
+127785600
+127872000
+127958400
+128044800
+128131200
+128217600
+128304000
+128390400
+128476800
+128563200
+128649600
+128736000
+128822400
+; 1975 - Wednesday
+157766400
+157852800
+157939200
+158025600
+158112000
+158198400
+158284800
+158371200
+158457600
+158544000
+158630400
+158716800
+158803200
+158889600
+158976000
+159062400
+159148800
+159235200
+159321600
+159408000
+159494400
+159580800
+159667200
+159753600
+159840000
+159926400
+160012800
+160099200
+160185600
+160272000
+160358400
+; 1976 - Thursday
+189302400
+189388800
+189475200
+189561600
+189648000
+189734400
+189820800
+189907200
+189993600
+190080000
+190166400
+190252800
+190339200
+190425600
+190512000
+190598400
+190684800
+190771200
+190857600
+190944000
+191030400
+191116800
+191203200
+191289600
+191376000
+191462400
+191548800
+191635200
+191721600
+191808000
+191894400
+; 1977 - Saturday
+220924800
+221011200
+221097600
+221184000
+221270400
+221356800
+221443200
+221529600
+221616000
+221702400
+221788800
+221875200
+221961600
+222048000
+222134400
+222220800
+222307200
+222393600
+222480000
+222566400
+222652800
+222739200
+222825600
+222912000
+222998400
+223084800
+223171200
+223257600
+223344000
+223430400
+223516800
+; 1978 - Sunday
+252460800
+252547200
+252633600
+252720000
+252806400
+252892800
+252979200
+253065600
+253152000
+253238400
+253324800
+253411200
+253497600
+253584000
+253670400
+253756800
+253843200
+253929600
+254016000
+254102400
+254188800
+254275200
+254361600
+254448000
+254534400
+254620800
+254707200
+254793600
+254880000
+254966400
+255052800],
+[; 1973 - Monday
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+04
+05
+05
+05
+; 1974 - Tuesday
+00
+00
+00
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+; 1975 - Wednesday
+00
+00
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+; 1976 - Thursday
+00
+00
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+; 1977 - Saturday
+00
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+04
+05
+; 1978 - Sunday
+00
+01
+01
+01
+01
+01
+01
+01
+02
+02
+02
+02
+02
+02
+02
+03
+03
+03
+03
+03
+03
+03
+04
+04
+04
+04
+04
+04
+04
+05
+05
+])
+
+# The year as a decimal number without a century (range 00 to 99).
+STRFTIME([%y],
+[10
+80870400
+1322870453],
+[70
+72
+11
+])
+
+# The year as a decimal number including the century.
+STRFTIME([%Y],
+[10
+80870400
+1322870453],
+[1970
+1972
+2011
+])
+
+# The time-zone as hour offset from GMT, for formatting RFC-822
+# dates (e.g. "%a, %d %b %Y %H:%M:%S %z") */
+m4_define([STRFTIME_TZ],[0200])
+STRFTIME([%z],
+[1322870453],
+[+0200
+])
+
+m4_define([STRFTIME_TZ],[-0200])
+STRFTIME([%z],
+[1322870453],
+[-0200
+])
+m4_define([STRFTIME_TZ],[0])
+
+# A literal '%' character.
+STRFTIME([<%%>],
+[1],
+[<%>
+])
+
+# Any other character after % is reproduced verbatim
+STRFTIME([<%5d>],
+[1],
+[<%5d>
+])
+
+dnl ------------------------------------------------------------------
+STRFTIME([Today is %A, %B %e %Y%n%H:%M:%S (%z).],
+[1322879591],
+[Today is Saturday, December  3 2011
+02:33:11 (+0000).
+])
+
+dnl ------------------------------------------------------------------
+# Equivalent to %a %b %e %H:%M:%S %Y
+STRFTIME([%c],
+[26697600],
+[Fri Nov  6 00:00:00 1970
+])
+
+# Equivalent to %m/%d/%y
+STRFTIME([%D],
+[80870400
+1322872282],
+[07/25/72
+12/03/11
+])
+
+# Equivalent to %Y-%m-%d (the ISO 8601 date format)
+STRFTIME([%F],
+[80870400
+1322872282],
+[1972-07-25
+2011-12-03
+])
+
+# The time in a.m. or p.m. notation, i.e. %I:%M:%S %p. */
+STRFTIME([%r],
+[1322872282],
+[12:31:22 AM
+])
+
+# The time in 24-hour notation (%H:%M)
+STRFTIME([%R],
+[1322872282],
+[00:31
+])
+
+# The time in 24-hour notation (%H:%M:%S)
+STRFTIME([%T],
+[1322872282],
+[00:31:22
+])
+
+# The preferred date representation without the time:
+# equivalent to %D
+STRFTIME([%x],
+[1322872282],
+[12/03/11
+])
+
+# The preferred date representation without the date:
+# equivalent to %H:%M:%S
+STRFTIME([%X],
+[1322872282],
+[00:31:22
+])
+
+dnl ------------------------------------------------------------------
+
+m4_popdef([STRFTIME])
+m4_popdef([STRFTIME_TZ])
diff --git a/libmailutils/tests/imapio.c b/libmailutils/tests/strftime.c
similarity index 50%
copy from libmailutils/tests/imapio.c
copy to libmailutils/tests/strftime.c
index 9783115..5a57666 100644
--- a/libmailutils/tests/imapio.c
+++ b/libmailutils/tests/strftime.c
@@ -1,5 +1,6 @@
 /* GNU Mailutils -- a suite of utilities for electronic mail
-   Copyright (C) 2011 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2007, 2009, 2010, 2011 Free Software
+   Foundation, Inc.
 
    GNU Mailutils is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -16,20 +17,22 @@
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
-#endif 
+#endif
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <mailutils/imapio.h>
-#include <mailutils/errno.h>
 #include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/util.h>
 #include <mailutils/stream.h>
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
 #include <mailutils/stdstream.h>
 
 void
 usage ()
 {
-  mu_stream_printf (mu_strout, "usage: %s [debug=SPEC] [-transcript] 
[-server]\n",
+  mu_stream_printf (mu_strout, "usage: %s [-format=FMT] [-tz=TZ]\n",
                    mu_program_name);
   exit (0);
 }
@@ -37,24 +40,39 @@ usage ()
 int
 main (int argc, char **argv)
 {
-  int i, rc;
-  int transcript = 0;
-  mu_imapio_t io;
-  mu_stream_t str;
-  int imapio_mode = MU_IMAPIO_CLIENT;
+  int rc, i;
+  char *format = "%c";
+  char *buf = NULL;
+  size_t size = 0;
+  size_t n;
+  struct mu_timezone tz, *tzp = NULL;
+
+  mu_set_program_name (argv[0]);
   
+  mu_set_program_name (argv[0]);
   mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
-  
+
+  memset (&tz, 0, sizeof tz);
   for (i = 1; i < argc; i++)
     {
       char *opt = argv[i];
 
-      if (strncmp (opt, "debug=", 6) == 0)
-       mu_debug_parse_spec (opt + 6);
-      else if (strcmp (opt, "-transcript") == 0)
-       transcript = 1;
-      else if (strcmp (opt, "-server") == 0)
-       imapio_mode = MU_IMAPIO_SERVER;
+      if (strncmp (opt, "-format=", 8) == 0)
+       format = opt + 8;
+      else if (strncmp (opt, "-tz=", 4) == 0)
+       {
+         int sign;
+         int n = atoi (opt + 4);
+         if (n < 0)
+           {
+             sign = -1;
+             n = - n;
+           }
+         else
+           sign = 1;
+         tz.utc_offset = sign * ((n / 100 * 60) + n % 100) * 60;
+         tzp = &tz;
+       }
       else if (strcmp (opt, "-h") == 0)
        usage ();
       else
@@ -64,34 +82,35 @@ main (int argc, char **argv)
        }
     }
 
-  MU_ASSERT (mu_iostream_create (&str, mu_strin, mu_strout));
-
-  
-  MU_ASSERT (mu_imapio_create (&io, str, imapio_mode));
-
-  if (transcript)
-    mu_imapio_trace_enable (io);
-  mu_stream_unref (str);
-
-  while ((rc = mu_imapio_getline (io)) == 0)
+  while ((rc = mu_stream_getline (mu_strin, &buf, &size, &n)) == 0 && n > 0)
     {
-      size_t wc;
-      char **wv;
+      char *p;
+      struct tm *tm;
+      time_t t;
 
-      MU_ASSERT (mu_imapio_get_words (io, &wc, &wv));
-      if (wc == 0)
-       break;
+      mu_rtrim_class (buf, MU_CTYPE_ENDLN);
 
-      mu_stream_printf (mu_strerr, "%d\n", wc);
-      for (i = 0; i < wc; i++)
+      if (*buf == ';')
        {
-         mu_stream_printf (mu_strerr, "%d: '%s'\n", i, wv[i]);
+         mu_printf ("%s\n", buf);
+         continue;
+       }
+      t = strtoul (buf, &p, 10);
+      if (*p)
+       {
+         mu_error ("bad input line near %s", p);
+         continue;
        }
-    }
 
+      tm = gmtime (&t);
+      mu_c_streamftime (mu_strout, format, tm, tzp);
+      mu_printf ("\n");
+    }
+  
   if (rc)
-    mu_error ("mu_imap_getline: %s", mu_strerror (rc));
-
-  mu_imapio_free (io);
+    {
+      mu_error ("%s", mu_strerror (rc));
+      return 1;
+    }
   return 0;
 }
diff --git a/libmailutils/tests/testsuite.at b/libmailutils/tests/testsuite.at
index 59366ab..9235686 100644
--- a/libmailutils/tests/testsuite.at
+++ b/libmailutils/tests/testsuite.at
@@ -24,7 +24,7 @@ m4_define([MU_TEST_KEYWORDS])
 dnl ------------------------------------------------------------
 m4_define([MU_TEST_COMMAND])
 
-m4_define([mu_trimstr],[m4_if([$2],-1,[$1],[m4_substr($1,0,$2)...])])
+m4_define([mu_trimstr],[m4_if([$2],-1,[$1],[m4_substr([$1],0,$2)...])])
 m4_define([mu_firstline],[mu_trimstr([$1],m4_index([$1],[
 ]))])
 
@@ -43,7 +43,7 @@ dnl RUN-IF-FAIL $7
 dnl RUN-IF-PASS $8
 dnl
 m4_define([MU_GENERIC_TEST],[
-AT_SETUP([m4_if(MU_TEST_GROUP,[],,MU_TEST_GROUP: 
)m4_if([$1],[],mu_firstline([$3]),[$1])])
+AT_SETUP([m4_if(MU_TEST_GROUP,[],,MU_TEST_GROUP: 
)m4_if([$1],[],[mu_firstline([$3])],[$1])])
 AT_KEYWORDS([MU_TEST_KEYWORDS $2])
 AT_CHECK([
 AT_DATA([input],[$3
@@ -83,4 +83,7 @@ m4_include([debugspec.at])
 AT_BANNER([IMAP IO])
 m4_include([imapio.at])
 
+m4_include([scantime.at])
+m4_include([strftime.at])
+
 m4_include([fsaf.at])
\ No newline at end of file
diff --git a/libmu_scm/mu_message.c b/libmu_scm/mu_message.c
index 4ee603e..14d8821 100644
--- a/libmu_scm/mu_message.c
+++ b/libmu_scm/mu_message.c
@@ -104,7 +104,7 @@ mu_scm_message_print (SCM message_smob, SCM port, 
scm_print_state * pstate)
        scm_puts ("UNKNOWN", port);
       
       if (mu_envelope_sget_date (env, &p) == 0
-          && mu_parse_ctime_date_time (&p, &tm, &tz) == 0)
+          && mu_scan_datetime (p, MU_DATETIME_FROM, &tm, &tz, NULL) == 0)
        {
          strftime (datebuf, sizeof (datebuf), "%a %b %e %H:%M", &tm);
          buffer = datebuf;
@@ -416,7 +416,7 @@ SCM_DEFINE_PUBLIC (scm_mu_message_get_envelope_date, 
"mu-message-get-envelope-da
   if (status)
     mu_scm_error (FUNC_NAME, status, "cannot get envelope date",
                  scm_list_1 (mesg));
-  status = mu_parse_ctime_date_time (&sdate, &tm, &tz);
+  status = mu_scan_datetime (sdate, MU_DATETIME_FROM, &tm, &tz, NULL);
   if (status)
     mu_scm_error (FUNC_NAME, status, "invalid envelope date",
                  scm_list_1 (scm_from_locale_string (sdate)));
diff --git a/libmu_sieve/actions.c b/libmu_sieve/actions.c
index 56bf2bf..c0a50d1 100644
--- a/libmu_sieve/actions.c
+++ b/libmu_sieve/actions.c
@@ -145,7 +145,6 @@ mime_create_reason (mu_mime_t mime, mu_message_t msg, const 
char *text)
   char *sender;
   mu_body_t body;
   mu_header_t hdr;
-  char datestr[80];
   static char *content_header =
     "Content-Type: text/plain;charset=" MU_SIEVE_CHARSET "\n"
     "Content-Transfer-Encoding: 8bit\n";
@@ -156,13 +155,14 @@ mime_create_reason (mu_mime_t mime, mu_message_t msg, 
const char *text)
 
   time (&t);
   tm = localtime (&t);
-  mu_strftime (datestr, sizeof datestr, "%a, %b %d %H:%M:%S %Y %Z", tm);
 
   mu_sieve_get_message_sender (msg, &sender);
 
-  mu_stream_printf (stream, 
-                   "The original message was received at %s from %s.\n",
-                   datestr, sender);
+  mu_c_streamftime (stream,
+                   "The original message was received at "
+                   "%a, %b %d %H:%M:%S %Y %Z", tm, NULL);
+  
+  mu_stream_printf (stream, " from %s.\n", sender);
   free (sender);
   mu_stream_printf (stream,
                    "Message was refused by recipient's mail filtering 
program.\n");
@@ -201,7 +201,7 @@ mime_create_ds (mu_mime_t mime, mu_message_t orig)
 
   mu_message_get_envelope (orig, &env);
   if (mu_envelope_sget_date (env, &p) == 0
-      && mu_parse_ctime_date_time (&p, &tm, &tz) == 0)
+      && mu_scan_datetime (p, MU_DATETIME_FROM, &tm, &tz, NULL) == 0)
     t = mu_tm2time (&tm, &tz);
   else
     /* Use local time instead */
diff --git a/libproto/imap/fetch.c b/libproto/imap/fetch.c
index 1b6a70a..01b4791 100644
--- a/libproto/imap/fetch.c
+++ b/libproto/imap/fetch.c
@@ -339,14 +339,13 @@ _date_mapper (union mu_imap_fetch_response *resp,
              struct imap_list_element *elt,
              struct parse_response_env *parse_env)
 {
-  const char *p;
   struct tm tm;
   struct mu_timezone tz;
   
   if (elt->type != imap_eltype_string)
     return MU_ERR_FAILURE;
-  p = elt->v.string;
-  if (mu_parse_imap_date_time (&p, &tm, &tz))
+  if (mu_scan_datetime (elt->v.string, MU_DATETIME_INTERNALDATE, &tm, &tz,
+                       NULL))
     return MU_ERR_FAILURE;
   resp->internaldate.tm = tm;
   resp->internaldate.tz = tz;
@@ -472,10 +471,10 @@ _fill_response (void *item, void *data)
        rc = MU_ERR_FAILURE;
       else
        {
-         char const *p = elt->v.string;
-         if (mu_parse_imap_date_time (&p,
-                                      &env->envelope->date,
-                                      &env->envelope->tz))
+         if (mu_scan_datetime (elt->v.string,
+                               MU_DATETIME_IMAP,
+                               &env->envelope->date,
+                               &env->envelope->tz, NULL))
            rc = MU_ERR_FAILURE;
          else
            rc = 0;
diff --git a/mail/from.c b/mail/from.c
index f3a2912..e4aeda2 100644
--- a/mail/from.c
+++ b/mail/from.c
@@ -213,7 +213,7 @@ hdr_date (struct header_call_args *args, void *data)
       
       mu_message_get_envelope (args->msg, &env);
       if (mu_envelope_sget_date (env, &p) == 0
-          && mu_parse_ctime_date_time (&p, &tm, &tz) == 0)
+          && mu_scan_datetime (p, MU_DATETIME_FROM, &tm, &tz, NULL) == 0)
        strftime (date, sizeof(date), "%a %b %e %H:%M", &tm);
     }
   return header_buf_string (args, date);


hooks/post-receive
-- 
GNU Mailutils



reply via email to

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