r884: convert samba4 to use [u]int32_t instead of [u]int32
[bbaumbach/samba-autobuild/.git] / source4 / 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 0
27 #endif
28 #ifndef TIME_T_MAX
29 #define TIME_T_MAX (~(time_t)0)
30 #endif
31
32 /*******************************************************************
33  External access to time_t_min and time_t_max.
34 ********************************************************************/
35 time_t get_time_t_max(void)
36 {
37         return TIME_T_MAX;
38 }
39
40 /*******************************************************************
41 a gettimeofday wrapper
42 ********************************************************************/
43 void GetTimeOfDay(struct timeval *tval)
44 {
45 #ifdef HAVE_GETTIMEOFDAY_TZ
46         gettimeofday(tval,NULL);
47 #else
48         gettimeofday(tval);
49 #endif
50 }
51
52 /*******************************************************************
53 yield the difference between *A and *B, in seconds, ignoring leap seconds
54 ********************************************************************/
55 static int tm_diff(struct tm *a, struct tm *b)
56 {
57         int ay = a->tm_year + (1900 - 1);
58         int by = b->tm_year + (1900 - 1);
59         int intervening_leap_days =
60                 (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
61         int years = ay - by;
62         int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday);
63         int hours = 24*days + (a->tm_hour - b->tm_hour);
64         int minutes = 60*hours + (a->tm_min - b->tm_min);
65         int seconds = 60*minutes + (a->tm_sec - b->tm_sec);
66
67         return seconds;
68 }
69
70 /*******************************************************************
71   return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
72   ******************************************************************/
73 int get_time_zone(time_t t)
74 {
75         struct tm *tm = gmtime(&t);
76         struct tm tm_utc;
77         if (!tm)
78                 return 0;
79         tm_utc = *tm;
80         tm = localtime(&t);
81         if (!tm)
82                 return 0;
83         return tm_diff(&tm_utc,tm);
84 }
85
86 #define TIME_FIXUP_CONSTANT 11644473600LL
87
88 /****************************************************************************
89 interpret an 8 byte "filetime" structure to a time_t
90 It's originally in "100ns units since jan 1st 1601"
91 ****************************************************************************/
92 time_t nt_time_to_unix(NTTIME nt)
93 {
94         nt += 1000*1000*10/2;
95         nt /= 1000*1000*10;
96         nt -= TIME_FIXUP_CONSTANT;
97
98         if (TIME_T_MIN >= nt || nt >= TIME_T_MAX) {
99                 return 0;
100         }
101
102         return (time_t)nt;
103 }
104
105
106 /****************************************************************************
107 put a 8 byte filetime from a time_t
108 This takes GMT as input
109 ****************************************************************************/
110 void unix_to_nt_time(NTTIME *nt, time_t t)
111 {
112         uint64_t t2; 
113
114         if (t == (time_t)-1) {
115                 *nt = (NTTIME)-1LL;
116                 return;
117         }               
118         if (t == 0) {
119                 *nt = 0;
120                 return;
121         }               
122
123         t2 = t;
124         t2 += TIME_FIXUP_CONSTANT;
125         t2 *= 1000*1000*10;
126
127         *nt = t2;
128 }
129
130
131 /****************************************************************************
132 check if it's a null mtime
133 ****************************************************************************/
134 BOOL null_mtime(time_t mtime)
135 {
136         return mtime == 0 || 
137                 mtime == (time_t)0xFFFFFFFF || 
138                 mtime == (time_t)-1;
139 }
140
141 /*******************************************************************
142   create a 16 bit dos packed date
143 ********************************************************************/
144 static uint16 make_dos_date1(struct tm *t)
145 {
146         uint16 ret=0;
147         ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1);
148         ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5));
149         return ret;
150 }
151
152 /*******************************************************************
153   create a 16 bit dos packed time
154 ********************************************************************/
155 static uint16 make_dos_time1(struct tm *t)
156 {
157         uint16 ret=0;
158         ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3));
159         ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5));
160         return ret;
161 }
162
163 /*******************************************************************
164   create a 32 bit dos packed date/time from some parameters
165   This takes a GMT time and returns a packed localtime structure
166 ********************************************************************/
167 static uint32_t make_dos_date(time_t unixdate, int zone_offset)
168 {
169         struct tm *t;
170         uint32_t ret=0;
171
172         if (unixdate == 0) {
173                 return 0;
174         }
175
176         unixdate -= zone_offset;
177
178         t = gmtime(&unixdate);
179         if (!t) {
180                 return 0xFFFFFFFF;
181         }
182
183         ret = make_dos_date1(t);
184         ret = ((ret&0xFFFF)<<16) | make_dos_time1(t);
185
186         return ret;
187 }
188
189 /*******************************************************************
190 put a dos date into a buffer (time/date format)
191 This takes GMT time and puts local time in the buffer
192 ********************************************************************/
193 void push_dos_date(char *buf, int offset, time_t unixdate, int zone_offset)
194 {
195         uint32_t x = make_dos_date(unixdate, zone_offset);
196         SIVAL(buf,offset,x);
197 }
198
199 /*******************************************************************
200 put a dos date into a buffer (date/time format)
201 This takes GMT time and puts local time in the buffer
202 ********************************************************************/
203 void push_dos_date2(char *buf,int offset,time_t unixdate, int zone_offset)
204 {
205         uint32_t x;
206         x = make_dos_date(unixdate, zone_offset);
207         x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
208         SIVAL(buf,offset,x);
209 }
210
211 /*******************************************************************
212 put a dos 32 bit "unix like" date into a buffer. This routine takes
213 GMT and converts it to LOCAL time before putting it (most SMBs assume
214 localtime for this sort of date)
215 ********************************************************************/
216 void push_dos_date3(char *buf,int offset,time_t unixdate, int zone_offset)
217 {
218         if (!null_mtime(unixdate)) {
219                 unixdate -= zone_offset;
220         }
221         SIVAL(buf,offset,unixdate);
222 }
223
224 /*******************************************************************
225   interpret a 32 bit dos packed date/time to some parameters
226 ********************************************************************/
227 static void interpret_dos_date(uint32_t date,int *year,int *month,int *day,int *hour,int *minute,int *second)
228 {
229         uint32_t p0,p1,p2,p3;
230
231         p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; 
232         p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF;
233
234         *second = 2*(p0 & 0x1F);
235         *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3);
236         *hour = (p1>>3)&0xFF;
237         *day = (p2&0x1F);
238         *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1;
239         *year = ((p3>>1)&0xFF) + 80;
240 }
241
242 /*******************************************************************
243   create a unix date (int GMT) from a dos date (which is actually in
244   localtime)
245 ********************************************************************/
246 time_t pull_dos_date(const uint8 *date_ptr, int zone_offset)
247 {
248         uint32_t dos_date=0;
249         struct tm t;
250         time_t ret;
251
252         dos_date = IVAL(date_ptr,0);
253
254         if (dos_date == 0) return (time_t)0;
255   
256         interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon,
257                            &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec);
258         t.tm_isdst = -1;
259   
260         ret = timegm(&t);
261
262         ret += zone_offset;
263
264         return ret;
265 }
266
267 /*******************************************************************
268 like make_unix_date() but the words are reversed
269 ********************************************************************/
270 time_t pull_dos_date2(const uint8 *date_ptr, int zone_offset)
271 {
272         uint32_t x,x2;
273
274         x = IVAL(date_ptr,0);
275         x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16);
276         SIVAL(&x,0,x2);
277
278         return pull_dos_date((void *)&x, zone_offset);
279 }
280
281 /*******************************************************************
282   create a unix GMT date from a dos date in 32 bit "unix like" format
283   these generally arrive as localtimes, with corresponding DST
284   ******************************************************************/
285 time_t pull_dos_date3(const uint8 *date_ptr, int zone_offset)
286 {
287         time_t t = (time_t)IVAL(date_ptr,0);
288         if (!null_mtime(t)) {
289                 t += zone_offset;
290         }
291         return t;
292 }
293
294
295 /***************************************************************************
296 return a HTTP/1.0 time string
297   ***************************************************************************/
298 char *http_timestring(TALLOC_CTX *mem_ctx, time_t t)
299 {
300         char *buf;
301         char tempTime[60];
302         struct tm *tm = localtime(&t);
303
304         if (!tm) {
305                 return talloc_asprintf(mem_ctx,"%ld seconds since the Epoch",(long)t);
306         }
307
308 #ifndef HAVE_STRFTIME
309         buf = talloc_strdup(mem_ctx, asctime(tm));
310         if (buf[strlen(buf)-1] == '\n') {
311                 buf[strlen(buf)-1] = 0;
312         }
313 #else
314         strftime(tempTime, sizeof(tempTime)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
315         buf = talloc_strdup(mem_ctx, tempTime);
316 #endif /* !HAVE_STRFTIME */
317
318         return buf;
319 }
320
321 /***************************************************************************
322 return a LDAP time string
323   ***************************************************************************/
324 char *ldap_timestring(TALLOC_CTX *mem_ctx, time_t t)
325 {
326         struct tm *tm = gmtime(&t);
327
328         if (!tm) {
329                 return NULL;
330         }
331
332         /* formatted like: 20040408072012.0Z */
333         return talloc_asprintf(mem_ctx, 
334                                "%04u%02u%02u%02u%02u%02u.0Z",
335                                tm->tm_year+1900, tm->tm_mon+1,
336                                tm->tm_mday, tm->tm_hour, tm->tm_min,
337                                tm->tm_sec);
338 }
339
340
341
342 /****************************************************************************
343  Return the date and time as a string
344 ****************************************************************************/
345 char *timestring(TALLOC_CTX *mem_ctx, time_t t)
346 {
347         char *TimeBuf;
348         char tempTime[80];
349         struct tm *tm;
350
351         tm = localtime(&t);
352         if (!tm) {
353                 return talloc_asprintf(mem_ctx,
354                                        "%ld seconds since the Epoch",
355                                        (long)t);
356         }
357
358 #ifdef HAVE_STRFTIME
359         /* some versions of gcc complain about using %c. This is a bug
360            in the gcc warning, not a bug in this code. See a recent
361            strftime() manual page for details.
362          */
363         strftime(tempTime,sizeof(tempTime)-1,"%c %Z",tm);
364         TimeBuf = talloc_strdup(mem_ctx, tempTime);
365 #else
366         TimeBuf = talloc_strdup(mem_ctx, asctime(tm));
367 #endif
368
369         return TimeBuf;
370 }
371
372 /*
373   return a talloced string representing a NTTIME for human consumption
374 */
375 const char *nt_time_string(TALLOC_CTX *mem_ctx, NTTIME nt)
376 {
377         time_t t = nt_time_to_unix(nt);
378         return talloc_strdup(mem_ctx, timestring(mem_ctx, t));
379 }
380
381
382 /*
383   put a NTTIME into a packet
384 */
385 void push_nttime(void *base, uint16 offset, NTTIME t)
386 {
387         SBVAL(base, offset,   t);
388 }
389
390 /*
391   pull a NTTIME from a packet
392 */
393 NTTIME pull_nttime(void *base, uint16 offset)
394 {
395         NTTIME ret = BVAL(base, offset);
396         return ret;
397 }
398
399 /*
400   parse a nttime as a large integer in a string and return a NTTIME
401 */
402 NTTIME nttime_from_string(const char *s)
403 {
404         return strtoull(s, NULL, 0);
405 }