|
|
1.1 root 1: /* xpdatetime.c */
2:
3: /* Cross-platform (and eXtra Precision) date/time functions */
4:
5: /* $Id: xpdatetime.c,v 1.7 2010/05/28 01:27:40 sbbs Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2010 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This library is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU Lesser General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU Lesser General Public License for more details: lgpl.txt or *
18: * http://www.fsf.org/copyleft/lesser.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include <string.h> /* memset */
39: #include "datewrap.h" /* sane_mktime */
40: #include "xpdatetime.h" /* xpDateTime_t */
41:
42: /**************************************/
43: /* Cross-platform date/time functions */
44: /**************************************/
45:
46: xpDateTime_t xpDateTime_create(unsigned year, unsigned month, unsigned day
47: ,unsigned hour, unsigned minute, float second
48: ,xpTimeZone_t zone)
49: {
50: xpDateTime_t xpDateTime;
51:
52: xpDateTime.date.year = year;
53: xpDateTime.date.month = month;
54: xpDateTime.date.day = day;
55: xpDateTime.time.hour = hour;
56: xpDateTime.time.minute = minute;
57: xpDateTime.time.second = second;
58: xpDateTime.zone = zone;
59:
60: return xpDateTime;
61: }
62:
63: xpDateTime_t xpDateTime_now(void)
64: {
65: #if defined(_WIN32)
66: SYSTEMTIME systime;
67:
68: GetLocalTime(&systime);
69: return(xpDateTime_create(systime.wYear,systime.wMonth,systime.wDay
70: ,systime.wHour,systime.wMinute,(float)systime.wSecond+(systime.wMilliseconds*0.001F)
71: ,xpTimeZone_local()));
72: #else /* !Win32 (e.g. Unix) */
73: struct tm tm;
74: struct timeval tv;
75: time_t t;
76:
77: gettimeofday(&tv, NULL);
78: t=tv.tv_sec;
79: localtime_r(&t,&tm);
80:
81: return xpDateTime_create(1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday
82: ,tm.tm_hour,tm.tm_min,(float)tm.tm_sec+(tv.tv_usec*0.00001)
83: ,xpTimeZone_local());
84: #endif
85: }
86:
87: xpTimeZone_t xpTimeZone_local(void)
88: {
89: #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DARWIN__)
90: struct tm tm;
91: time_t t;
92:
93: localtime_r(&t, &tm);
94: return(tm.tm_gmtoff/60);
95: #elif defined(_WIN32)
96: TIME_ZONE_INFORMATION tz;
97: DWORD tzRet;
98:
99: /*****************************/
100: /* Get Time-zone information */
101: /*****************************/
102: memset(&tz,0,sizeof(tz));
103: tzRet=GetTimeZoneInformation(&tz);
104: switch(tzRet) {
105: case TIME_ZONE_ID_DAYLIGHT:
106: tz.Bias += tz.DaylightBias;
107: break;
108: case TIME_ZONE_ID_STANDARD:
109: tz.Bias += tz.StandardBias;
110: break;
111: }
112:
113: return -tz.Bias;
114: #else
115:
116: #if defined(__BORLANDC__) || defined(__CYGWIN__)
117: #define timezone _timezone
118: #endif
119:
120: /* Converts (_)timezone from seconds west of UTC to minutes east of UTC */
121: /* Adjust for DST, assuming adjustment is always 60 minutes <sigh> */
122: return -((timezone/60) - (daylight*60));
123: #endif
124: }
125:
126: time_t xpDateTime_to_time(xpDateTime_t xpDateTime)
127: {
128: struct tm tm;
129:
130: ZERO_VAR(tm);
131:
132: if(xpDateTime.date.year==0)
133: return(INVALID_TIME);
134:
135: tm.tm_year = xpDateTime.date.year;
136: tm.tm_mon = xpDateTime.date.month;
137: tm.tm_mday = xpDateTime.date.day;
138:
139: tm.tm_hour = xpDateTime.time.hour;
140: tm.tm_min = xpDateTime.time.minute;
141: tm.tm_sec = (int)xpDateTime.time.second;
142:
143: return sane_mktime(&tm);
144: }
145:
146: xpDateTime_t time_to_xpDateTime(time_t ti, xpTimeZone_t zone)
147: {
148: xpDateTime_t never;
149: struct tm tm;
150:
151: ZERO_VAR(never);
152: ZERO_VAR(tm);
153: if(localtime_r(&ti,&tm)==NULL)
154: return(never);
155:
156: return xpDateTime_create(1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday
157: ,tm.tm_hour,tm.tm_min,(float)tm.tm_sec
158: ,zone==xpTimeZone_LOCAL ? xpTimeZone_local() : zone);
159: }
160:
161: xpDateTime_t gmtime_to_xpDateTime(time_t ti)
162: {
163: xpDateTime_t never;
164: struct tm tm;
165:
166: ZERO_VAR(never);
167: ZERO_VAR(tm);
168: if(gmtime_r(&ti,&tm)==NULL)
169: return(never);
170:
171: return xpDateTime_create(1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday
172: ,tm.tm_hour,tm.tm_min,(float)tm.tm_sec
173: ,xpTimeZone_UTC);
174: }
175:
176: /**********************************************/
177: /* Decimal-coded ISO-8601 date/time functions */
178: /**********************************************/
179:
180: isoDate_t xpDateTime_to_isoDateTime(xpDateTime_t xpDateTime, isoTime_t* isoTime)
181: {
182: if(isoTime!=NULL)
183: *isoTime=0;
184:
185: if(xpDateTime.date.year==0)
186: return(0);
187:
188: if(isoTime!=NULL)
189: *isoTime=isoTime_create(xpDateTime.time.hour,xpDateTime.time.minute,xpDateTime.time.second);
190:
191: return isoDate_create(xpDateTime.date.year,xpDateTime.date.month,xpDateTime.date.day);
192: }
193:
194: xpDateTime_t isoDateTime_to_xpDateTime(isoDate_t date, isoTime_t ti)
195: {
196: return xpDateTime_create(isoDate_year(date),isoDate_month(date),isoDate_day(date)
197: ,isoTime_hour(ti),isoTime_minute(ti),(float)isoTime_second(ti),xpTimeZone_local());
198: }
199:
200: isoDate_t time_to_isoDateTime(time_t ti, isoTime_t* isoTime)
201: {
202: struct tm tm;
203:
204: if(isoTime!=NULL)
205: *isoTime=0;
206:
207: ZERO_VAR(tm);
208: if(localtime_r(&ti,&tm)==NULL)
209: return(0);
210:
211: if(isoTime!=NULL)
212: *isoTime=isoTime_create(tm.tm_hour,tm.tm_min,tm.tm_sec);
213:
214: return isoDate_create(1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday);
215: }
216:
217: isoTime_t time_to_isoTime(time_t ti)
218: {
219: isoTime_t isoTime;
220:
221: time_to_isoDateTime(ti,&isoTime);
222:
223: return isoTime;
224: }
225:
226: isoDate_t gmtime_to_isoDateTime(time_t ti, isoTime_t* isoTime)
227: {
228: struct tm tm;
229:
230: if(isoTime!=NULL)
231: *isoTime=0;
232:
233: ZERO_VAR(tm);
234: if(gmtime_r(&ti,&tm)==NULL)
235: return(0);
236:
237: if(isoTime!=NULL)
238: *isoTime=isoTime_create(tm.tm_hour,tm.tm_min,tm.tm_sec);
239:
240: return isoDate_create(1900+tm.tm_year,1+tm.tm_mon,tm.tm_mday);
241: }
242:
243: isoTime_t gmtime_to_isoTime(time_t ti)
244: {
245: isoTime_t isoTime;
246:
247: gmtime_to_isoDateTime(ti,&isoTime);
248:
249: return isoTime;
250: }
251:
252: time_t isoDateTime_to_time(isoDate_t date, isoTime_t ti)
253: {
254: struct tm tm;
255:
256: ZERO_VAR(tm);
257:
258: if(date==0)
259: return(INVALID_TIME);
260:
261: tm.tm_year = isoDate_year(date);
262: tm.tm_mon = isoDate_month(date);
263: tm.tm_mday = isoDate_day(date);
264:
265: tm.tm_hour = isoTime_hour(ti);
266: tm.tm_min = isoTime_minute(ti);
267: tm.tm_sec = isoTime_second(ti);
268:
269: return sane_mktime(&tm);
270: }
271:
272: /****************************************************************************/
273: /* Conversion from xpDate/Time/Zone to isoDate/Time/Zone Strings */
274: /****************************************************************************/
275:
276: char* xpDate_to_isoDateStr(xpDate_t date, const char* sep, char* str, size_t maxlen)
277: {
278: if(sep==NULL)
279: sep="-";
280:
281: snprintf(str,maxlen,"%04u%s%02u%s%02u"
282: ,date.year ,sep
283: ,date.month ,sep
284: ,date.day);
285:
286: return str;
287: }
288:
289: /* precision example output
290: * -2 "14"
291: * -1 "14:02"
292: * 0 "14:02:39"
293: * 1 "14:02:39.8"
294: * 2 "14:02:39.82"
295: * 3 "14:02:39.829"
296: */
297: char* xpTime_to_isoTimeStr(xpTime_t ti, const char* sep, int precision
298: ,char* str, size_t maxlen)
299: {
300: if(sep==NULL)
301: sep=":";
302:
303: if(precision < -1) /* HH */
304: snprintf(str, maxlen, "%02u", ti.hour);
305: else if(precision < 0) /* HH:MM */
306: snprintf(str, maxlen, "%02u%s%02u"
307: ,ti.hour ,sep
308: ,ti.minute
309: );
310: else /* HH:MM:SS[.fract] */
311: snprintf(str, maxlen, "%02u%s%02u%s%0*.*f"
312: ,ti.hour ,sep
313: ,ti.minute ,sep
314: ,precision ? (precision+3) : 2
315: ,precision
316: ,ti.second
317: );
318:
319: return str;
320: }
321:
322: char* xpTimeZone_to_isoTimeZoneStr(xpTimeZone_t zone, const char* sep
323: ,char *str, size_t maxlen)
324: {
325: xpTimeZone_t tz=zone;
326:
327: if(tz==xpTimeZone_UTC)
328: return "Z";
329:
330: if(sep==NULL)
331: sep=":";
332:
333: if(tz<0)
334: tz=-tz;
335:
336: snprintf(str,maxlen,"%c%02u%s%02u"
337: ,zone < 0 ? '-':'+'
338: ,tz/60
339: ,sep
340: ,tz%60);
341:
342: return str;
343: }
344:
345: char* xpDateTime_to_isoDateTimeStr(xpDateTime_t dt
346: ,const char* date_sep, const char* datetime_sep, const char* time_sep
347: ,int precision
348: ,char* str, size_t maxlen)
349: {
350: char tz_str[16];
351: char date_str[16];
352: char time_str[16];
353:
354: if(datetime_sep==NULL) datetime_sep="T";
355:
356: snprintf(str,maxlen,"%s%s%s%s"
357: ,xpDate_to_isoDateStr(dt.date, date_sep, date_str, sizeof(date_str))
358: ,datetime_sep
359: ,xpTime_to_isoTimeStr(dt.time, time_sep, precision, time_str, sizeof(time_str))
360: ,xpTimeZone_to_isoTimeZoneStr(dt.zone,time_sep,tz_str,sizeof(tz_str)));
361:
362: return str;
363: }
364:
365: /****************************************************************************/
366: /* isoDate/Time/Zone String parsing functions */
367: /****************************************************************************/
368:
369: BOOL isoTimeZoneStr_parse(const char* str, xpTimeZone_t* zone)
370: {
371: unsigned hour=0,minute=0;
372:
373: switch(*str) {
374: case 0: /* local time-zone */
375: *zone = xpTimeZone_local();
376: return TRUE;
377: case 'Z': /* UTC */
378: *zone = xpTimeZone_UTC;
379: return TRUE;
380: case '+':
381: case '-': /* "+/- HH[:]MM" */
382: if(sscanf(str+1,"%2u%*s%2u",&hour,&minute)>=1) {
383: *zone = (hour*60) + minute;
384: if(*str=='-')
385: *zone = -(*zone);
386: return TRUE;
387: }
388: break;
389: }
390: return FALSE;
391: }
392:
393: /* TODO: adjust times in 24:xx:xx format */
394: xpDateTime_t isoDateTimeStr_parse(const char* str)
395: {
396: char zone[16];
397: xpDateTime_t xpDateTime;
398:
399: zone[0]=0;
400: ZERO_VAR(xpDateTime);
401:
402: if((sscanf(str,"%4u-%2u-%2uT%2u:%2u:%f%6s" /* CCYY-MM-DDThh:MM:ss�hhmm */
403: ,&xpDateTime.date.year
404: ,&xpDateTime.date.month
405: ,&xpDateTime.date.day
406: ,&xpDateTime.time.hour
407: ,&xpDateTime.time.minute
408: ,&xpDateTime.time.second
409: ,zone)>=2
410: || sscanf(str,"%4u%2u%2uT%2u%2u%f%6s" /* CCYYMMDDThhmmss�hhmm */
411: ,&xpDateTime.date.year
412: ,&xpDateTime.date.month
413: ,&xpDateTime.date.day
414: ,&xpDateTime.time.hour
415: ,&xpDateTime.time.minute
416: ,&xpDateTime.time.second
417: ,zone)>=4
418: || sscanf(str,"%4u%2u%2u%2u%2u%f%6s" /* CCYYMMDDhhmmss�hhmm */
419: ,&xpDateTime.date.year
420: ,&xpDateTime.date.month
421: ,&xpDateTime.date.day
422: ,&xpDateTime.time.hour
423: ,&xpDateTime.time.minute
424: ,&xpDateTime.time.second
425: ,zone)>=1
426: ) && isoTimeZoneStr_parse(zone,&xpDateTime.zone))
427: return xpDateTime;
428:
429: return xpDateTime;
430: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.