Round and round we go....
[ira/wip.git] / source3 / lib / time.c
index 215c28a2108d93c43e08140c75c64a45823b12fa..c39753403d707dc9d1a7614bce35a24931323941 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    time handling functions
-   Copyright (C) Andrew Tridgell 1992-1995
+   Copyright (C) Andrew Tridgell 1992-1998
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
   */
 
 
-int serverzone=0;
 int extra_time_offset = 0;
 
-extern int DEBUGLEVEL;
-
 #ifndef CHAR_BIT
 #define CHAR_BIT 8
 #endif
@@ -44,17 +41,29 @@ extern int DEBUGLEVEL;
 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
 #endif
 
+/*******************************************************************
+ External access to time_t_min and time_t_max.
+********************************************************************/
 
+time_t get_time_t_min(void)
+{
+       return TIME_T_MIN;
+}
+
+time_t get_time_t_max(void)
+{
+       return TIME_T_MAX;
+}
 
 /*******************************************************************
 a gettimeofday wrapper
 ********************************************************************/
 void GetTimeOfDay(struct timeval *tval)
 {
-#ifdef GETTIMEOFDAY1
-  gettimeofday(tval);
+#ifdef HAVE_GETTIMEOFDAY_TZ
+       gettimeofday(tval,NULL);
 #else
-  gettimeofday(tval,NULL);
+       gettimeofday(tval);
 #endif
 }
 
@@ -74,28 +83,82 @@ static int tm_diff(struct tm *a, struct tm *b)
   int hours = 24*days + (a->tm_hour - b->tm_hour);
   int minutes = 60*hours + (a->tm_min - b->tm_min);
   int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
+
   return seconds;
 }
 
 /*******************************************************************
-  return the UTC offset in seconds west of UTC
+  return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
   ******************************************************************/
 static int TimeZone(time_t t)
 {
-  struct tm tm_utc = *(gmtime(&t));
-  return tm_diff(&tm_utc,localtime(&t));
+  struct tm *tm = gmtime(&t);
+  struct tm tm_utc;
+  if (!tm)
+    return 0;
+  tm_utc = *tm;
+  tm = localtime(&t);
+  if (!tm)
+    return 0;
+  return tm_diff(&tm_utc,tm);
+
 }
 
+static BOOL done_serverzone_init;
+
+/* Return the smb serverzone value */
+
+static int get_serverzone(void)
+{
+        static int serverzone;
+
+        if (!done_serverzone_init) {
+                serverzone = TimeZone(time(NULL));
+
+                if ((serverzone % 60) != 0) {
+                        DEBUG(1,("WARNING: Your timezone is not a multiple of 1 minute.\n"));
+                }
+
+                DEBUG(4,("Serverzone is %d\n",serverzone));
+
+                done_serverzone_init = True;
+        }
+
+        return serverzone;
+}
+
+/* Re-read the smb serverzone value */
+
+static struct timeval start_time_hires;
 
-/*******************************************************************
-init the time differences
-********************************************************************/
 void TimeInit(void)
 {
-  serverzone = TimeZone(time(NULL));
-  DEBUG(4,("Serverzone is %d\n",serverzone));
+       done_serverzone_init = False;
+       get_serverzone();
+       /* Save the start time of this process. */
+       if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0)
+               GetTimeOfDay(&start_time_hires);
 }
 
+/**********************************************************************
+ Return a timeval struct of the uptime of this process. As TimeInit is
+ done before a daemon fork then this is the start time from the parent
+ daemon start. JRA.
+***********************************************************************/
+
+void get_process_uptime(struct timeval *ret_time)
+{
+       struct timeval time_now_hires;
+
+       GetTimeOfDay(&time_now_hires);
+       ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec;
+       ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+       if (time_now_hires.tv_usec < start_time_hires.tv_usec) {
+               ret_time->tv_sec -= 1;
+               ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec);
+       } else
+               ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec;
+}
 
 /*******************************************************************
 return the same value as TimeZone, but it should be more efficient.
@@ -107,7 +170,7 @@ Updated by Paul Eggert <eggert@twinsun.com>
 ********************************************************************/
 static int TimeZoneFaster(time_t t)
 {
-  static struct dst_table {time_t start,end; int zone;} *dst_table = NULL;
+  static struct dst_table {time_t start,end; int zone;} *tdt, *dst_table = NULL;
   static int table_size = 0;
   int i;
   int zone = 0;
@@ -127,11 +190,14 @@ static int TimeZoneFaster(time_t t)
     time_t low,high;
 
     zone = TimeZone(t);
-    dst_table = (struct dst_table *)Realloc(dst_table,
+    tdt = (struct dst_table *)Realloc(dst_table,
                                              sizeof(dst_table[0])*(i+1));
-    if (!dst_table) {
+    if (!tdt) {
+      DEBUG(0,("TimeZoneFaster: out of memory!\n"));
+      SAFE_FREE(dst_table);
       table_size = 0;
     } else {
+      dst_table = tdt;
       table_size++;
 
       dst_table[i].zone = zone; 
@@ -211,7 +277,7 @@ static int LocTimeDiff(time_t lte)
 
 
 /****************************************************************************
-try to optimise the localtime call, it can be quite expenive on some machines
+try to optimise the localtime call, it can be quite expensive on some machines
 ****************************************************************************/
 struct tm *LocalTime(time_t *t)
 {
@@ -222,7 +288,6 @@ struct tm *LocalTime(time_t *t)
   return(gmtime(&t2));
 }
 
-
 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
 
 /****************************************************************************
@@ -234,71 +299,192 @@ its the GMT you get by taking a localtime and adding the
 serverzone. This is NOT the same as GMT in some cases. This routine
 converts this to real GMT.
 ****************************************************************************/
-time_t interpret_long_date(char *p)
+time_t nt_time_to_unix(NTTIME *nt)
 {
   double d;
   time_t ret;
-  uint32 tlow,thigh;
-  tlow = IVAL(p,0);
-  thigh = IVAL(p,4);
+  /* The next two lines are a fix needed for the 
+     broken SCO compiler. JRA. */
+  time_t l_time_min = TIME_T_MIN;
+  time_t l_time_max = TIME_T_MAX;
 
-  if (thigh == 0) return(0);
+  if (nt->high == 0) return(0);
 
-  d = ((double)thigh)*4.0*(double)(1<<30);
-  d += (tlow&0xFFF00000);
+  d = ((double)nt->high)*4.0*(double)(1<<30);
+  d += (nt->low&0xFFF00000);
   d *= 1.0e-7;
  
   /* now adjust by 369 years to make the secs since 1970 */
   d -= TIME_FIXUP_CONSTANT;
 
-  if (!(TIME_T_MIN <= d && d <= TIME_T_MAX))
+  if (!(l_time_min <= d && d <= l_time_max))
     return(0);
 
   ret = (time_t)(d+0.5);
 
   /* this takes us from kludge-GMT to real GMT */
-  ret -= serverzone;
+  ret -= get_serverzone();
   ret += LocTimeDiff(ret);
 
   return(ret);
 }
 
-
 /****************************************************************************
-put a 8 byte filetime from a time_t
-This takes real GMT as input and converts to kludge-GMT
+convert a NTTIME structure to a time_t
+It's originally in "100ns units"
+
+this is an absolute version of the one above.
+By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970
+if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM
 ****************************************************************************/
-void put_long_date(char *p,time_t t)
+time_t nt_time_to_unix_abs(NTTIME *nt)
 {
-  uint32 tlow,thigh;
-  double d;
+       double d;
+       time_t ret;
+       /* The next two lines are a fix needed for the 
+          broken SCO compiler. JRA. */
+       time_t l_time_min = TIME_T_MIN;
+       time_t l_time_max = TIME_T_MAX;
+
+       if (nt->high == 0)
+               return(0);
+
+       if (nt->high==0x80000000 && nt->low==0)
+               return -1;
+
+       /* reverse the time */
+       /* it's a negative value, turn it to positive */
+       nt->high=~nt->high;
+       nt->low=~nt->low;
+
+       d = ((double)nt->high)*4.0*(double)(1<<30);
+       d += (nt->low&0xFFF00000);
+       d *= 1.0e-7;
+  
+       if (!(l_time_min <= d && d <= l_time_max))
+               return(0);
 
-  if (t==0) {
-    SIVAL(p,0,0); SIVAL(p,4,0);
-    return;
-  }
+       ret = (time_t)(d+0.5);
+
+       /* this takes us from kludge-GMT to real GMT */
+       ret -= get_serverzone();
+       ret += LocTimeDiff(ret);
+
+       return(ret);
+}
 
-  /* this converts GMT to kludge-GMT */
-  t -= TimeDiff(t) - serverzone; 
 
-  d = (double) (t);
 
-  d += TIME_FIXUP_CONSTANT;
+/****************************************************************************
+interprets an nt time into a unix time_t
+****************************************************************************/
+time_t interpret_long_date(char *p)
+{
+       NTTIME nt;
+       nt.low = IVAL(p,0);
+       nt.high = IVAL(p,4);
+       return nt_time_to_unix(&nt);
+}
 
-  d *= 1.0e7;
+/****************************************************************************
+put a 8 byte filetime from a time_t
+This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void unix_to_nt_time(NTTIME *nt, time_t t)
+{
+       double d;
+
+       if (t==0)
+       {
+               nt->low = 0;
+               nt->high = 0;
+               return;
+       }
+       if (t == TIME_T_MAX)
+       {
+               nt->low = 0xffffffff;
+               nt->high = 0x7fffffff;
+               return;
+       }               
+       if (t == -1)
+       {
+               nt->low = 0xffffffff;
+               nt->high = 0xffffffff;
+               return;
+       }               
+
+       /* this converts GMT to kludge-GMT */
+       t -= LocTimeDiff(t) - get_serverzone(); 
+
+       d = (double)(t);
+       d += TIME_FIXUP_CONSTANT;
+       d *= 1.0e7;
+
+       nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+       nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
 
-  thigh = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
-  tlow = (uint32)(d - ((double)thigh)*4.0*(double)(1<<30));
+/****************************************************************************
+convert a time_t to a NTTIME structure
 
-  SIVAL(p,0,tlow);
-  SIVAL(p,4,thigh);
+this is an absolute version of the one above.
+By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601
+if the nttime_t was 5 seconds, the NTTIME is 5 seconds. JFM
+****************************************************************************/
+void unix_to_nt_time_abs(NTTIME *nt, time_t t)
+{
+       double d;
+
+       if (t==0) {
+               nt->low = 0;
+               nt->high = 0;
+               return;
+       }
+
+       if (t == TIME_T_MAX) {
+               nt->low = 0xffffffff;
+               nt->high = 0x7fffffff;
+               return;
+       }
+               
+       if (t == -1) {
+               /* that's what NT uses for infinite */
+               nt->low = 0x0;
+               nt->high = 0x80000000;
+               return;
+       }               
+
+       /* this converts GMT to kludge-GMT */
+       t -= LocTimeDiff(t) - get_serverzone(); 
+
+       d = (double)(t);
+       d *= 1.0e7;
+
+       nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+       nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+
+       /* convert to a negative value */
+       nt->high=~nt->high;
+       nt->low=~nt->low;
 }
 
 
+/****************************************************************************
+take an NTTIME structure, containing high / low time.  convert to unix time.
+lkclXXXX this may need 2 SIVALs not a memcpy.  we'll see...
+****************************************************************************/
+void put_long_date(char *p,time_t t)
+{
+       NTTIME nt;
+       unix_to_nt_time(&nt, t);
+       SIVAL(p, 0, nt.low);
+       SIVAL(p, 4, nt.high);
+}
+
 /****************************************************************************
 check if it's a null mtime
 ****************************************************************************/
-static BOOL null_mtime(time_t mtime)
+BOOL null_mtime(time_t mtime)
 {
   if (mtime == 0 || mtime == 0xFFFFFFFF || mtime == (time_t)-1)
     return(True);
@@ -308,7 +494,7 @@ static BOOL null_mtime(time_t mtime)
 /*******************************************************************
   create a 16 bit dos packed date
 ********************************************************************/
-static uint16 make_dos_date1(time_t unixdate,struct tm *t)
+static uint16 make_dos_date1(struct tm *t)
 {
   uint16 ret=0;
   ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1);
@@ -319,7 +505,7 @@ static uint16 make_dos_date1(time_t unixdate,struct tm *t)
 /*******************************************************************
   create a 16 bit dos packed time
 ********************************************************************/
-static uint16 make_dos_time1(time_t unixdate,struct tm *t)
+static uint16 make_dos_time1(struct tm *t)
 {
   uint16 ret=0;
   ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3));
@@ -337,9 +523,11 @@ static uint32 make_dos_date(time_t unixdate)
   uint32 ret=0;
 
   t = LocalTime(&unixdate);
+  if (!t)
+    return 0xFFFFFFFF;
 
-  ret = make_dos_date1(unixdate,t);
-  ret = ((ret&0xFFFF)<<16) | make_dos_time1(unixdate,t);
+  ret = make_dos_date1(t);
+  ret = ((ret&0xFFFF)<<16) | make_dos_time1(t);
 
   return(ret);
 }
@@ -439,57 +627,124 @@ time_t make_unix_date2(void *date_ptr)
   ******************************************************************/
 time_t make_unix_date3(void *date_ptr)
 {
-  time_t t = IVAL(date_ptr,0);
+  time_t t = (time_t)IVAL(date_ptr,0);
   if (!null_mtime(t))
     t += LocTimeDiff(t);
   return(t);
 }
 
-/****************************************************************************
-set the time on a file
-****************************************************************************/
-BOOL set_filetime(char *fname,time_t mtime)
-{  
-  struct utimbuf times;
-
-  if (null_mtime(mtime)) return(True);
 
-  times.modtime = times.actime = mtime;
+/***************************************************************************
+return a HTTP/1.0 time string
+  ***************************************************************************/
+char *http_timestring(time_t t)
+{
+  static fstring buf;
+  struct tm *tm = LocalTime(&t);
 
-  if (sys_utime(fname,&times)) {
-    DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
-  }
-    
-  return(True);
+  if (!tm)
+    slprintf(buf,sizeof(buf)-1,"%ld seconds since the Epoch",(long)t);
+  else
+#ifndef HAVE_STRFTIME
+  fstrcpy(buf, asctime(tm));
+  if(buf[strlen(buf)-1] == '\n')
+    buf[strlen(buf)-1] = 0;
+#else /* !HAVE_STRFTIME */
+  strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
+#endif /* !HAVE_STRFTIME */
+  return buf;
 }
 
 
+
 /****************************************************************************
 return the date and time as a string
Return the date and time as a string
 ****************************************************************************/
-char *timestring(void )
-{
-  static char TimeBuf[100];
-  time_t t = time(NULL);
-  struct tm *tm = LocalTime(&t);
 
-#ifdef NO_STRFTIME
-  strcpy(TimeBuf, asctime(tm));
-#elif defined(CLIX) || defined(CONVEX)
-  strftime(TimeBuf,100,"%m/%d/%y %I:%M:%S %p",tm);
-#elif defined(AMPM)
-  strftime(TimeBuf,100,"%D %r",tm);
-#elif defined(TZ_TIME)
-  {
-    int zone = TimeDiff(t);
-    int absZoneMinutes = (zone<0 ? -zone : zone) / 60;
-    size_t len = strftime(TimeBuf,sizeof(TimeBuf)-6,"%D %T",tm);
-    sprintf(TimeBuf+len," %c%02d%02d",
-           zone<0?'+':'-',absZoneMinutes/60,absZoneMinutes%60);
-  }
+char *timestring(BOOL hires)
+{
+       static fstring TimeBuf;
+       struct timeval tp;
+       time_t t;
+       struct tm *tm;
+
+       if (hires) {
+               GetTimeOfDay(&tp);
+               t = (time_t)tp.tv_sec;
+       } else {
+               t = time(NULL);
+       }
+       tm = LocalTime(&t);
+       if (!tm) {
+               if (hires) {
+                       slprintf(TimeBuf,
+                                sizeof(TimeBuf)-1,
+                                "%ld.%06ld seconds since the Epoch",
+                                (long)tp.tv_sec, 
+                                (long)tp.tv_usec);
+               } else {
+                       slprintf(TimeBuf,
+                                sizeof(TimeBuf)-1,
+                                "%ld seconds since the Epoch",
+                                (long)t);
+               }
+       } else {
+#ifdef HAVE_STRFTIME
+               if (hires) {
+                       strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm);
+                       slprintf(TimeBuf+strlen(TimeBuf),
+                                sizeof(TimeBuf)-1 - strlen(TimeBuf), 
+                                ".%06ld", 
+                                (long)tp.tv_usec);
+               } else {
+                       strftime(TimeBuf,100,"%Y/%m/%d %H:%M:%S",tm);
+               }
 #else
-  strftime(TimeBuf,100,"%D %T",tm);
+               if (hires) {
+                       slprintf(TimeBuf, 
+                                sizeof(TimeBuf)-1, 
+                                "%s.%06ld", 
+                                asctime(tm), 
+                                (long)tp.tv_usec);
+               } else {
+                       fstrcpy(TimeBuf, asctime(tm));
+               }
 #endif
-  return(TimeBuf);
+       }
+       return(TimeBuf);
 }
 
+/****************************************************************************
+  return the best approximation to a 'create time' under UNIX from a stat
+  structure.
+****************************************************************************/
+
+time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs)
+{
+  time_t ret, ret1;
+
+  if(S_ISDIR(st->st_mode) && fake_dirs)
+    return (time_t)315493200L;          /* 1/1/1980 */
+    
+  ret = MIN(st->st_ctime, st->st_mtime);
+  ret1 = MIN(ret, st->st_atime);
+
+  if(ret1 != (time_t)0)
+    return ret1;
+
+  /*
+   * One of ctime, mtime or atime was zero (probably atime).
+   * Just return MIN(ctime, mtime).
+   */
+  return ret;
+}
+
+/****************************************************************************
+initialise an NTTIME to -1, which means "unknown" or "don't expire"
+****************************************************************************/
+
+void init_nt_time(NTTIME *nt)
+{
+       nt->high = 0x7FFFFFFF;
+       nt->low = 0xFFFFFFFF;
+}