Code indentation.
[midnight-commander.git] / src / vfs / smbfs / helpers / lib / time.c
blob2c236a5662e9d401c8414e102b67304263b9a63d
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 time handling functions
6 Copyright (C) Andrew Tridgell 1992-1998
8 Copyright (C) 2011
9 The Free Software Foundation, Inc.
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "includes.h"
30 This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
31 in May 1996
35 int serverzone = 0;
36 int extra_time_offset = 0;
38 extern int DEBUGLEVEL;
40 #ifndef CHAR_BIT
41 #define CHAR_BIT 8
42 #endif
44 #ifndef TIME_T_MIN
45 #define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
46 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
47 #endif
48 #ifndef TIME_T_MAX
49 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
50 #endif
54 /*******************************************************************
55 a gettimeofday wrapper
56 ********************************************************************/
57 void
58 GetTimeOfDay (struct timeval *tval)
60 #ifdef HAVE_GETTIMEOFDAY_TZ
61 gettimeofday (tval, NULL);
62 #else
63 gettimeofday (tval);
64 #endif
67 #define TM_YEAR_BASE 1900
69 /*******************************************************************
70 yield the difference between *A and *B, in seconds, ignoring leap seconds
71 ********************************************************************/
72 static int
73 tm_diff (struct tm *a, struct tm *b)
75 int ay = a->tm_year + (TM_YEAR_BASE - 1);
76 int by = b->tm_year + (TM_YEAR_BASE - 1);
77 int intervening_leap_days = (ay / 4 - by / 4) - (ay / 100 - by / 100) + (ay / 400 - by / 400);
78 int years = ay - by;
79 int days = 365 * years + intervening_leap_days + (a->tm_yday - b->tm_yday);
80 int hours = 24 * days + (a->tm_hour - b->tm_hour);
81 int minutes = 60 * hours + (a->tm_min - b->tm_min);
82 int seconds = 60 * minutes + (a->tm_sec - b->tm_sec);
84 return seconds;
87 /*******************************************************************
88 return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
89 ******************************************************************/
90 static int
91 TimeZone (time_t t)
93 struct tm *tm = gmtime (&t);
94 struct tm tm_utc;
95 if (!tm)
96 return 0;
97 tm_utc = *tm;
98 tm = localtime (&t);
99 if (!tm)
100 return 0;
101 return tm_diff (&tm_utc, tm);
106 /*******************************************************************
107 init the time differences
108 ********************************************************************/
109 void
110 TimeInit (void)
112 serverzone = TimeZone (time (NULL));
114 if ((serverzone % 60) != 0)
116 DEBUG (1, ("WARNING: Your timezone is not a multiple of 1 minute.\n"));
119 DEBUG (4, ("Serverzone is %d\n", serverzone));
123 /*******************************************************************
124 return the same value as TimeZone, but it should be more efficient.
126 We keep a table of DST offsets to prevent calling localtime() on each
127 call of this function. This saves a LOT of time on many unixes.
129 Updated by Paul Eggert <eggert@twinsun.com>
130 ********************************************************************/
131 static int
132 TimeZoneFaster (time_t t)
134 static struct dst_table
136 time_t start, end;
137 int zone;
138 } *dst_table = NULL;
139 static int table_size = 0;
140 int i;
141 int zone = 0;
143 if (t == 0)
144 t = time (NULL);
146 /* Tunis has a 8 day DST region, we need to be careful ... */
147 #define MAX_DST_WIDTH (365*24*60*60)
148 #define MAX_DST_SKIP (7*24*60*60)
150 for (i = 0; i < table_size; i++)
151 if (t >= dst_table[i].start && t <= dst_table[i].end)
152 break;
154 if (i < table_size)
156 zone = dst_table[i].zone;
158 else
160 time_t low, high;
162 zone = TimeZone (t);
163 dst_table = (struct dst_table *) Realloc (dst_table, sizeof (dst_table[0]) * (i + 1));
164 if (!dst_table)
166 table_size = 0;
168 else
170 table_size++;
172 dst_table[i].zone = zone;
173 dst_table[i].start = dst_table[i].end = t;
175 /* no entry will cover more than 6 months */
176 low = t - MAX_DST_WIDTH / 2;
177 if (t < low)
178 low = TIME_T_MIN;
180 high = t + MAX_DST_WIDTH / 2;
181 if (high < t)
182 high = TIME_T_MAX;
184 /* widen the new entry using two bisection searches */
185 while (low + 60 * 60 < dst_table[i].start)
187 if (dst_table[i].start - low > MAX_DST_SKIP * 2)
188 t = dst_table[i].start - MAX_DST_SKIP;
189 else
190 t = low + (dst_table[i].start - low) / 2;
191 if (TimeZone (t) == zone)
192 dst_table[i].start = t;
193 else
194 low = t;
197 while (high - 60 * 60 > dst_table[i].end)
199 if (high - dst_table[i].end > MAX_DST_SKIP * 2)
200 t = dst_table[i].end + MAX_DST_SKIP;
201 else
202 t = high - (high - dst_table[i].end) / 2;
203 if (TimeZone (t) == zone)
204 dst_table[i].end = t;
205 else
206 high = t;
208 #if 0
209 DEBUG (1, ("Added DST entry from %s ", asctime (localtime (&dst_table[i].start))));
210 DEBUG (1, ("to %s (%d)\n", asctime (localtime (&dst_table[i].end)), dst_table[i].zone));
211 #endif
214 return zone;
217 /****************************************************************************
218 return the UTC offset in seconds west of UTC, adjusted for extra time offset
219 **************************************************************************/
221 TimeDiff (time_t t)
223 return TimeZoneFaster (t) + 60 * extra_time_offset;
227 /****************************************************************************
228 return the UTC offset in seconds west of UTC, adjusted for extra time
229 offset, for a local time value. If ut = lt + LocTimeDiff(lt), then
230 lt = ut - TimeDiff(ut), but the converse does not necessarily hold near
231 daylight savings transitions because some local times are ambiguous.
232 LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions.
233 +**************************************************************************/
234 static int
235 LocTimeDiff (time_t lte)
237 time_t lt = lte - 60 * extra_time_offset;
238 int d = TimeZoneFaster (lt);
239 time_t t = lt + d;
241 /* if overflow occurred, ignore all the adjustments so far */
242 if (((lte < lt) ^ (extra_time_offset < 0)) | ((t < lt) ^ (d < 0)))
243 t = lte;
245 /* now t should be close enough to the true UTC to yield the right answer */
246 return TimeDiff (t);
250 /****************************************************************************
251 try to optimise the localtime call, it can be quite expensive on some machines
252 ****************************************************************************/
253 struct tm *
254 LocalTime (time_t * t)
256 time_t t2 = *t;
258 t2 -= TimeDiff (t2);
260 return (gmtime (&t2));
263 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
265 /****************************************************************************
266 interpret an 8 byte "filetime" structure to a time_t
267 It's originally in "100ns units since jan 1st 1601"
269 It appears to be kludge-GMT (at least for file listings). This means
270 its the GMT you get by taking a localtime and adding the
271 serverzone. This is NOT the same as GMT in some cases. This routine
272 converts this to real GMT.
273 ****************************************************************************/
274 time_t
275 nt_time_to_unix (NTTIME * nt)
277 double d;
278 time_t ret;
279 /* The next two lines are a fix needed for the
280 broken SCO compiler. JRA. */
281 time_t l_time_min = TIME_T_MIN;
282 time_t l_time_max = TIME_T_MAX;
284 if (nt->high == 0)
285 return (0);
287 d = ((double) nt->high) * 4.0 * (double) (1 << 30);
288 d += (nt->low & 0xFFF00000);
289 d *= 1.0e-7;
291 /* now adjust by 369 years to make the secs since 1970 */
292 d -= TIME_FIXUP_CONSTANT;
294 if (!(l_time_min <= d && d <= l_time_max))
295 return (0);
297 ret = (time_t) (d + 0.5);
299 /* this takes us from kludge-GMT to real GMT */
300 ret -= serverzone;
301 ret += LocTimeDiff (ret);
303 return (ret);
308 /****************************************************************************
309 interprets an nt time into a unix time_t
310 ****************************************************************************/
311 time_t
312 interpret_long_date (char *p)
314 NTTIME nt;
315 nt.low = IVAL (p, 0);
316 nt.high = IVAL (p, 4);
317 return nt_time_to_unix (&nt);
320 /****************************************************************************
321 put a 8 byte filetime from a time_t
322 This takes real GMT as input and converts to kludge-GMT
323 ****************************************************************************/
324 void
325 unix_to_nt_time (NTTIME * nt, time_t t)
327 double d;
329 if (t == 0)
331 nt->low = 0;
332 nt->high = 0;
333 return;
336 /* this converts GMT to kludge-GMT */
337 t -= LocTimeDiff (t) - serverzone;
339 d = (double) (t);
340 d += TIME_FIXUP_CONSTANT;
341 d *= 1.0e7;
343 nt->high = (uint32) (d * (1.0 / (4.0 * (double) (1 << 30))));
344 nt->low = (uint32) (d - ((double) nt->high) * 4.0 * (double) (1 << 30));
348 /****************************************************************************
349 take an NTTIME structure, containing high / low time. convert to unix time.
350 lkclXXXX this may need 2 SIVALs not a memcpy. we'll see...
351 ****************************************************************************/
352 void
353 put_long_date (char *p, time_t t)
355 NTTIME nt;
356 unix_to_nt_time (&nt, t);
357 SIVAL (p, 0, nt.low);
358 SIVAL (p, 4, nt.high);
361 /****************************************************************************
362 check if it's a null mtime
363 ****************************************************************************/
364 BOOL
365 null_mtime (time_t mtime)
367 if (mtime == (time_t) 0 || mtime == (time_t) 0xFFFFFFFF || mtime == (time_t) - 1)
368 return (True);
369 return (False);
372 /*******************************************************************
373 create a 16 bit dos packed date
374 ********************************************************************/
375 static uint16
376 make_dos_date1 (struct tm *t)
378 uint16 ret = 0;
379 ret = (((unsigned) (t->tm_mon + 1)) >> 3) | ((t->tm_year - 80) << 1);
380 ret = ((ret & 0xFF) << 8) | (t->tm_mday | (((t->tm_mon + 1) & 0x7) << 5));
381 return (ret);
384 /*******************************************************************
385 create a 16 bit dos packed time
386 ********************************************************************/
387 static uint16
388 make_dos_time1 (struct tm *t)
390 uint16 ret = 0;
391 ret = ((((unsigned) t->tm_min >> 3) & 0x7) | (((unsigned) t->tm_hour) << 3));
392 ret = ((ret & 0xFF) << 8) | ((t->tm_sec / 2) | ((t->tm_min & 0x7) << 5));
393 return (ret);
396 /*******************************************************************
397 create a 32 bit dos packed date/time from some parameters
398 This takes a GMT time and returns a packed localtime structure
399 ********************************************************************/
400 static uint32
401 make_dos_date (time_t unixdate)
403 struct tm *t;
404 uint32 ret = 0;
406 t = LocalTime (&unixdate);
407 if (!t)
408 return 0xFFFFFFFF;
410 ret = make_dos_date1 (t);
411 ret = ((ret & 0xFFFF) << 16) | make_dos_time1 (t);
413 return (ret);
416 /*******************************************************************
417 put a dos date into a buffer (time/date format)
418 This takes GMT time and puts local time in the buffer
419 ********************************************************************/
420 void
421 put_dos_date (char *buf, int offset, time_t unixdate)
423 uint32 x = make_dos_date (unixdate);
424 SIVAL (buf, offset, x);
427 /*******************************************************************
428 put a dos date into a buffer (date/time format)
429 This takes GMT time and puts local time in the buffer
430 ********************************************************************/
431 void
432 put_dos_date2 (char *buf, int offset, time_t unixdate)
434 uint32 x = make_dos_date (unixdate);
435 x = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16);
436 SIVAL (buf, offset, x);
439 /*******************************************************************
440 put a dos 32 bit "unix like" date into a buffer. This routine takes
441 GMT and converts it to LOCAL time before putting it (most SMBs assume
442 localtime for this sort of date)
443 ********************************************************************/
444 void
445 put_dos_date3 (char *buf, int offset, time_t unixdate)
447 if (!null_mtime (unixdate))
448 unixdate -= TimeDiff (unixdate);
449 SIVAL (buf, offset, unixdate);
452 /*******************************************************************
453 interpret a 32 bit dos packed date/time to some parameters
454 ********************************************************************/
455 static void
456 interpret_dos_date (uint32 date, int *year, int *month, int *day, int *hour, int *minute,
457 int *second)
459 uint32 p0, p1, p2, p3;
461 p0 = date & 0xFF;
462 p1 = ((date & 0xFF00) >> 8) & 0xFF;
463 p2 = ((date & 0xFF0000) >> 16) & 0xFF;
464 p3 = ((date & 0xFF000000) >> 24) & 0xFF;
466 *second = 2 * (p0 & 0x1F);
467 *minute = ((p0 >> 5) & 0xFF) + ((p1 & 0x7) << 3);
468 *hour = (p1 >> 3) & 0xFF;
469 *day = (p2 & 0x1F);
470 *month = ((p2 >> 5) & 0xFF) + ((p3 & 0x1) << 3) - 1;
471 *year = ((p3 >> 1) & 0xFF) + 80;
474 /*******************************************************************
475 create a unix date (int GMT) from a dos date (which is actually in
476 localtime)
477 ********************************************************************/
478 time_t
479 make_unix_date (void *date_ptr)
481 uint32 dos_date = 0;
482 struct tm t;
483 time_t ret;
485 dos_date = IVAL (date_ptr, 0);
487 if (dos_date == 0)
488 return (0);
490 interpret_dos_date (dos_date, &t.tm_year, &t.tm_mon,
491 &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
492 t.tm_isdst = -1;
494 /* mktime() also does the local to GMT time conversion for us */
495 ret = mktime (&t);
497 return (ret);
500 /*******************************************************************
501 like make_unix_date() but the words are reversed
502 ********************************************************************/
503 time_t
504 make_unix_date2 (void *date_ptr)
506 uint32 x, x2;
508 x = IVAL (date_ptr, 0);
509 x2 = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16);
510 SIVAL (&x, 0, x2);
512 return (make_unix_date ((void *) &x));
515 /*******************************************************************
516 create a unix GMT date from a dos date in 32 bit "unix like" format
517 these generally arrive as localtimes, with corresponding DST
518 ******************************************************************/
519 time_t
520 make_unix_date3 (void *date_ptr)
522 time_t t = (time_t) IVAL (date_ptr, 0);
523 if (!null_mtime (t))
524 t += LocTimeDiff (t);
525 return (t);
528 #if 0
529 /***************************************************************************
530 return a HTTP/1.0 time string
531 ***************************************************************************/
532 char *
533 http_timestring (time_t t)
535 static fstring buf;
536 struct tm *tm = LocalTime (&t);
538 if (!tm)
539 slprintf (buf, sizeof (buf) - 1, "%ld seconds since the Epoch", (long) t);
540 else
541 strftime (buf, sizeof (buf) - 1, "%a, %d %b %Y %H:%M:%S %Z", tm);
542 return buf;
544 #endif /*0 */
547 /****************************************************************************
548 return the date and time as a string
549 ****************************************************************************/
550 char *
551 timestring (void)
553 static fstring TimeBuf;
554 time_t t = time (NULL);
555 struct tm *tm = LocalTime (&t);
557 if (!tm)
559 slprintf (TimeBuf, sizeof (TimeBuf) - 1, "%ld seconds since the Epoch", (long) t);
561 else
563 strftime (TimeBuf, 100, "%Y/%m/%d %H:%M:%S", tm);
565 return (TimeBuf);
568 /****************************************************************************
569 return the best approximation to a 'create time' under UNIX from a stat
570 structure.
571 ****************************************************************************/
573 time_t
574 get_create_time (SMB_STRUCT_STAT * st, BOOL fake_dirs)
576 time_t ret, ret1;
578 if (S_ISDIR (st->st_mode) && fake_dirs)
579 return (time_t) 315493200L; /* 1/1/1980 */
581 ret = MIN (st->st_ctime, st->st_mtime);
582 ret1 = MIN (ret, st->st_atime);
584 if (ret1 != (time_t) 0)
585 return ret1;
588 * One of ctime, mtime or atime was zero (probably atime).
589 * Just return MIN(ctime, mtime).
591 return ret;