2 Unix SMB/Netbios implementation.
4 time handling functions
6 Copyright (C) Andrew Tridgell 1992-1998
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/>.
30 This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com>
36 int extra_time_offset
= 0;
38 extern int DEBUGLEVEL
;
45 #define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
46 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
49 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
54 /*******************************************************************
55 a gettimeofday wrapper
56 ********************************************************************/
58 GetTimeOfDay (struct timeval
*tval
)
60 #ifdef HAVE_GETTIMEOFDAY_TZ
61 gettimeofday (tval
, NULL
);
67 #define TM_YEAR_BASE 1900
69 /*******************************************************************
70 yield the difference between *A and *B, in seconds, ignoring leap seconds
71 ********************************************************************/
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);
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
);
87 /*******************************************************************
88 return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
89 ******************************************************************/
93 struct tm
*tm
= gmtime (&t
);
101 return tm_diff (&tm_utc
, tm
);
106 /*******************************************************************
107 init the time differences
108 ********************************************************************/
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 ********************************************************************/
132 TimeZoneFaster (time_t t
)
134 static struct dst_table
139 static int table_size
= 0;
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
)
156 zone
= dst_table
[i
].zone
;
163 dst_table
= (struct dst_table
*) Realloc (dst_table
, sizeof (dst_table
[0]) * (i
+ 1));
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;
180 high
= t
+ MAX_DST_WIDTH
/ 2;
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
;
190 t
= low
+ (dst_table
[i
].start
- low
) / 2;
191 if (TimeZone (t
) == zone
)
192 dst_table
[i
].start
= 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
;
202 t
= high
- (high
- dst_table
[i
].end
) / 2;
203 if (TimeZone (t
) == zone
)
204 dst_table
[i
].end
= t
;
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
));
217 /****************************************************************************
218 return the UTC offset in seconds west of UTC, adjusted for extra time offset
219 **************************************************************************/
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 +**************************************************************************/
235 LocTimeDiff (time_t lte
)
237 time_t lt
= lte
- 60 * extra_time_offset
;
238 int d
= TimeZoneFaster (lt
);
241 /* if overflow occurred, ignore all the adjustments so far */
242 if (((lte
< lt
) ^ (extra_time_offset
< 0)) | ((t
< lt
) ^ (d
< 0)))
245 /* now t should be close enough to the true UTC to yield the right answer */
250 /****************************************************************************
251 try to optimise the localtime call, it can be quite expensive on some machines
252 ****************************************************************************/
254 LocalTime (time_t * t
)
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 ****************************************************************************/
275 nt_time_to_unix (NTTIME
* nt
)
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
;
287 d
= ((double) nt
->high
) * 4.0 * (double) (1 << 30);
288 d
+= (nt
->low
& 0xFFF00000);
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
))
297 ret
= (time_t) (d
+ 0.5);
299 /* this takes us from kludge-GMT to real GMT */
301 ret
+= LocTimeDiff (ret
);
308 /****************************************************************************
309 interprets an nt time into a unix time_t
310 ****************************************************************************/
312 interpret_long_date (char *p
)
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 ****************************************************************************/
325 unix_to_nt_time (NTTIME
* nt
, time_t t
)
336 /* this converts GMT to kludge-GMT */
337 t
-= LocTimeDiff (t
) - serverzone
;
340 d
+= TIME_FIXUP_CONSTANT
;
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 ****************************************************************************/
353 put_long_date (char *p
, time_t t
)
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 ****************************************************************************/
365 null_mtime (time_t mtime
)
367 if (mtime
== (time_t) 0 || mtime
== (time_t) 0xFFFFFFFF || mtime
== (time_t) - 1)
372 /*******************************************************************
373 create a 16 bit dos packed date
374 ********************************************************************/
376 make_dos_date1 (struct tm
*t
)
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));
384 /*******************************************************************
385 create a 16 bit dos packed time
386 ********************************************************************/
388 make_dos_time1 (struct tm
*t
)
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));
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 ********************************************************************/
401 make_dos_date (time_t unixdate
)
406 t
= LocalTime (&unixdate
);
410 ret
= make_dos_date1 (t
);
411 ret
= ((ret
& 0xFFFF) << 16) | make_dos_time1 (t
);
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 ********************************************************************/
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 ********************************************************************/
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 ********************************************************************/
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 ********************************************************************/
456 interpret_dos_date (uint32 date
, int *year
, int *month
, int *day
, int *hour
, int *minute
,
459 uint32 p0
, p1
, p2
, p3
;
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;
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
477 ********************************************************************/
479 make_unix_date (void *date_ptr
)
485 dos_date
= IVAL (date_ptr
, 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
);
494 /* mktime() also does the local to GMT time conversion for us */
500 /*******************************************************************
501 like make_unix_date() but the words are reversed
502 ********************************************************************/
504 make_unix_date2 (void *date_ptr
)
508 x
= IVAL (date_ptr
, 0);
509 x2
= ((x
& 0xFFFF) << 16) | ((x
& 0xFFFF0000) >> 16);
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 ******************************************************************/
520 make_unix_date3 (void *date_ptr
)
522 time_t t
= (time_t) IVAL (date_ptr
, 0);
524 t
+= LocTimeDiff (t
);
529 /***************************************************************************
530 return a HTTP/1.0 time string
531 ***************************************************************************/
533 http_timestring (time_t t
)
536 struct tm
*tm
= LocalTime (&t
);
539 slprintf (buf
, sizeof (buf
) - 1, "%ld seconds since the Epoch", (long) t
);
541 strftime (buf
, sizeof (buf
) - 1, "%a, %d %b %Y %H:%M:%S %Z", tm
);
547 /****************************************************************************
548 return the date and time as a string
549 ****************************************************************************/
553 static fstring TimeBuf
;
554 time_t t
= time (NULL
);
555 struct tm
*tm
= LocalTime (&t
);
559 slprintf (TimeBuf
, sizeof (TimeBuf
) - 1, "%ld seconds since the Epoch", (long) t
);
563 strftime (TimeBuf
, 100, "%Y/%m/%d %H:%M:%S", tm
);
568 /****************************************************************************
569 return the best approximation to a 'create time' under UNIX from a stat
571 ****************************************************************************/
574 get_create_time (SMB_STRUCT_STAT
* st
, BOOL fake_dirs
)
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)
588 * One of ctime, mtime or atime was zero (probably atime).
589 * Just return MIN(ctime, mtime).