Index: trunk.strftime/src/port.c
===================================================================
--- trunk.strftime/src/port.c	(Revision 2364)
+++ trunk.strftime/src/port.c	(Arbeitskopie)
@@ -19,6 +19,7 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#include <locale.h>
 #include <time.h>
 
 #include "backend.h"
@@ -83,25 +84,33 @@
 
 /*-------------------------------------------------------------------------*/
 char *
-time_string (mp_int t)
+time_fstring (mp_int t, const char* str, Bool localized)
 
-/* Return a textual representation of the time <t>. */
-
+/* Return a textual representation of the time <t> according to the format
+ * string <str>. Doesn't cache because it would be necessary to 
+ * save the format string and compare.
+ * If localized is true, this function sets the locale according to the 
+ * environment variable before calling strftime and resets it afterwards.
+ * TODO: It would be nicer to allocate the result buffer dynamically
+ * TODO::for using longer format strings. */
 {
-    static char result[80];
-    struct tm *tm;
-    mp_int last_time = -1;
-
-    if (t != last_time)
-    {
-        time_t ti = (time_t)t;
-        last_time = t;
-        tm = localtime(&ti);
-        strftime(result, sizeof(result)-1, "%a %b %d %H:%M:%S %Y", tm);
+    static char result[512];
+    struct tm *tm; // broken-down time struct
+    
+    time_t ti = (time_t)t;
+    tm = localtime(&ti);
+    if (!localized) {
+        setlocale(LC_TIME, "C");
+        strftime(result, sizeof(result)-1, str, tm);
+        setlocale(LC_TIME, "");
     }
+    else
+        strftime(result, sizeof(result)-1, str, tm);
+    
     return result;
-} /* time_string() */
+} /* time_fstring() */
 
+
 /*-------------------------------------------------------------------------*/
 char *
 utime_string (mp_int t, mp_int ut)
@@ -112,18 +121,13 @@
     static char result[80];
     struct tm *tm;
     size_t len;
-    mp_int last_t = -1, last_ut = -1;
 
-    if (t != last_t || ut != last_ut)
-    {
-        time_t ti = (time_t)t;
-        last_t= t;
-        last_ut= ut;
-        tm = localtime(&ti);
-        len = strftime(result, sizeof(result)-1, "%a %b %d %H:%M:%S:", tm);
-        sprintf(result+len, "%06ld", ut);
-        strftime(result+len+6, sizeof(result)-7-len, " %Y", tm);
-    }
+    time_t ti = (time_t)t;
+    tm = localtime(&ti);
+    len = strftime(result, sizeof(result)-1, "%a %b %d %H:%M:%S:", tm);
+    sprintf(result+len, "%06ld", ut);
+    strftime(result+len+6, sizeof(result)-7-len, " %Y", tm);
+    
     return result;
 } /* utime_string() */
 
Index: trunk.strftime/src/port.h
===================================================================
--- trunk.strftime/src/port.h	(Revision 2364)
+++ trunk.strftime/src/port.h	(Arbeitskopie)
@@ -427,7 +427,7 @@
 
 extern char current_time_stamp[];
 extern mp_int get_current_time(void);
-extern char * time_string(mp_int);
+extern char * time_fstring(mp_int t, const char* str, Bool localized);
 extern char * utime_string(mp_int, mp_int);
 extern char * time_stamp(void);
 extern char *xmemmem(const char *, size_t, const char *, size_t);
Index: trunk.strftime/src/efuns.c
===================================================================
--- trunk.strftime/src/efuns.c	(Revision 2364)
+++ trunk.strftime/src/efuns.c	(Arbeitskopie)
@@ -107,6 +107,7 @@
 #include "comm.h"
 #include "dumpstat.h"
 #include "exec.h"
+#include "gcollect.h"
 #include "heartbeat.h"
 #include "interpret.h"
 #include "lex.h"
@@ -141,6 +142,11 @@
 #include "../mudlib/sys/strings.h"
 #include "../mudlib/sys/time.h"
 
+/* Variables */
+string_t *last_ctime_result = NULL;
+  /* points to the result of the last f_ctime() call. If the caller asks for 
+   * the same timestamp, it will be returned. */
+
 /* Forward declarations */
 static void copy_svalue (svalue_t *dest, svalue_t *, struct pointer_table *, int);
 
@@ -8626,18 +8632,24 @@
  *
  * Interpret the argument clock as number of seconds since Jan,
  * 1st, 1970, 0.00 and convert it to a nice date and time string.
+ * In this case, the result string will be cached and tabled.
  *
  * Alternatively, accept an array of two ints: the first is <clock>
  * value as in the first form, the second int is the number of
  * microseconds elapsed in the current second.
+ * In this case the result will not be cached as the value is very
+ * unlikely to be the same in 2 consecutive calls.
  */
 
 {
     char *ts, *cp;
     string_t *rc;
+    
+    static mp_int last_time = -1;  // letzte Uhrzeit
 
     if (sp->type != T_NUMBER)
     {
+      /* utime case */
         if (VEC_SIZE(sp->u.vec) != 2)
             errorf("Bad arg 1 to ctime(): Invalid array size %ld, expected 2.\n"
                  , (long)VEC_SIZE(sp->u.vec));
@@ -8647,28 +8659,65 @@
         if (sp->u.vec->item[1].type != T_NUMBER)
             errorf("Bad arg 1 to ctime(): Element 1 is '%s', expected 'int'.\n"
                  , efun_arg_typename(sp->u.vec->item[1].type));
+        
         ts = utime_string( sp->u.vec->item[0].u.number
                          , sp->u.vec->item[1].u.number);
+        
+        /* If the string contains nl characters, extract the substring
+         * before the first one. Else just copy the (volatile) result
+         * we got.
+         */
+        cp = strchr(ts, '\n');
+        if (cp)
+        {
+            int len = cp - ts;
+            memsafe(rc = new_n_mstring(ts, len), len, "ctime() result");
+        }
+        else
+        {
+            memsafe(rc = new_mstring(ts), strlen(ts), "ctime() result");
+        }
     }
     else
     {
-        ts = time_string(sp->u.number);
-    }
-
-    /* If the string contains nl characters, extract the substring
-     * before the first one. Else just copy the (volatile) result
-     * we got.
-     */
-    cp = strchr(ts, '\n');
-    if (cp)
-    {
-        int len = cp - ts;
-        memsafe(rc = new_n_mstring(ts, len), len, "ctime() result");
-    }
-    else
-    {
-        memsafe(rc = new_mstring(ts), strlen(ts), "ctime() result");
-    }
+      /* second-precision case */
+        // test if string for this time is cached
+        if (last_time != sp->u.number)
+        {
+          /* cache is outdated */
+            ts = time_fstring(sp->u.number, "%a %b %d %H:%M:%S %Y", 0);
+            
+            /* If the string contains nl characters, extract the substring
+             * before the first one. Else just copy the (volatile) result
+             * we got.
+             * Table strings, because they are probably used more then once. 
+             */
+            cp = strchr(ts, '\n');
+            if (cp)
+            {
+                int len = cp - ts;
+                memsafe(rc = new_n_tabled(ts, len), len,
+                        "ctime() result");
+            }
+            else
+            {
+                memsafe(rc = new_tabled(ts), strlen(ts),
+                        "ctime() result");
+            }
+            /* fill cache, free last (invalid) string first and don't forget 
+             * to increase the ref count for the cache. */
+            free_mstring(last_ctime_result);
+            last_ctime_result = rc;
+            ref_mstring(rc);
+            last_time = sp->u.number;
+        }
+        else {
+            // return last result (and increase ref count)
+            rc = last_ctime_result;
+            ref_mstring(rc);
+        }
+    }  // if (sp->type != T_NUMBER)
+    
     free_svalue(sp);
     put_string(sp, rc);
     return sp;
@@ -8734,5 +8783,108 @@
     return sp;
 } /* f_utime() */
 
+/*-------------------------------------------------------------------------*/
+svalue_t *
+v_strftime(svalue_t *sp, int num_arg)
+/* EFUN strftime()
+ *
+ *   string strftime()
+ *   string strftime(string fmt)
+ *   string strftime(int clock)
+ *   string strftime(string fmt, int clock)
+ *   string strftime(string fmt, int clock, int localized)
+ *
+ * Interpret the argument clock as number of seconds since Jan,
+ * 1st, 1970, 0.00 and convert it to a nice date and time string.
+ * The formatstring must be given in fmt and may contain the placeholders
+ * defined in 'man 3 strftime'.
+ * If localized == MY_TRUE then the time string will be created with the
+ * locale set in the environment variable LC_TIME
+ * Defaults: fmt="%c", clock=current_time, localized=MY_TRUE
+ * NOTE: the returned string will have at most 511 Characters.
+ * TODO: Implement proper caching of the result.
+ * TODO: instead of using pxalloc, efuns.c may implement support functions 
+ * TODO::for the GC, which mark the appropriate blocks as referenced.
+ */
+
+{
+    char *ts;
+    string_t *rc = NULL;  // ergebnisstring
+    
+    /* Begin of arguments on the stack */
+    svalue_t *arg = sp - num_arg + 1;
+    
+    // defaults:
+    Bool localized = MY_TRUE;
+    mp_int clk = current_time;
+    char *cfmt = "%c";
+    
+    // evaluate arguments
+    switch(num_arg) {
+        case 3:
+            localized = (Bool)arg[2].u.number;
+            // fall-through
+        case 2:
+            if (arg[1].u.number < 0)
+                errorf("Bad arg 2 to strftime(): got %ld, expected 0 .. %ld\n",
+                        arg[1].u.number, PINT_MAX);
+            clk = arg[1].u.number;
+            // fall-through
+        case 1:
+            // empty strings default to "%c" => only set fmt if non-empty
+            if (arg[0].type == T_STRING && mstrsize(arg[0].u.str)) {
+                cfmt = get_txt(arg[0].u.str);
+            }
+            else if (arg[0].type == T_NUMBER) {
+                if (num_arg>1) // bei > 1 argument nur strings erlaubt
+                    vefun_exp_arg_error(1, TF_STRING, sp->type, sp);
+                else if (arg[0].u.number >= 0)
+                    clk = arg[0].u.number;
+                else
+                    errorf("Bad argument 1 to strftime(): got %ld, expected 0 .. %ld\n",
+                        arg[0].u.number, PINT_MAX);
+            }
+            break;
+    }
+
+    ts = time_fstring(clk,cfmt,localized); 
+    memsafe(rc = new_tabled(ts), strlen(ts)+sizeof(string_t), "strftime() result");
+    
+    sp = pop_n_elems(num_arg, sp);
+    push_string(sp, rc);
+    
+    return sp;
+} /* f_strftime() */
+
 /***************************************************************************/
 
+/*-------------------------------------------------------------------------*/
+#ifdef GC_SUPPORT
+
+void
+clear_ref_from_efuns (void)
+
+/* GC support: Clear the refs for the memory containing the (ctime) cache.
+ */
+
+{
+  if (last_ctime_result)
+      clear_string_ref(last_ctime_result);
+} /* clear_ref_from_efuns() */
+
+/*-------------------------------------------------------------------------*/
+void
+count_ref_from_efuns (void)
+
+/* GC support: Count the refs for the memory containing the (ctime) cache.
+ */
+
+{
+  if (last_ctime_result)
+      count_ref_from_string(last_ctime_result);
+} /* count_ref_from_wiz_list() */
+
+#endif /* GC_SUPPORT */
+
+/*-------------------------------------------------------------------------*/
+
Index: trunk.strftime/src/main.c
===================================================================
--- trunk.strftime/src/main.c	(Revision 2364)
+++ trunk.strftime/src/main.c	(Arbeitskopie)
@@ -267,6 +267,7 @@
 
     boot_time = (mp_int)time(NULL);
     setlocale(LC_CTYPE, ""); /* Use the locale defined in the LANG env var */
+    setlocale(LC_TIME, "");
     get_stack_direction();
     mb_init();
     init_interpret();
Index: trunk.strftime/src/efuns.h
===================================================================
--- trunk.strftime/src/efuns.h	(Revision 2364)
+++ trunk.strftime/src/efuns.h	(Arbeitskopie)
@@ -82,6 +82,7 @@
 extern svalue_t *tell_room(svalue_t *sp);
 
 extern svalue_t *f_ctime(svalue_t *);
+extern svalue_t *v_strftime(svalue_t *, int num_arg);
 extern svalue_t *v_debug_info(svalue_t *sp, int num_arg);
 extern svalue_t *f_rusage(svalue_t *sp);
 extern svalue_t *f_random(svalue_t *);
Index: trunk.strftime/src/func_spec
===================================================================
--- trunk.strftime/src/func_spec	(Revision 2364)
+++ trunk.strftime/src/func_spec	(Arbeitskopie)
@@ -373,6 +373,7 @@
 mixed   restore_value(string);
 
 string  ctime(int*|int default: F_TIME);
+string  strftime(string|int|void, int|void, int|void);
 int     random(int);
 int     time();
 int*    utime();
Index: trunk.strftime/src/gcollect.c
===================================================================
--- trunk.strftime/src/gcollect.c	(Revision 2364)
+++ trunk.strftime/src/gcollect.c	(Arbeitskopie)
@@ -2061,6 +2061,7 @@
     mstring_clear_refs();
     clear_ref_from_wiz_list();
     clear_ref_from_call_outs();
+    clear_ref_from_efuns();
 #if defined(USE_PARSE_COMMAND)
     clear_parse_refs();
 #endif
@@ -2268,6 +2269,7 @@
 
     count_ref_from_wiz_list();
     count_ref_from_call_outs();
+    count_ref_from_efuns();
 
     if (master_ob)
         master_ob->ref++;
