r601: added the server code for all the samr_SetUserInfo and samr_QueryUserInfo level...
[jelmer/samba4-debian.git] / source / lib / time.c
1 /* 
2    Unix SMB/CIFS implementation.
3    time handling functions
4
5    Copyright (C) Andrew Tridgell                1992-2004
6    Copyright (C) Stefan (metze) Metzmacher      2002   
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 #ifndef TIME_T_MIN
26 #define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
27                     : ~ (time_t) 0 << (sizeof (time_t) * 8 - 1))
28 #endif
29 #ifndef TIME_T_MAX
30 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
31 #endif
32
33 /*******************************************************************
34  External access to time_t_min and time_t_max.
35 ********************************************************************/
36 time_t get_time_t_max(void)
37 {
38         return TIME_T_MAX;
39 }
40
41 /*******************************************************************
42 a gettimeofday wrapper
43 ********************************************************************/
44 void GetTimeOfDay(struct timeval *tval)
45 {
46 #ifdef HAVE_GETTIMEOFDAY_TZ
47         gettimeofday(tval,NULL);
48 #else
49         gettimeofday(tval);
50 #endif
51 }
52
53 /*******************************************************************
54 yield the difference between *A and *B, in seconds, ignoring leap seconds
55 ********************************************************************/
56 static int tm_diff(struct tm *a, struct tm *b)
57 {
58         int ay = a->tm_year + (1900 - 1);
59         int by = b->tm_year + (1900 - 1);
60         int intervening_leap_days =
61                 (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
62         int years = ay - by;
63         int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday);
64         int hours = 24*days + (a->tm_hour - b->tm_hour);
65         int minutes = 60*hours + (a->tm_min - b->tm_min);
66         int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
67
68         return seconds;
69 }
70
71 /*******************************************************************
72   return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
73   ******************************************************************/
74 int get_time_zone(time_t t)
75 {
76         struct tm *tm = gmtime(&t);
77         struct tm tm_utc;
78         if (!tm)
79                 return 0;
80         tm_utc = *tm;
81         tm = localtime(&t);
82         if (!tm)
83                 return 0;
84         return tm_diff(&tm_utc,tm);
85 }
86
87 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
88
89 /****************************************************************************
90 interpret an 8 byte "filetime" structure to a time_t
91 It's originally in "100ns units since jan 1st 1601"
92 ****************************************************************************/
93 time_t nt_time_to_unix(const NTTIME *nt)
94 {
95         double d;
96         time_t ret;
97
98         if (nt->high == 0) {
99                 return 0;
100         }
101
102         d = ((double)nt->high)*4.0*(double)(1<<30);
103         d += (nt->low&0xFFF00000);
104         d *= 1.0e-7;
105  
106         /* now adjust by 369 years to make the secs since 1970 */
107         d -= TIME_FIXUP_CONSTANT;
108
109         if (TIME_T_MIN >= d || d >= TIME_T_MAX) {
110                 return 0;
111         }
112
113         ret = (time_t)(d+0.5);
114
115         return ret;
116 }
117
118
119 /****************************************************************************
120 put a 8 byte filetime from a time_t
121 This takes GMT as input
122 ****************************************************************************/
123 void unix_to_nt_time(NTTIME *nt, time_t t)
124 {
125         double d;
126
127         if (t==0) {
128                 nt->low = 0;
129                 nt->high = 0;
130                 return;
131         }
132         if (t == TIME_T_MAX) {
133                 nt->low = 0xffffffff;
134                 nt->high = 0x7fffffff;
135                 return;
136         }               
137         if (t == -1) {
138                 nt->low = 0xffffffff;
139                 nt->high = 0xffffffff;
140                 return;
141         }               
142
143         d = (double)(t);
144         d += TIME_FIXUP_CONSTANT;
145         d *= 1.0e7;
146
147         nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
148         nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
149 }
150
151
152 /****************************************************************************
153 check if it's a null mtime
154 ****************************************************************************/
155 BOOL null_mtime(time_t mtime)
156 {
157         return mtime == 0 || 
158                 mtime == (time_t)0xFFFFFFFF || 
159                 mtime == (time_t)-1;
160 }
161
162 /*******************************************************************
163   create a 16 bit dos packed date
164 ********************************************************************/
165 static uint16 make_dos_date1(struct tm *t)
166 {
167         uint16 ret=0;
168         ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1);
169         ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5));
170         return ret;
171 }
172
173 /*******************************************************************
174   create a 16 bit dos packed time
175 ********************************************************************/
176 static uint16 make_dos_time1(struct tm *t)
177 {
178         uint16 ret=0;
179         ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3));
180         ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5));
181         return ret;
182 }
183
184 /*******************************************************************
185   create a 32 bit dos packed date/time from some parameters
186   This takes a GMT time and returns a packed localtime structure
187 ********************************************************************/
188 static uint32 make_dos_date(time_t unixdate, int zone_offset)
189 {
190         struct tm *t;
191         uint32 ret=0;
192
193         if (unixdate == 0) {
194                 return 0;
195         }
196
197         unixdate -= zone_offset;
198
199         t = gmtime(&unixdate);
200         if (!t) {
201                 return 0xFFFFFFFF;
202         }
203
204         ret = make_dos_date1(t);
205         ret = ((ret&0xFFFF)<<16) | make_dos_time1(t);
206
207         return ret;
208 }
209
210 /*******************************************************************
211 put a dos date into a buffer (time/date format)
212 This takes GMT time and puts local time in the buffer
213 ********************************************************************/
214 void push_dos_date(char *buf, int offset, time_t unixdate, int zone_offset)
215 {
216         uint32 x = make_dos_date(unixdate, zone_offset);
217         SIVAL(buf,offset,x);
218 }
219
220 /*******************************************************************
221 put a dos date into a buffer (date/time format)
222 This takes GMT time and puts local time in the buffer
223 ********************************************************************/
224 void push_dos_date2(char *buf,int offset,time_t unixdate, int zone_offset)
225 {
226         uint32 x;
227         x = make_dos_date(unixdate, zone_offset);
228         x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
229         SIVAL(buf,offset,x);
230 }
231
232 /*******************************************************************
233 put a dos 32 bit "unix like" date into a buffer. This routine takes
234 GMT and converts it to LOCAL time before putting it (most SMBs assume
235 localtime for this sort of date)
236 ********************************************************************/
237 void push_dos_date3(char *buf,int offset,time_t unixdate, int zone_offset)
238 {
239         if (!null_mtime(unixdate)) {
240                 unixdate -= zone_offset;
241         }
242         SIVAL(buf,offset,unixdate);
243 }
244
245 /*******************************************************************
246   interpret a 32 bit dos packed date/time to some parameters
247 ********************************************************************/
248 static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second)
249 {
250         uint32 p0,p1,p2,p3;
251
252         p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; 
253         p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF;
254
255         *second = 2*(p0 & 0x1F);
256         *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3);
257         *hour = (p1>>3)&0xFF;
258         *day = (p2&0x1F);
259         *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1;
260         *year = ((p3>>1)&0xFF) + 80;
261 }
262
263 /*******************************************************************
264   create a unix date (int GMT) from a dos date (which is actually in
265   localtime)
266 ********************************************************************/
267 time_t pull_dos_date(const uint8 *date_ptr, int zone_offset)
268 {
269         uint32 dos_date=0;
270         struct tm t;
271         time_t ret;
272
273         dos_date = IVAL(date_ptr,0);
274
275         if (dos_date == 0) return (time_t)0;
276   
277         interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon,
278                            &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec);
279         t.tm_isdst = -1;
280   
281         ret = timegm(&t);
282
283         ret += zone_offset;
284
285         return ret;
286 }
287
288 /*******************************************************************
289 like make_unix_date() but the words are reversed
290 ********************************************************************/
291 time_t pull_dos_date2(const uint8 *date_ptr, int zone_offset)
292 {
293         uint32 x,x2;
294
295         x = IVAL(date_ptr,0);
296         x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
297         SIVAL(&x,0,x2);
298
299         return pull_dos_date((void *)&x, zone_offset);
300 }
301
302 /*******************************************************************
303   create a unix GMT date from a dos date in 32 bit "unix like" format
304   these generally arrive as localtimes, with corresponding DST
305   ******************************************************************/
306 time_t pull_dos_date3(const uint8 *date_ptr, int zone_offset)
307 {
308         time_t t = (time_t)IVAL(date_ptr,0);
309         if (!null_mtime(t)) {
310                 t += zone_offset;
311         }
312         return t;
313 }
314
315
316 /***************************************************************************
317 return a HTTP/1.0 time string
318   ***************************************************************************/
319 char *http_timestring(TALLOC_CTX *mem_ctx, time_t t)
320 {
321         char *buf;
322         char tempTime[60];
323         struct tm *tm = localtime(&t);
324
325         if (!tm) {
326                 return talloc_asprintf(mem_ctx,"%ld seconds since the Epoch",(long)t);
327         }
328
329 #ifndef HAVE_STRFTIME
330         buf = talloc_strdup(mem_ctx, asctime(tm));
331         if (buf[strlen(buf)-1] == '\n') {
332                 buf[strlen(buf)-1] = 0;
333         }
334 #else
335         strftime(tempTime, sizeof(tempTime)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
336         buf = talloc_strdup(mem_ctx, tempTime);
337 #endif /* !HAVE_STRFTIME */
338
339         return buf;
340 }
341
342 /***************************************************************************
343 return a LDAP time string
344   ***************************************************************************/
345 char *ldap_timestring(TALLOC_CTX *mem_ctx, time_t t)
346 {
347         struct tm *tm = gmtime(&t);
348
349         if (!tm) {
350                 return NULL;
351         }
352
353         /* formatted like: 20040408072012.0Z */
354         return talloc_asprintf(mem_ctx, 
355                                "%04u%02u%02u%02u%02u%02u.0Z",
356                                tm->tm_year+1900, tm->tm_mon+1,
357                                tm->tm_mday, tm->tm_hour, tm->tm_min,
358                                tm->tm_sec);
359 }
360
361
362
363 /****************************************************************************
364  Return the date and time as a string
365 ****************************************************************************/
366 char *timestring(TALLOC_CTX *mem_ctx, time_t t)
367 {
368         char *TimeBuf;
369         char tempTime[80];
370         struct tm *tm;
371
372         tm = localtime(&t);
373         if (!tm) {
374                 return talloc_asprintf(mem_ctx,
375                                        "%ld seconds since the Epoch",
376                                        (long)t);
377         }
378
379 #ifdef HAVE_STRFTIME
380         /* some versions of gcc complain about using %c. This is a bug
381            in the gcc warning, not a bug in this code. See a recent
382            strftime() manual page for details.
383          */
384         strftime(tempTime,sizeof(tempTime)-1,"%c %Z",tm);
385         TimeBuf = talloc_strdup(mem_ctx, tempTime);
386 #else
387         TimeBuf = talloc_strdup(mem_ctx, asctime(tm));
388 #endif
389
390         return TimeBuf;
391 }
392
393 /****************************************************************************
394 check if NTTIME is 0
395 ****************************************************************************/
396 BOOL nt_time_is_zero(NTTIME *nt)
397 {
398         return (nt->high==0);
399 }
400
401 /*
402   return a talloced string representing a NTTIME for human consumption
403 */
404 const char *nt_time_string(TALLOC_CTX *mem_ctx, const NTTIME *nt)
405 {
406         time_t t = nt_time_to_unix(nt);
407         return talloc_strdup(mem_ctx, timestring(mem_ctx, t));
408 }
409
410
411 /*
412   put a NTTIME into a packet
413 */
414 void push_nttime(void *base, uint16 offset, NTTIME *t)
415 {
416         SIVAL(base, offset,   t->low);
417         SIVAL(base, offset+4, t->high);
418 }
419
420 /*
421   pull a NTTIME from a packet
422 */
423 NTTIME pull_nttime(void *base, uint16 offset)
424 {
425         NTTIME ret;
426         ret.low = IVAL(base, offset);
427         ret.high = IVAL(base, offset+4);
428         return ret;
429 }
430
431 /*
432   convert a NTTIME to a double in 100-nano-seconds since 1601
433 */
434 double nttime_to_double_nt(NTTIME t)
435 {
436         const double t32 = 4294967296.0;
437         return t.high*t32 + t.low;
438 }
439
440 /*
441   convert a double in 100-nano-seconds since 1601 to a NTTIME
442 */
443 NTTIME nttime_from_double_nt(double t)
444 {
445         const double t32 = 4294967296.0;
446         NTTIME ret;
447         ret.high = t / t32;
448         ret.low = t - (ret.high*t32);
449         return ret;
450 }
451
452 /*
453   parse a nttime as a large integer in a string and return a NTTIME
454 */
455 NTTIME nttime_from_string(const char *s)
456 {
457         return nttime_from_double_nt(strtod(s, NULL));
458 }