1 /*
2 * rasqal_datetime.c - Rasqal XSD dateTime and XSD date
3 *
4 * Copyright (C) 2007-2011, David Beckett http://www.dajobe.org/
5 *
6 * Contributions:
7 * Copyright (C) 2007, Lauri Aalto <laalto@iki.fi>
8 *
9 * This package is Free Software and part of Redland http://librdf.org/
10 *
11 * It is licensed under the following three licenses as alternatives:
12 * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
13 * 2. GNU General Public License (GPL) V2 or any newer version
14 * 3. Apache License, V2.0 or any newer version
15 *
16 * You may not use this file except in compliance with at least one of
17 * the above three licenses.
18 *
19 * See LICENSE.html or LICENSE.txt at the top of this package for the
20 * complete terms and further detail along with the license texts for
21 * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
22 *
23 *
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <rasqal_config.h>
28 #endif
29
30 #ifdef WIN32
31 #include <win32_rasqal_config.h>
32 #endif
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <ctype.h>
37 #ifdef TIME_WITH_SYS_TIME
38 # include <sys/time.h>
39 # include <time.h>
40 #else
41 # ifdef HAVE_SYS_TIME_H
42 # include <sys/time.h>
43 # else
44 # include <time.h>
45 # endif
46 #endif
47 #ifdef HAVE_STDLIB_H
48 #include <stdlib.h>
49 #endif
50 #include <stdarg.h>
51 #include <limits.h>
52
53 #include "rasqal.h"
54 #include "rasqal_internal.h"
55
56 /* Local definitions */
57
58 static int rasqal_xsd_datetime_parse(const char *datetime_string, rasqal_xsd_datetime *result, int is_dateTime);
59 static unsigned int days_per_month(int month, int year);
60
61
62 #ifndef ISNUM
63 #define ISNUM(c) ((c) >= '0' && (c) <= '9')
64 #endif
65
66
67 /**
68 * rasqal_xsd_datetime_normalize:
69 * @datetime: date time
70 *
71 * INTERNAL - Normalize a date time into the allowed range
72 *
73 * The result will always give either
74 * have_tz 'N' with timezone_minutes RASQAL_XSD_DATETIME_NO_TZ
75 * have_tz 'Z' with timezone_minutes 0
76 *
77 * Return value: zero on success, non zero on failure.
78 */
79 static int
rasqal_xsd_datetime_normalize(rasqal_xsd_datetime * datetime)80 rasqal_xsd_datetime_normalize(rasqal_xsd_datetime *datetime)
81 {
82 int t;
83
84 if(datetime->have_tz == 'Y') {
85 if(datetime->timezone_minutes) {
86 /* Normalize to Zulu if there was a timezone offset */
87 datetime->hour = RASQAL_GOOD_CAST(signed char, datetime->hour - (datetime->timezone_minutes / 60));
88 datetime->minute = RASQAL_GOOD_CAST(signed char, datetime->minute - (datetime->timezone_minutes % 60));
89
90 datetime->timezone_minutes = 0;
91 }
92 datetime->have_tz = 'Z';
93 }
94
95 /* second & second parts: no need to normalize as they are not
96 * touched after range check
97 */
98
99 /* minute */
100 if(datetime->minute < 0) {
101 datetime->minute = RASQAL_GOOD_CAST(signed char, datetime->minute + 60);
102 datetime->hour--;
103 } else if(datetime->minute > 59) {
104 datetime->minute = RASQAL_GOOD_CAST(signed char, datetime->minute - 60);
105 datetime->hour++;
106 }
107
108 /* hour */
109 if(datetime->hour < 0) {
110 datetime->hour = RASQAL_GOOD_CAST(signed char, datetime->hour + 24);
111 datetime->day--;
112 } else if(datetime->hour > 23) {
113 datetime->hour = RASQAL_GOOD_CAST(signed char, datetime->hour - 24);
114 datetime->day++;
115 }
116
117 /* day */
118 if(datetime->day < 1) {
119 int y2;
120 t = --datetime->month;
121 /* going back beyond year boundary? */
122 if(!t) {
123 t = 12;
124 y2 = datetime->year-1;
125 } else
126 y2 = datetime->year;
127 datetime->day = RASQAL_GOOD_CAST(unsigned char, datetime->day + days_per_month(t, y2));
128 } else {
129 t = RASQAL_GOOD_CAST(int, days_per_month(datetime->month, datetime->year));
130 if(datetime->day > t) {
131 datetime->day = RASQAL_GOOD_CAST(unsigned char, datetime->day - t);
132 datetime->month++;
133 }
134 }
135
136 /* month & year */
137 if(datetime->month < 1) {
138 datetime->month = RASQAL_GOOD_CAST(unsigned char, datetime->month + 12);
139 datetime->year--;
140 /* there is no year 0 - go backwards to year -1 */
141 if(!datetime->year)
142 datetime->year--;
143 } else if(datetime->month > 12) {
144 datetime->month = RASQAL_GOOD_CAST(unsigned char, datetime->month - 12);
145 datetime->year++;
146 /* there is no year 0 - go forwards to year 1 */
147 if(!datetime->year)
148 datetime->year++;
149 }
150
151 datetime->time_on_timeline = rasqal_xsd_datetime_get_as_unixtime(datetime);
152
153 /* success */
154 return 0;
155 }
156
157
158 /**
159 * rasqal_xsd_datetime_parse:
160 * @datetime_string: xsd:dateTime as lexical form string
161 * @result: target struct for holding dateTime components
162 * @is_dateTime: is xsd:dateTime and should look for time (hour, mins, secs)
163 * otherwise is xsd:date and should skip to looking for timezone
164 *
165 * INTERNAL - Parse a xsd:dateTime string into a #rasqal_xsd_datetime struct.
166 *
167 * Does NOT normalize the structure. Call
168 * rasqal_xsd_datetime_normalize() to do that.
169 *
170 * http://www.w3.org/TR/xmlschema-2/#dt-dateTime
171 *
172 * "The lexical space of dateTime consists of finite-length sequences of
173 * characters of the form:
174 * '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?,
175 * where
176 *
177 * * '-'? yyyy is a four-or-more digit optionally negative-signed numeral that
178 * represents the year; if more than four digits, leading zeros are
179 * prohibited, and '0000' is prohibited (see the Note above (3.2.7); also
180 * note that a plus sign is not permitted);
181 * * the remaining '-'s are separators between parts of the date portion;
182 * * the first mm is a two-digit numeral that represents the month;
183 * * dd is a two-digit numeral that represents the day;
184 * * 'T' is a separator indicating that time-of-day follows;
185 * * hh is a two-digit numeral that represents the hour; '24' is permitted if
186 * the minutes and seconds represented are zero, and the dateTime value so
187 * represented is the first instant of the following day (the hour property
188 * of a dateTime object in the value space cannot have a value greater
189 * than 23);
190 * * ':' is a separator between parts of the time-of-day portion;
191 * * the second mm is a two-digit numeral that represents the minute;
192 * * ss is a two-integer-digit numeral that represents the whole seconds;
193 * * '.' s+ (if present) represents the fractional seconds;
194 * * zzzzzz (if present) represents the timezone"
195 *
196 *
197 * http://www.w3.org/TR/xmlschema-2/#dt-date
198 * lexical space: '-'? yyyy '-' mm '-' dd zzzzzz?
199 *
200 * Return value: zero on success, non zero on failure.
201 */
202 static int
rasqal_xsd_datetime_parse(const char * datetime_string,rasqal_xsd_datetime * result,int is_dateTime)203 rasqal_xsd_datetime_parse(const char *datetime_string,
204 rasqal_xsd_datetime *result,
205 int is_dateTime)
206 {
207 const char *p, *q;
208 #define B_SIZE 16
209 char b[B_SIZE];
210 unsigned int l, t, t2, is_neg;
211 unsigned long u;
212 #define MICROSECONDS_MAX_DIGITS 6
213
214 if(!datetime_string || !result)
215 return -1;
216
217 p = (const char *)datetime_string;
218 is_neg = 0;
219
220 /* Parse year */
221
222 /* negative years permitted */
223 if(*p == '-') {
224 is_neg = 1;
225 p++;
226 }
227 for(q = p; ISNUM(*p); p++)
228 ;
229 l = RASQAL_GOOD_CAST(unsigned int, p - q);
230
231 /* error if
232 - less than 4 digits in year
233 - more than 4 digits && leading zeros
234 - '-' does not follow numbers
235 */
236 if(l < 4 || (l > 4 && *q=='0') || *p != '-')
237 return -1;
238
239 if(l >= (B_SIZE - 1))
240 l = RASQAL_GOOD_CAST(unsigned int, B_SIZE - 1);
241
242 memcpy(b, q, l);
243 b[l] = 0; /* ensure nul termination */
244 u = strtoul(b, 0, 10);
245
246 /* year "0000" not permitted
247 * restrict to signed int range
248 * >= instead of > to allow for +-1 year adjustment in normalization
249 * (however, these +-INT_MAX years cannot be parsed back in if
250 * converted to string)
251 */
252 if(!u || u >= INT_MAX)
253 return -1;
254
255 result->year = is_neg ? -RASQAL_GOOD_CAST(int, u) : RASQAL_GOOD_CAST(int, u);
256
257 /* parse month */
258
259 for(q = ++p; ISNUM(*p); p++)
260 ;
261 l = RASQAL_GOOD_CAST(unsigned int, p - q);
262
263 /* error if month is not 2 digits or '-' is not the separator */
264 if(l != 2 || *p != '-')
265 return -2;
266
267 t = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0')*10);
268 t += RASQAL_GOOD_CAST(unsigned int, *q - '0');
269
270 /* month must be 1..12 */
271 if(t < 1 || t > 12)
272 return -2;
273
274 result->month = RASQAL_GOOD_CAST(unsigned char, t);
275
276 /* parse day */
277
278 for(q = ++p; ISNUM(*p); p++)
279 ;
280 l = RASQAL_GOOD_CAST(unsigned int, p - q);
281
282 if(is_dateTime) {
283 /* xsd:dateTime: error if day is not 2 digits or 'T' is not the separator */
284 if(l != 2 || *p != 'T')
285 return -3;
286 } else {
287 /* xsd:date: error if day is not 2 digits or separator is not
288 * 'Z' (utc)
289 * '+' or '-' (timezone offset)
290 * nul (end of string - timezone is optional)
291 */
292 if(l != 2 || (*p && *p != 'Z' && *p != '+' && *p != '-'))
293 return -3;
294 }
295
296 t = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0') * 10);
297 t += RASQAL_GOOD_CAST(unsigned int, *q - '0');
298
299 /* day must be 1..days_per_month */
300 if(t < 1 || t > days_per_month(result->month, result->year))
301 return -3;
302
303 result->day = RASQAL_GOOD_CAST(unsigned char, t);
304
305 if(is_dateTime) {
306 /* parse hour */
307
308 for(q = ++p; ISNUM(*p); p++)
309 ;
310 l = RASQAL_GOOD_CAST(unsigned int, p - q);
311
312 /* error if hour is not 2 digits or ':' is not the separator */
313 if(l != 2 || *p != ':')
314 return -4;
315
316 t = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0')*10);
317 t += RASQAL_GOOD_CAST(unsigned int, *q - '0');
318
319 /* hour must be 0..24 - will handle special case 24 later
320 * (no need to check for < 0)
321 */
322 if(t > 24)
323 return -4;
324
325 result->hour = RASQAL_GOOD_CAST(signed char, t);
326
327 /* parse minute */
328
329 for(q = ++p; ISNUM(*p); p++)
330 ;
331 l = RASQAL_GOOD_CAST(unsigned int, p - q);
332
333 /* error if minute is not 2 digits or ':' is not the separator */
334 if(l != 2 || *p != ':')
335 return -5;
336
337 t = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0') * 10);
338 t += RASQAL_GOOD_CAST(unsigned int, *q - '0');
339
340 /* minute must be 0..59
341 * (no need to check for < 0)
342 */
343 if(t > 59)
344 return -5;
345
346 result->minute = RASQAL_GOOD_CAST(signed char, t);
347
348 /* parse second whole part */
349
350 for(q = ++p; ISNUM(*p); p++)
351 ;
352 l = RASQAL_GOOD_CAST(unsigned int, p - q);
353
354 /* error if second is not 2 digits or separator is not
355 * '.' (second fraction)
356 * 'Z' (utc)
357 * '+' or '-' (timezone offset)
358 * nul (end of string - second fraction and timezone are optional)
359 */
360 if(l != 2 || (*p && *p != '.' && *p != 'Z' && *p != '+' && *p != '-'))
361 return -6;
362
363 t = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0')*10);
364 t += RASQAL_GOOD_CAST(unsigned int, *q - '0');
365
366 /* second must be 0..59
367 * (no need to check for < 0)
368 */
369 if(t > 59)
370 return -6;
371
372 result->second = RASQAL_GOOD_CAST(signed char, t);
373
374 /* now that we have hour, minute and second, we can check
375 * if hour == 24 -> only 24:00:00 permitted (normalized later)
376 */
377 if(result->hour == 24 && (result->minute || result->second))
378 return -7;
379
380 /* parse fraction seconds if any */
381 result->microseconds = 0;
382 if(*p == '.') {
383 for(q = ++p; ISNUM(*p); p++)
384 ;
385
386 /* ignore trailing zeros */
387 while(*--p == '0')
388 ;
389 p++;
390
391 if(!(*q == '0' && q == p)) {
392 /* allow ".0" */
393 l = RASQAL_GOOD_CAST(unsigned int, p - q);
394
395 if(l < 1) /* need at least 1 num */
396 return -8;
397
398 /* support only to microseconds with truncation */
399 if(l > MICROSECONDS_MAX_DIGITS)
400 l = MICROSECONDS_MAX_DIGITS;
401
402 result->microseconds = 0;
403 for(t2 = 0; t2 < MICROSECONDS_MAX_DIGITS; ++t2) {
404 if(t2 < l)
405 result->microseconds += (*q++ - '0');
406 if(t2 != MICROSECONDS_MAX_DIGITS - 1)
407 result->microseconds *= 10;
408 }
409
410 }
411
412 /* skip ignored trailing zeros */
413 while(*p == '0')
414 p++;
415 }
416
417 } else { /* end if is_dateTime */
418 /* set to center of day interval (noon) */
419 result->hour = 12;
420 result->minute = 0;
421 result->second = 0;
422 result->microseconds = 0;
423 }
424
425
426 /* parse & adjust timezone offset */
427 /* result is normalized later */
428 result->timezone_minutes = RASQAL_XSD_DATETIME_NO_TZ;
429 result->have_tz = 'N';
430 if(*p) {
431 if(*p == 'Z') {
432 /* utc timezone - no need to adjust */
433 result->timezone_minutes = 0;
434 result->have_tz = 'Z';
435 p++;
436 } else if(*p == '+' || *p == '-') {
437 result->timezone_minutes = 0;
438 result->have_tz = 'Y';
439
440 /* work out timezone offsets */
441 is_neg = *p == '-';
442
443 /* timezone hours */
444 for(q = ++p; ISNUM(*p); p++)
445 ;
446 l = RASQAL_GOOD_CAST(unsigned int, p - q);
447 if(l != 2 || *p!=':')
448 return -9;
449
450 t2 = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0') * 10);
451 t2 += RASQAL_GOOD_CAST(unsigned int, *q - '0');
452 if(t2 > 14)
453 /* tz offset hours are restricted to 0..14
454 * (no need to check for < 0)
455 */
456 return -9;
457
458 result->timezone_minutes = RASQAL_GOOD_CAST(short int, (is_neg ? -t2 : t2) * 60);
459
460 /* timezone minutes */
461 for(q = ++p; ISNUM(*p); p++)
462 ;
463 l = RASQAL_GOOD_CAST(unsigned int, p - q);
464 if(l != 2)
465 return -10;
466
467 t = RASQAL_GOOD_CAST(unsigned int, (*q++ - '0') * 10);
468 t += RASQAL_GOOD_CAST(unsigned int, *q - '0');
469 if(t > 59 || (t2 == 14 && t != 0)) {
470 /* tz offset minutes are restricted to 0..59
471 * (no need to check for < 0)
472 * or 0 if hour offset is exactly +-14
473 */
474 return -10;
475 }
476
477 result->timezone_minutes = RASQAL_GOOD_CAST(short int, result->timezone_minutes + RASQAL_GOOD_CAST(short int, (is_neg ? -t : t)));
478 }
479
480 /* failure if extra chars after the timezone part */
481 if(*p)
482 return -11;
483
484 }
485
486 /* Initialise field even though this is not valid before
487 * rasqal_xsd_datetime_normalize() is called on this object.
488 */
489 result->time_on_timeline = 0;
490
491 return 0;
492 }
493
494
495 static int
rasqal_xsd_date_parse(const char * date_string,rasqal_xsd_date * result)496 rasqal_xsd_date_parse(const char *date_string, rasqal_xsd_date *result)
497 {
498 rasqal_xsd_datetime dt_result; /* on stack */
499 int rc;
500
501 rc = rasqal_xsd_datetime_parse(date_string, &dt_result, 0);
502 if(!rc) {
503 result->year = dt_result.year;
504 result->month = dt_result.month;
505 result->day = dt_result.day;
506 result->time_on_timeline = dt_result.time_on_timeline;
507 result->timezone_minutes = dt_result.timezone_minutes;
508 result->have_tz = dt_result.have_tz; /* This will be N or Z */
509 }
510
511 return rc;
512 }
513
514 #ifdef STANDALONE
515 /**
516 * rasqal_xsd_date_normalize:
517 * @date: date
518 *
519 * INTERNAL - Normalize a date into the allowed range
520 *
521 * Return value: zero on success, non zero on failure.
522 */
523 static int
rasqal_xsd_date_normalize(rasqal_xsd_date * date)524 rasqal_xsd_date_normalize(rasqal_xsd_date *date)
525 {
526 rasqal_xsd_datetime dt_result; /* on stack */
527 int rc;
528
529 memset(&dt_result, '\0', sizeof(dt_result));
530
531 dt_result.year = date->year;
532 dt_result.month = date->month;
533 dt_result.day = date->day;
534 /* set to center of day interval (noon) */
535 dt_result.hour = 12;
536 dt_result.minute = 0;
537 dt_result.second = 0;
538 dt_result.microseconds = 0;
539 dt_result.timezone_minutes = date->timezone_minutes;
540 dt_result.have_tz = date->have_tz;
541
542 rc = rasqal_xsd_datetime_normalize(&dt_result);
543 if(!rc) {
544 date->year = dt_result.year;
545 date->month = dt_result.month;
546 date->day = dt_result.day;
547 date->time_on_timeline = dt_result.time_on_timeline;
548 date->timezone_minutes = dt_result.timezone_minutes;
549 date->have_tz = dt_result.have_tz; /* This will be N or Z */
550 }
551
552 return rc;
553 }
554 #endif /* STANDALONE */
555
556
557
558 /**
559 * rasqal_new_xsd_datetime:
560 * @world: world object
561 * @datetime_string: XSD Datetime string
562 *
563 * Constructor - make a new XSD datetime object from a string
564 *
565 * Return value: new datetime or NULL on failure
566 */
567 rasqal_xsd_datetime*
rasqal_new_xsd_datetime(rasqal_world * world,const char * datetime_string)568 rasqal_new_xsd_datetime(rasqal_world* world, const char *datetime_string)
569 {
570 rasqal_xsd_datetime* dt;
571 int rc = 0;
572
573 dt = RASQAL_MALLOC(rasqal_xsd_datetime*, sizeof(*dt));
574 if(!dt)
575 return NULL;
576
577 rc = rasqal_xsd_datetime_parse(datetime_string, dt, 1);
578 if(!rc) {
579 rasqal_xsd_datetime dt_temp; /* copy on stack to normalize */
580 memcpy(&dt_temp, dt, sizeof(dt_temp));
581
582 rc = rasqal_xsd_datetime_normalize(&dt_temp);
583 if(!rc)
584 dt->time_on_timeline = dt_temp.time_on_timeline;
585 }
586
587 if(rc) {
588 rasqal_free_xsd_datetime(dt); dt = NULL;
589 }
590
591 return dt;
592 }
593
594
595 /**
596 * rasqal_new_xsd_datetime_from_unixtime:
597 * @world: world object
598 * @secs: unixtime
599 *
600 * Constructor - make a new XSD datetime object from unixtime seconds
601 *
602 * Return value: new datetime or NULL on failure
603 */
604 rasqal_xsd_datetime*
rasqal_new_xsd_datetime_from_unixtime(rasqal_world * world,time_t secs)605 rasqal_new_xsd_datetime_from_unixtime(rasqal_world* world, time_t secs)
606 {
607 rasqal_xsd_datetime* dt;
608 int rc = 0;
609
610 dt = RASQAL_MALLOC(rasqal_xsd_datetime*, sizeof(*dt));
611 if(!dt)
612 return NULL;
613
614 rc = rasqal_xsd_datetime_set_from_unixtime(dt, secs);
615
616 if(rc) {
617 rasqal_free_xsd_datetime(dt); dt = NULL;
618 }
619
620 return dt;
621 }
622
623
624 /**
625 * rasqal_new_xsd_datetime_from_timeval:
626 * @world: world object
627 * @tv: pointer to struct timeval
628 *
629 * Constructor - make a new XSD datetime object from a timeval
630 *
631 * Return value: new datetime or NULL on failure
632 */
633 rasqal_xsd_datetime*
rasqal_new_xsd_datetime_from_timeval(rasqal_world * world,struct timeval * tv)634 rasqal_new_xsd_datetime_from_timeval(rasqal_world* world, struct timeval *tv)
635 {
636 rasqal_xsd_datetime* dt;
637 int rc = 0;
638
639 dt = RASQAL_MALLOC(rasqal_xsd_datetime*, sizeof(*dt));
640 if(!dt)
641 return NULL;
642
643 rc = rasqal_xsd_datetime_set_from_timeval(dt, tv);
644
645 if(rc) {
646 rasqal_free_xsd_datetime(dt); dt = NULL;
647 }
648
649 return dt;
650 }
651
652
653 /**
654 * rasqal_new_xsd_datetime_from_xsd_date:
655 * @world: world object
656 * @date: pointer to XSD date
657 *
658 * Constructor - make a new XSD datetime object from an XSD date
659 *
660 * Return value: new datetime or NULL on failure
661 */
662 rasqal_xsd_datetime*
rasqal_new_xsd_datetime_from_xsd_date(rasqal_world * world,rasqal_xsd_date * date)663 rasqal_new_xsd_datetime_from_xsd_date(rasqal_world* world, rasqal_xsd_date *date)
664 {
665 rasqal_xsd_datetime* dt;
666
667 dt = RASQAL_CALLOC(rasqal_xsd_datetime*, 1, sizeof(*dt));
668 if(!dt)
669 return NULL;
670
671 dt->year = date->year;
672 dt->month = date->month;
673 dt->day = date->day;
674 /* hour, minute, seconds, microseconds are all zero from calloc */
675 dt->timezone_minutes = date->timezone_minutes;
676 dt->time_on_timeline = date->time_on_timeline;
677 dt->have_tz = date->have_tz;
678
679 return dt;
680 }
681
682
683 /**
684 * rasqal_free_xsd_datetime:
685 * @dt: datetime object
686 *
687 * Destroy XSD datetime object.
688 **/
689 void
rasqal_free_xsd_datetime(rasqal_xsd_datetime * dt)690 rasqal_free_xsd_datetime(rasqal_xsd_datetime* dt)
691 {
692 if(!dt)
693 return;
694
695 RASQAL_FREE(datetime, dt);
696 }
697
698
699 #define TIMEZONE_BUFFER_LEN 6
700
701 /*
702 * rasqal_xsd_timezone_format:
703 * @timezone_minutes: timezone minutes from #rasqal_xsd_datetime or #rasqal_xsd_date
704 * @have_tz: have tz flag from #rasqal_xsd_datetime or #rasqal_xsd_date
705 * @buffer: buffer to write the formatted timezone
706 * @bufsize: length of @buffer; must be 7 or larger
707 *
708 * INTERNAL - format a timezone into the passed in buffer
709 *
710 * Return value: size of buffer or 0 on failure
711 */
712 static int
rasqal_xsd_timezone_format(signed short timezone_minutes,char have_tz,char * buffer,size_t bufsize)713 rasqal_xsd_timezone_format(signed short timezone_minutes,
714 char have_tz,
715 char* buffer, size_t bufsize)
716 {
717 size_t tz_len;
718
719 if(!buffer || !bufsize)
720 return -1;
721
722 if(have_tz == 'N') {
723 tz_len = 0;
724
725 buffer[0] = '\0';
726 } else if(have_tz == 'Z') {
727 tz_len = 1;
728 if(bufsize < (tz_len + 1))
729 return -1;
730
731 buffer[0] = 'Z';
732 buffer[1] = '\0';
733 } else {
734 int mins;
735 int hours;
736 int digit;
737
738 tz_len = TIMEZONE_BUFFER_LEN;
739
740 if(bufsize < (tz_len + 1))
741 return -1;
742
743 mins = abs(timezone_minutes);
744 buffer[0] = (!mins || mins != timezone_minutes ? '-' : '+');
745
746 hours = (mins / 60);
747 digit = (hours / 10);
748 buffer[1] = RASQAL_GOOD_CAST(char, digit + '0');
749 buffer[2] = RASQAL_GOOD_CAST(char, hours - (digit * 10) + '0');
750 buffer[3] = ':';
751
752 mins -= hours * 60;
753 buffer[4] = RASQAL_GOOD_CAST(char, (mins / 10) + '0');
754 mins -= mins * 10;
755 buffer[5] = RASQAL_GOOD_CAST(char, mins + '0');
756
757 buffer[6] = '\0';
758 }
759
760 return RASQAL_GOOD_CAST(int, tz_len);
761 }
762
763
764 static int
rasqal_xsd_format_microseconds(char * buffer,size_t bufsize,unsigned int microseconds)765 rasqal_xsd_format_microseconds(char* buffer, size_t bufsize,
766 unsigned int microseconds)
767 {
768 int len = 0;
769 char *p;
770 unsigned int value;
771 unsigned int base = 10;
772 unsigned int multiplier;
773
774 value = microseconds;
775 multiplier = 100000;
776 do {
777 value = value % multiplier;
778 multiplier /= base;
779 len++;
780 } while(value && multiplier);
781
782 if(!buffer || RASQAL_GOOD_CAST(int, bufsize) < (len + 1)) /* +1 for NUL */
783 return len;
784
785 value = microseconds;
786 multiplier = 100000;
787 p = buffer;
788 do {
789 unsigned digit = value / multiplier;
790 *p++ = RASQAL_GOOD_CAST(char, '0' + digit);
791 value = value % multiplier;
792 multiplier /= base;
793 } while(value && multiplier);
794 *p = '\0';
795
796 return len;
797 }
798
799
800 /**
801 * rasqal_xsd_datetime_to_counted_string:
802 * @dt: datetime struct
803 * @len_p: output length (or NULL)
804 *
805 * Convert a #rasqal_xsd_datetime struct to a xsd:dateTime lexical form counted string.
806 *
807 * Caller should rasqal_free_memory() the returned string.
808 *
809 * See http://www.w3.org/TR/xmlschema-2/#dateTime-canonical-representation
810 *
811 * Return value: lexical form string or NULL on failure.
812 */
813 char*
rasqal_xsd_datetime_to_counted_string(const rasqal_xsd_datetime * dt,size_t * len_p)814 rasqal_xsd_datetime_to_counted_string(const rasqal_xsd_datetime *dt,
815 size_t *len_p)
816 {
817 size_t len;
818 char *buffer = NULL;
819 char *p;
820 /* "[+-]HH:MM\0" */
821 char timezone_string[TIMEZONE_BUFFER_LEN + 1];
822 size_t year_len;
823 int tz_string_len;
824 size_t microseconds_len = 0;
825
826 /*
827 * http://www.w3.org/TR/xmlschema-2/#dateTime-canonical-representation
828 *
829 * "Except for trailing fractional zero digits in the seconds representation,
830 * '24:00:00' time representations, and timezone (for timezoned values),
831 * the mapping from literals to values is one-to-one.
832 * Where there is more than one possible representation,
833 * the canonical representation is as follows:
834 * * The 2-digit numeral representing the hour must not be '24';
835 * * The fractional second string, if present, must not end in '0';
836 * * for timezoned values, the timezone must be represented with 'Z'
837 * (All timezoned dateTime values are UTC.)."
838 */
839
840 if(!dt)
841 return NULL;
842
843 tz_string_len = rasqal_xsd_timezone_format(dt->timezone_minutes, dt->have_tz,
844 timezone_string,
845 TIMEZONE_BUFFER_LEN + 1);
846 if(tz_string_len < 0)
847 return NULL;
848
849 year_len = rasqal_format_integer(NULL, 0, dt->year, 4, '0');
850
851 len = year_len +
852 RASQAL_GOOD_CAST(size_t, 15) + /* "-MM-DDTHH:MM:SS" = 15 */
853 RASQAL_GOOD_CAST(size_t, tz_string_len);
854 if(dt->microseconds) {
855 microseconds_len = RASQAL_GOOD_CAST(size_t, rasqal_xsd_format_microseconds(NULL, 0,
856 RASQAL_GOOD_CAST(unsigned int, dt->microseconds)));
857 len += 1 /* . */ + microseconds_len;
858 }
859
860 if(len_p)
861 *len_p = len;
862
863 buffer = RASQAL_MALLOC(char*, len + 1);
864 if(!buffer)
865 return NULL;
866
867 p = buffer;
868 p += rasqal_format_integer(p, year_len + 1, dt->year, 4, '0');
869 *p++ = '-';
870 p += rasqal_format_integer(p, 2 + 1, dt->month, 2, '0');
871 *p++ = '-';
872 p += rasqal_format_integer(p, 2 + 1, dt->day, 2, '0');
873 *p++ = 'T';
874
875 p += rasqal_format_integer(p, 2 + 1, dt->hour, 2, '0');
876 *p++ = ':';
877 p += rasqal_format_integer(p, 2 + 1, dt->minute, 2, '0');
878 *p++ = ':';
879 p += rasqal_format_integer(p, 2 + 1, dt->second, 2, '0');
880
881 if(dt->microseconds) {
882 *p++ = '.';
883 p += rasqal_xsd_format_microseconds(p, microseconds_len + 1,
884 RASQAL_GOOD_CAST(unsigned int, dt->microseconds));
885 }
886 if(tz_string_len) {
887 memcpy(p, timezone_string, RASQAL_GOOD_CAST(size_t, tz_string_len));
888 p += tz_string_len;
889 }
890
891 *p = '\0';
892
893 return buffer;
894 }
895
896
897 /**
898 * rasqal_xsd_datetime_to_string:
899 * @dt: datetime struct
900 *
901 * Convert a #rasqal_xsd_datetime struct to a xsd:dateTime lexical form string.
902 *
903 * Caller should rasqal_free_memory() the returned string.
904 *
905 * Return value: lexical form string or NULL on failure.
906 */
907 char*
rasqal_xsd_datetime_to_string(const rasqal_xsd_datetime * dt)908 rasqal_xsd_datetime_to_string(const rasqal_xsd_datetime *dt)
909 {
910 return rasqal_xsd_datetime_to_counted_string(dt, NULL);
911 }
912
913
914 /**
915 * rasqal_xsd_datetime_equals2:
916 * @dt1: first XSD dateTime
917 * @dt2: second XSD dateTime
918 * @incomparible_p: address to store incomparable flag (or NULL)
919 *
920 * Compare two XSD dateTimes for equality.
921 *
922 * Return value: non-0 if equal.
923 **/
924 int
rasqal_xsd_datetime_equals2(const rasqal_xsd_datetime * dt1,const rasqal_xsd_datetime * dt2,int * incomparible_p)925 rasqal_xsd_datetime_equals2(const rasqal_xsd_datetime *dt1,
926 const rasqal_xsd_datetime *dt2,
927 int *incomparible_p)
928 {
929 int cmp = rasqal_xsd_datetime_compare2(dt1, dt2, incomparible_p);
930 return !cmp;
931 }
932
933
934 #ifndef RASQAL_DISABLE_DEPRECATED
935 /**
936 * rasqal_xsd_datetime_equals:
937 * @dt1: first XSD dateTime
938 * @dt2: second XSD dateTime
939 *
940 * Compare two XSD dateTimes for equality.
941 *
942 * @Deprecated: for rasqal_xsd_datetime_equals2 that returns incomparibility.
943 *
944 * Return value: non-0 if equal.
945 **/
946 int
rasqal_xsd_datetime_equals(const rasqal_xsd_datetime * dt1,const rasqal_xsd_datetime * dt2)947 rasqal_xsd_datetime_equals(const rasqal_xsd_datetime *dt1,
948 const rasqal_xsd_datetime *dt2)
949 {
950 return rasqal_xsd_datetime_equals2(dt1, dt2, NULL);
951 }
952 #endif
953
954 /*
955 * 3.2.7.4 Order relation on dateTime
956 * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#dateTime
957 */
958 static int
rasqal_xsd_timeline_compare(time_t dt_timeline1,signed int dt_msec1,signed short tz_minutes1,time_t dt_timeline2,signed int dt_msec2,signed short tz_minutes2,int * incomparible_p)959 rasqal_xsd_timeline_compare(time_t dt_timeline1, signed int dt_msec1,
960 signed short tz_minutes1,
961 time_t dt_timeline2, signed int dt_msec2,
962 signed short tz_minutes2,
963 int *incomparible_p)
964 {
965 int dt1_has_tz = (tz_minutes1 != RASQAL_XSD_DATETIME_NO_TZ);
966 int dt2_has_tz = (tz_minutes2 != RASQAL_XSD_DATETIME_NO_TZ);
967 int rc;
968
969 #define SECS_FOR_14_HOURS (14 * 3600)
970
971 /* Normalize - if there is a timezone that is not Z, convert it to Z
972 *
973 * Already done in rasqal_xsd_datetime_normalize() on construction
974 */
975
976 if(dt1_has_tz == dt2_has_tz) {
977 /* both are on same timeline */
978 if(dt_timeline1 < dt_timeline2)
979 rc = -1;
980 else if(dt_timeline1 > dt_timeline2)
981 rc = 1;
982 else
983 rc = dt_msec1 - dt_msec2;
984 } else if(dt1_has_tz) {
985 /* dt1 has a tz, dt2 has no tz */
986 if(dt_timeline1 < (dt_timeline2 - SECS_FOR_14_HOURS))
987 rc = -1;
988 else if(dt_timeline1 > (dt_timeline2 + SECS_FOR_14_HOURS))
989 rc = 1;
990 else {
991 if(incomparible_p)
992 *incomparible_p = 1;
993 rc = 2; /* incomparible really */
994 }
995 } else {
996 /* dt1 has no tz, dt2 has a tz */
997 if((dt_timeline1 + SECS_FOR_14_HOURS) < dt_timeline2)
998 rc = -1;
999 else if((dt_timeline1 - SECS_FOR_14_HOURS) > dt_timeline2)
1000 rc = 1;
1001 else {
1002 if(incomparible_p)
1003 *incomparible_p = 1;
1004 rc = 2; /* incomparible really */
1005 }
1006 }
1007
1008 return rc;
1009 }
1010
1011
1012 /**
1013 * rasqal_xsd_datetime_compare2:
1014 * @dt1: first XSD dateTime
1015 * @dt2: second XSD dateTime
1016 * @incomparible_p: address to store incomparable flag (or NULL)
1017 *
1018 * Compare two XSD dateTimes
1019 *
1020 * If the only one of the two dateTimes have timezones, the results
1021 * may be incomparible and that will return >0 and set the
1022 * value of the int point to by @incomparible_p to non-0
1023 *
1024 * Return value: <0 if @dt1 is less than @dt2, 0 if equal, >0 otherwise
1025 **/
1026 int
rasqal_xsd_datetime_compare2(const rasqal_xsd_datetime * dt1,const rasqal_xsd_datetime * dt2,int * incomparible_p)1027 rasqal_xsd_datetime_compare2(const rasqal_xsd_datetime *dt1,
1028 const rasqal_xsd_datetime *dt2,
1029 int *incomparible_p)
1030 {
1031 if(incomparible_p)
1032 *incomparible_p = 0;
1033
1034 /* Handle NULLs */
1035 if(!dt1 || !dt2) {
1036 /* NULLs sort earlier. equal only if both are NULL */
1037 if(!dt1 && !dt2)
1038 return 0;
1039
1040 return (!dt1) ? -1 : 1;
1041 }
1042
1043 return rasqal_xsd_timeline_compare(dt1->time_on_timeline, dt1->microseconds,
1044 dt1->timezone_minutes,
1045 dt2->time_on_timeline, dt2->microseconds,
1046 dt2->timezone_minutes,
1047 incomparible_p);
1048 }
1049
1050 #ifndef RASQAL_DISABLE_DEPRECATED
1051 /**
1052 * rasqal_xsd_datetime_compare:
1053 * @dt1: first XSD dateTime
1054 * @dt2: second XSD dateTime
1055 *
1056 * Compare two XSD dateTimes
1057 *
1058 * @Deprecated for rasqal_xsd_datetime_compare2() which can return the incomparible result.
1059 *
1060 * Return value: <0 if @dt1 is less than @dt2, 0 if equal, >0 otherwise
1061 **/
1062 int
rasqal_xsd_datetime_compare(const rasqal_xsd_datetime * dt1,const rasqal_xsd_datetime * dt2)1063 rasqal_xsd_datetime_compare(const rasqal_xsd_datetime *dt1,
1064 const rasqal_xsd_datetime *dt2)
1065 {
1066 return rasqal_xsd_datetime_compare2(dt1, dt2, NULL);
1067 }
1068 #endif
1069
1070 /**
1071 * rasqal_xsd_datetime_get_seconds_as_decimal:
1072 * @world: world object
1073 * @dt: XSD dateTime
1074 *
1075 * Get the seconds component of a dateTime as a decimal
1076 *
1077 * Return value: decimal object or NULL on failure
1078 **/
1079 rasqal_xsd_decimal*
rasqal_xsd_datetime_get_seconds_as_decimal(rasqal_world * world,rasqal_xsd_datetime * dt)1080 rasqal_xsd_datetime_get_seconds_as_decimal(rasqal_world* world,
1081 rasqal_xsd_datetime* dt)
1082 {
1083 rasqal_xsd_decimal* dec;
1084
1085 dec = rasqal_new_xsd_decimal(world);
1086 if(!dec)
1087 return NULL;
1088
1089 if(!dt->microseconds) {
1090 rasqal_xsd_decimal_set_long(dec, (long)dt->second);
1091 } else {
1092 /* Max len 9 "SS.UUUUUU\0" */
1093 char str[10];
1094
1095 sprintf(str, "%d.%06d", dt->second, dt->microseconds);
1096
1097 rasqal_xsd_decimal_set_string(dec, str);
1098 }
1099
1100 return dec;
1101 }
1102
1103
1104 /* xsd:date formatted length excluding formatted year length */
1105 #define DATE_BUFFER_LEN_NO_YEAR 6
1106
1107 /**
1108 * rasqal_xsd_date_to_counted_string:
1109 * @date: date struct
1110 * @len_p: output length (or NULL)
1111 *
1112 * Convert a #rasqal_xsd_date struct to a xsd:date lexical form string.
1113 *
1114 * Caller should rasqal_free_memory() the returned string.
1115 *
1116 * See http://www.w3.org/TR/xmlschema-2/#date-canonical-representation
1117 *
1118 * Return value: lexical form string or NULL on failure.
1119 */
1120 char*
rasqal_xsd_date_to_counted_string(const rasqal_xsd_date * date,size_t * len_p)1121 rasqal_xsd_date_to_counted_string(const rasqal_xsd_date *date, size_t *len_p)
1122 {
1123 char *buffer = NULL;
1124 size_t len;
1125 char *p;
1126 int value;
1127 unsigned int d;
1128 size_t year_len;
1129 /* "[+-]HH:MM\0" */
1130 char timezone_string[TIMEZONE_BUFFER_LEN + 1];
1131 int tz_string_len;
1132
1133 /* http://www.w3.org/TR/xmlschema-2/#date-canonical-representation
1134 *
1135 * "the date portion of the canonical representation (the entire
1136 * representation for nontimezoned values, and all but the timezone
1137 * representation for timezoned values) is always the date portion of
1138 * the dateTime canonical representation of the interval midpoint
1139 * (the dateTime representation, truncated on the right to eliminate
1140 * 'T' and all following characters). For timezoned values, append
1141 * the canonical representation of the ·recoverable timezone·. "
1142 *
1143 */
1144
1145 if(!date)
1146 return NULL;
1147
1148 tz_string_len = rasqal_xsd_timezone_format(date->timezone_minutes,
1149 date->have_tz,
1150 timezone_string,
1151 TIMEZONE_BUFFER_LEN + 1);
1152 if(tz_string_len < 0)
1153 return NULL;
1154
1155 year_len = rasqal_format_integer(NULL, 0, date->year, -1, '\0');
1156
1157 len = year_len + DATE_BUFFER_LEN_NO_YEAR + RASQAL_GOOD_CAST(size_t, tz_string_len);
1158
1159 if(len_p)
1160 *len_p = len;
1161
1162 buffer = RASQAL_MALLOC(char*, len + 1);
1163 if(!buffer)
1164 return NULL;
1165
1166 p = buffer;
1167
1168 /* value is year; length can vary */
1169 p += rasqal_format_integer(p, year_len + 1, date->year, -1, '\0');
1170
1171 *p++ = '-';
1172
1173 /* value is 2-digit month */
1174 value = date->month;
1175 d = RASQAL_GOOD_CAST(unsigned int, (value / 10));
1176 *p++ = RASQAL_GOOD_CAST(char, d + '0');
1177 value -= RASQAL_GOOD_CAST(int, d * 10);
1178 *p++ = RASQAL_GOOD_CAST(char, value + '0');
1179
1180 *p++ = '-';
1181
1182 /* value is 2-digit day */
1183 value = date->day;
1184 d = RASQAL_GOOD_CAST(unsigned int, (value / 10));
1185 *p++ = RASQAL_GOOD_CAST(char, d + '0');
1186 value -= RASQAL_GOOD_CAST(int, d * 10);
1187 *p++ = RASQAL_GOOD_CAST(char, value + '0');
1188
1189 if(tz_string_len) {
1190 memcpy(p, timezone_string, RASQAL_GOOD_CAST(size_t, tz_string_len));
1191 p += tz_string_len;
1192 }
1193
1194 *p = '\0';
1195
1196 return buffer;
1197 }
1198
1199
1200 /**
1201 * rasqal_xsd_date_to_string:
1202 * @d: date struct
1203 *
1204 * Convert a #rasqal_xsd_date struct to a xsd:date lexical form string.
1205 *
1206 * Caller should rasqal_free_memory() the returned string.
1207 *
1208 * Return value: lexical form string or NULL on failure.
1209 */
1210 char*
rasqal_xsd_date_to_string(const rasqal_xsd_date * d)1211 rasqal_xsd_date_to_string(const rasqal_xsd_date *d)
1212 {
1213 return rasqal_xsd_date_to_counted_string(d, NULL);
1214 }
1215
1216
1217 /**
1218 * days_per_month:
1219 * @month: month 1-12
1220 * @year: gregorian year
1221 *
1222 * INTERNAL - returns the number of days in given month and year.
1223 *
1224 * Return value: number of days or 0 on invalid arguments
1225 */
1226 static unsigned int
days_per_month(int month,int year)1227 days_per_month(int month, int year) {
1228 switch(month) {
1229 case 1:
1230 case 3:
1231 case 5:
1232 case 7:
1233 case 8:
1234 case 10:
1235 case 12:
1236 return 31;
1237
1238 case 4:
1239 case 6:
1240 case 9:
1241 case 11:
1242 return 30;
1243
1244 case 2:
1245 /* any of bottom 2 bits non-zero -> not 0 mod 4 -> not leap year */
1246 if(year & 3)
1247 return 28;
1248
1249 /* 0 mod 400 and 0 mod 4 -> leap year */
1250 if(!(year % 400))
1251 return 29;
1252
1253 /* 0 mod 100 and not 0 mod 400 and 0 mod 4 -> not leap year */
1254 if(!(year % 100))
1255 return 28;
1256
1257 /* other 0 mod 4 years -> leap year */
1258 return 29;
1259
1260 default:
1261 /* error */
1262 return 0;
1263 }
1264 }
1265
1266
1267 int
rasqal_xsd_datetime_check(const char * string)1268 rasqal_xsd_datetime_check(const char* string)
1269 {
1270 rasqal_xsd_datetime d;
1271
1272 /* This should be correct according to
1273 * http://www.w3.org/TR/xmlschema-2/#dateTime
1274 */
1275 return !rasqal_xsd_datetime_parse(string, &d, 1);
1276 }
1277
1278
1279 int
rasqal_xsd_date_check(const char * string)1280 rasqal_xsd_date_check(const char* string)
1281 {
1282 rasqal_xsd_date d;
1283
1284 /* This should be correct according to
1285 * http://www.w3.org/TR/xmlschema-2/#date
1286 */
1287 return !rasqal_xsd_date_parse(string, &d);
1288 }
1289
1290
1291 #define TM_YEAR_ORIGIN 1900
1292 #define TM_MONTH_ORIGIN 1
1293
1294 /**
1295 * rasqal_xsd_datetime_set_from_timeval:
1296 * @dt: datetime
1297 * @tv: timeval
1298 *
1299 * Set an XSD dateTime from a struct timeval pointer
1300 *
1301 * Returns: non-0 on failure
1302 **/
1303 int
rasqal_xsd_datetime_set_from_timeval(rasqal_xsd_datetime * dt,struct timeval * tv)1304 rasqal_xsd_datetime_set_from_timeval(rasqal_xsd_datetime *dt,
1305 struct timeval *tv)
1306 {
1307 struct tm* my_time;
1308 #ifdef HAVE_GMTIME_R
1309 struct tm time_buf;
1310 #endif
1311 time_t sec;
1312
1313 if(!dt || !tv)
1314 return 1;
1315
1316 sec = (time_t)tv->tv_sec;
1317 #ifdef HAVE_GMTIME_R
1318 memset(&time_buf, '\0', sizeof(time_buf));
1319 my_time = gmtime_r(&sec, &time_buf);
1320 #else
1321 my_time = gmtime(&sec);
1322 #endif
1323 if(!my_time)
1324 return 1;
1325
1326 dt->year = my_time->tm_year + TM_YEAR_ORIGIN;
1327 dt->month = RASQAL_GOOD_CAST(unsigned char, my_time->tm_mon + TM_MONTH_ORIGIN);
1328 dt->day = RASQAL_GOOD_CAST(unsigned char, my_time->tm_mday);
1329 dt->hour = RASQAL_GOOD_CAST(signed char, my_time->tm_hour);
1330 dt->minute = RASQAL_GOOD_CAST(signed char, my_time->tm_min);
1331 dt->second = RASQAL_GOOD_CAST(signed char, my_time->tm_sec);
1332 dt->microseconds = RASQAL_GOOD_CAST(int, tv->tv_usec);
1333 dt->timezone_minutes = 0; /* always Zulu time */
1334 dt->have_tz = 'Z';
1335
1336 return 0;
1337 }
1338
1339
1340 /**
1341 * rasqal_xsd_datetime_set_from_unixtime:
1342 * @dt: date time
1343 * @clock: unix time in seconds
1344 *
1345 * Set an XSD dateTime from unixtime seconds
1346 *
1347 * Returns: non-0 on failure
1348 **/
1349 int
rasqal_xsd_datetime_set_from_unixtime(rasqal_xsd_datetime * dt,time_t secs)1350 rasqal_xsd_datetime_set_from_unixtime(rasqal_xsd_datetime* dt,
1351 time_t secs)
1352 {
1353 struct timeval tv;
1354
1355 if(!dt)
1356 return 1;
1357
1358 tv.tv_sec = secs;
1359 tv.tv_usec = 0;
1360
1361 return rasqal_xsd_datetime_set_from_timeval(dt, &tv);
1362 }
1363
1364
1365 /**
1366 * rasqal_xsd_datetime_get_as_unixtime:
1367 * @dt: datetime
1368 *
1369 * Get a datetime as unix seconds
1370 *
1371 * Returns: unix seconds or 0 if @dt is NULL
1372 **/
1373 time_t
rasqal_xsd_datetime_get_as_unixtime(rasqal_xsd_datetime * dt)1374 rasqal_xsd_datetime_get_as_unixtime(rasqal_xsd_datetime* dt)
1375 {
1376 struct tm time_buf;
1377
1378 if(!dt)
1379 return 0;
1380
1381 memset(&time_buf, '\0', sizeof(time_buf));
1382
1383 time_buf.tm_year = dt->year - TM_YEAR_ORIGIN;
1384 time_buf.tm_mon = dt->month - TM_MONTH_ORIGIN;
1385 time_buf.tm_mday = dt->day;
1386 time_buf.tm_hour = dt->hour;
1387 time_buf.tm_min = dt->minute;
1388 time_buf.tm_sec = dt->second;
1389 time_buf.tm_wday = 0;
1390 time_buf.tm_yday = 0;
1391 time_buf.tm_isdst = -1;
1392
1393 #ifdef HAVE_TM_GMTOFF
1394 if(dt->timezone_minutes == RASQAL_XSD_DATETIME_NO_TZ)
1395 time_buf.tm_gmtoff = 0;
1396 else
1397 time_buf.tm_gmtoff = dt->timezone_minutes * 60;
1398 #endif
1399
1400 return rasqal_timegm(&time_buf);
1401 }
1402
1403
1404 /**
1405 * rasqal_xsd_datetime_get_as_timeval:
1406 * @dt: datetime
1407 *
1408 * Get a datetime as struct timeval
1409 *
1410 * The returned timeval must be freed by the caller such as using
1411 * rasqal_free_memory().
1412 *
1413 * Returns: pointer to a new timeval structure or NULL on failure
1414 **/
1415 struct timeval*
rasqal_xsd_datetime_get_as_timeval(rasqal_xsd_datetime * dt)1416 rasqal_xsd_datetime_get_as_timeval(rasqal_xsd_datetime *dt)
1417 {
1418 struct timeval *tv;
1419
1420 if(!dt)
1421 return NULL;
1422
1423 tv = RASQAL_CALLOC(struct timeval*, 1, sizeof(*tv));
1424 if(!tv)
1425 return NULL;
1426
1427 tv->tv_sec = rasqal_xsd_datetime_get_as_unixtime(dt);
1428 tv->tv_usec = dt->microseconds;
1429
1430 return tv;
1431 }
1432
1433
1434 /**
1435 * rasqal_xsd_datetime_get_timezone_as_counted_string:
1436 * @dt: datetime
1437 * @len_p: pointer to store returned string length
1438 *
1439 * Get the timezone of a datetime as a duration format string with optional length count
1440 *
1441 * The returned string is owned by the caller and must be freed
1442 * by rasqal_free_memory().
1443 *
1444 * Returns: pointer to a new string or NULL on failure
1445 **/
1446 char*
rasqal_xsd_datetime_get_timezone_as_counted_string(rasqal_xsd_datetime * dt,size_t * len_p)1447 rasqal_xsd_datetime_get_timezone_as_counted_string(rasqal_xsd_datetime *dt,
1448 size_t *len_p)
1449 {
1450 /* timezone duration as implemented here is a signed integer number
1451 * of seconds like +- 14 hours:minutes (no days or larger units, no
1452 * seconds or smaller).
1453 *
1454 * When written in the canonical format, a restricted
1455 * xsd:dayTimeDuration format, it is constraint to a format like -?PThhmm
1456 *
1457 * For example: -PT14H59M PT14H59M and PT0S for a zero timezone offset
1458 */
1459 #define TZ_STR_SIZE 10
1460 char* tz_str;
1461 char* p;
1462 int minutes;
1463 unsigned int hours;
1464
1465 if(!dt)
1466 return NULL;
1467
1468 minutes = dt->timezone_minutes;
1469 if(minutes == RASQAL_XSD_DATETIME_NO_TZ)
1470 return NULL;
1471
1472 tz_str = RASQAL_MALLOC(char*, TZ_STR_SIZE + 1);
1473 if(!tz_str)
1474 return NULL;
1475
1476 p = tz_str;
1477
1478 if(minutes < 0) {
1479 *p++ = '-';
1480 minutes = -minutes;
1481 }
1482
1483 *p++ = 'P';
1484 *p++ = 'T';
1485
1486 hours = RASQAL_GOOD_CAST(unsigned int, (minutes / 60));
1487 if(hours) {
1488 #if 1
1489 if(hours > 9) {
1490 *p++ = RASQAL_GOOD_CAST(char, '0' + (hours / 10));
1491 hours %= 10;
1492 }
1493 *p++ = RASQAL_GOOD_CAST(char, '0' + hours);
1494 *p++ = 'H';
1495 #else
1496 p += sprintf(p, "%dH", hours);
1497 #endif
1498 minutes -= RASQAL_GOOD_CAST(int, hours * 60);
1499 }
1500
1501 if(minutes) {
1502 #if 1
1503 if(minutes > 9) {
1504 *p++ = RASQAL_GOOD_CAST(char, '0' + (minutes / 10));
1505 minutes %= 10;
1506 }
1507 *p++ = RASQAL_GOOD_CAST(char, '0' + minutes);
1508 *p++ = 'M';
1509 #else
1510 p += sprintf(p, "%dM", minutes);
1511 #endif
1512 }
1513
1514 if(!dt->timezone_minutes) {
1515 *p++ = '0';
1516 *p++ = 'S';
1517 }
1518
1519 *p = '\0';
1520
1521 if(len_p)
1522 *len_p = RASQAL_GOOD_CAST(size_t, p - tz_str);
1523
1524 return tz_str;
1525 }
1526
1527
1528 /**
1529 * rasqal_xsd_datetime_get_tz_as_counted_string:
1530 * @dt: datetime
1531 * @len_p: pointer to store returned string length
1532 *
1533 * Get the timezone of a datetime as a timezone string
1534 *
1535 * The returned string is owned by the caller and must be freed
1536 * by rasqal_free_memory().
1537 *
1538 * Returns: pointer to a new string or NULL on failure
1539 **/
1540 char*
rasqal_xsd_datetime_get_tz_as_counted_string(rasqal_xsd_datetime * dt,size_t * len_p)1541 rasqal_xsd_datetime_get_tz_as_counted_string(rasqal_xsd_datetime* dt,
1542 size_t *len_p)
1543 {
1544 char* s;
1545
1546 s = RASQAL_MALLOC(char*, TIMEZONE_BUFFER_LEN + 1);
1547 if(!s)
1548 return NULL;
1549
1550 if(rasqal_xsd_timezone_format(dt->timezone_minutes, dt->have_tz,
1551 s, TIMEZONE_BUFFER_LEN + 1) < 0)
1552 goto failed;
1553
1554 if(len_p)
1555 *len_p = TIMEZONE_BUFFER_LEN;
1556
1557 return s;
1558
1559 failed:
1560 RASQAL_FREE(char*, s);
1561 return NULL;
1562 }
1563
1564
1565 /**
1566 * rasqal_new_xsd_date:
1567 * @world: world object
1568 * @date_string: XSD date string
1569 *
1570 * Constructor - make a new XSD date object from a string
1571 *
1572 * Return value: new datetime or NULL on failure
1573 */
1574 rasqal_xsd_date*
rasqal_new_xsd_date(rasqal_world * world,const char * date_string)1575 rasqal_new_xsd_date(rasqal_world* world, const char *date_string)
1576 {
1577 rasqal_xsd_datetime dt_result; /* on stack */
1578 rasqal_xsd_date* d;
1579 int rc = 0;
1580
1581 d = RASQAL_CALLOC(rasqal_xsd_date*, 1, sizeof(*d));
1582 if(!d)
1583 return NULL;
1584
1585 rc = rasqal_xsd_datetime_parse(date_string, &dt_result, 0);
1586 if(!rc) {
1587 d->year = dt_result.year;
1588 d->month = dt_result.month;
1589 d->day = dt_result.day;
1590 d->timezone_minutes = dt_result.timezone_minutes;
1591 d->have_tz = dt_result.have_tz;
1592
1593 dt_result.hour = 12; /* Noon */
1594 dt_result.minute = 0;
1595 dt_result.second = 0;
1596 dt_result.microseconds = 0;
1597
1598 rc = rasqal_xsd_datetime_normalize(&dt_result);
1599
1600 /* Track the starting instant as determined by the timezone */
1601 d->time_on_timeline = dt_result.time_on_timeline;
1602 if(d->timezone_minutes != RASQAL_XSD_DATETIME_NO_TZ)
1603 d->time_on_timeline += (60 * dt_result.timezone_minutes);
1604 }
1605
1606 if(rc) {
1607 rasqal_free_xsd_date(d); d = NULL;
1608 }
1609
1610 return d;
1611 }
1612
1613
1614 /**
1615 * rasqal_free_xsd_date:
1616 * @d: date object
1617 *
1618 * Destroy XSD date object.
1619 **/
1620 void
rasqal_free_xsd_date(rasqal_xsd_date * d)1621 rasqal_free_xsd_date(rasqal_xsd_date* d)
1622 {
1623 if(!d)
1624 return;
1625
1626 RASQAL_FREE(rasqal_xsd_date, d);
1627 }
1628
1629
1630 /**
1631 * rasqal_xsd_date_equals:
1632 * @d1: first XSD date
1633 * @d2: second XSD date
1634 * @incomparible_p: address to store incomparable flag (or NULL)
1635 *
1636 * Compare two XSD dates for equality.
1637 *
1638 * Return value: non-0 if equal.
1639 **/
1640 int
rasqal_xsd_date_equals(const rasqal_xsd_date * d1,const rasqal_xsd_date * d2,int * incomparible_p)1641 rasqal_xsd_date_equals(const rasqal_xsd_date *d1,
1642 const rasqal_xsd_date *d2,
1643 int *incomparible_p)
1644 {
1645 int cmp = rasqal_xsd_date_compare(d1, d2, incomparible_p);
1646 return !cmp;
1647 }
1648
1649
1650 /**
1651 * rasqal_xsd_date_compare:
1652 * @d1: first XSD date
1653 * @d2: second XSD date
1654 * @incomparible_p: address to store incomparable flag (or NULL)
1655 *
1656 * Compare two XSD dates
1657 *
1658 * If the only one of the two dates have timezones, the results
1659 * may be incomparible and that will return >0 and set the
1660 * value of the int point to by @incomparible_p to non-0
1661 *
1662 * Return value: <0 if @d1 is less than @d2, 0 if equal, >0 otherwise
1663 **/
1664 int
rasqal_xsd_date_compare(const rasqal_xsd_date * d1,const rasqal_xsd_date * d2,int * incomparible_p)1665 rasqal_xsd_date_compare(const rasqal_xsd_date *d1,
1666 const rasqal_xsd_date *d2,
1667 int *incomparible_p)
1668 {
1669 if(incomparible_p)
1670 *incomparible_p = 0;
1671
1672 /* Handle NULLs */
1673 if(!d1 || !d2) {
1674 /* NULLs sort earlier. equal only if both are NULL */
1675 if(!d1 && !d2)
1676 return 0;
1677
1678 return (!d1) ? -1 : 1;
1679 }
1680
1681 return rasqal_xsd_timeline_compare(d1->time_on_timeline, 0 /* msec */,
1682 d1->timezone_minutes,
1683 d2->time_on_timeline, 0 /* msec */,
1684 d2->timezone_minutes,
1685 incomparible_p);
1686 }
1687
1688
1689 #ifdef STANDALONE
1690 #include <stdio.h>
1691 #ifdef HAVE_SYS_TIME_H
1692 #include <sys/time.h>
1693 #endif
1694
1695 int main(int argc, char *argv[]);
1696
1697 #define MYASSERT(c) \
1698 if(!(c)) { \
1699 fprintf(stderr, "%s: assertion failed at %s:%d: %s\n", program, __FILE__, __LINE__, #c); \
1700 exit(1); \
1701 }
1702
1703 static int
test_datetime_parse_and_normalize(const char * datetime_string,rasqal_xsd_datetime * result)1704 test_datetime_parse_and_normalize(const char *datetime_string,
1705 rasqal_xsd_datetime *result)
1706 {
1707 if(rasqal_xsd_datetime_parse(datetime_string, result, 1))
1708 return 1;
1709
1710 return rasqal_xsd_datetime_normalize(result);
1711 }
1712
1713 static int
test_datetime_parser_tostring(const char * in_str,const char * out_expected)1714 test_datetime_parser_tostring(const char *in_str, const char *out_expected)
1715 {
1716 rasqal_xsd_datetime d; /* allocated on stack */
1717 char const *s = NULL;
1718 int r = 1;
1719
1720 if(!test_datetime_parse_and_normalize(in_str, &d)) {
1721 s = rasqal_xsd_datetime_to_string(&d);
1722 }
1723
1724 if(s) {
1725 r = strcmp(RASQAL_GOOD_CAST(char*, s), out_expected);
1726 if(r)
1727 fprintf(stderr, "input dateTime \"%s\" converted to canonical \"%s\", expected \"%s\"\n", in_str, s, out_expected);
1728 RASQAL_FREE(char*, s);
1729 } else
1730 fprintf(stderr, "input dateTime \"%s\" converted to canonical (null), expected \"%s\"\n", in_str, out_expected);
1731 return r;
1732 }
1733
1734
1735 static int
test_date_parse_and_normalize(const char * date_string,rasqal_xsd_date * result)1736 test_date_parse_and_normalize(const char *date_string,
1737 rasqal_xsd_date *result)
1738 {
1739 if(rasqal_xsd_date_parse(date_string, result))
1740 return 1;
1741
1742 return rasqal_xsd_date_normalize(result);
1743 }
1744
1745
1746 static int
test_date_parser_tostring(const char * in_str,const char * out_expected)1747 test_date_parser_tostring(const char *in_str, const char *out_expected)
1748 {
1749 rasqal_xsd_date d; /* allocated on stack */
1750 char const *s = NULL;
1751 int r = 1;
1752
1753 if(!test_date_parse_and_normalize(in_str, &d)) {
1754 s = rasqal_xsd_date_to_string(&d);
1755 }
1756
1757 if(s) {
1758 r = strcmp(RASQAL_GOOD_CAST(char*, s), out_expected);
1759 if(r)
1760 fprintf(stderr, "input date \"%s\" converted to canonical \"%s\", expected \"%s\"\n", in_str, s, out_expected);
1761 RASQAL_FREE(char*, s);
1762 } else
1763 fprintf(stderr, "input date \"%s\" converted to canonical (null), expected \"%s\"\n", in_str, out_expected);
1764 return r;
1765 }
1766
1767
1768 #define INCOMPARABLE 2
1769
1770 static int
test_date_equals(rasqal_world * world,const char * value1,const char * value2,int expected_eq)1771 test_date_equals(rasqal_world* world, const char *value1, const char *value2,
1772 int expected_eq)
1773 {
1774 rasqal_xsd_date* d1;
1775 rasqal_xsd_date* d2;
1776 int r = 1;
1777 int incomparable = 0;
1778 int eq;
1779
1780 d1 = rasqal_new_xsd_date(world, value1);
1781 d2 = rasqal_new_xsd_date(world, value2);
1782
1783 eq = rasqal_xsd_date_equals(d1, d2, &incomparable);
1784 if(incomparable)
1785 eq = INCOMPARABLE;
1786 rasqal_free_xsd_date(d1);
1787 rasqal_free_xsd_date(d2);
1788
1789 if(eq != expected_eq) {
1790 fprintf(stderr, "date equals \"%s\" to \"%s\" returned %d expected %d\n",
1791 value1, value2, eq, expected_eq);
1792 r = 1;
1793 }
1794 return r;
1795 }
1796
1797
1798 static int
test_date_not_equals(rasqal_world * world,const char * value1,const char * value2,int expected_neq)1799 test_date_not_equals(rasqal_world* world,
1800 const char *value1, const char *value2,
1801 int expected_neq)
1802 {
1803 rasqal_xsd_date* d1;
1804 rasqal_xsd_date* d2;
1805 int r = 1;
1806 int incomparable = 0;
1807 int neq;
1808
1809 d1 = rasqal_new_xsd_date(world, value1);
1810 d2 = rasqal_new_xsd_date(world, value2);
1811
1812 neq = !rasqal_xsd_date_equals(d1, d2, &incomparable);
1813 if(incomparable)
1814 neq = INCOMPARABLE;
1815 rasqal_free_xsd_date(d1);
1816 rasqal_free_xsd_date(d2);
1817
1818 if(neq != expected_neq) {
1819 fprintf(stderr,
1820 "date not equals \"%s\" to \"%s\" returned %d expected %d\n",
1821 value1, value2, neq, expected_neq);
1822 r = 1;
1823 }
1824 return r;
1825 }
1826
1827
1828 static int
test_date_compare(rasqal_world * world,const char * value1,const char * value2,int expected_cmp)1829 test_date_compare(rasqal_world* world, const char *value1, const char *value2,
1830 int expected_cmp)
1831 {
1832 rasqal_xsd_date* d1;
1833 rasqal_xsd_date* d2;
1834 int r = 1;
1835 int incomparable = 0;
1836 int cmp;
1837
1838 d1 = rasqal_new_xsd_date(world, value1);
1839 d2 = rasqal_new_xsd_date(world, value2);
1840
1841 cmp = rasqal_xsd_date_compare(d1, d2, &incomparable);
1842 if(incomparable)
1843 cmp = INCOMPARABLE;
1844 else if (cmp < 0)
1845 cmp = -1;
1846 else if (cmp > 0)
1847 cmp = 1;
1848
1849 rasqal_free_xsd_date(d1);
1850 rasqal_free_xsd_date(d2);
1851
1852 if(cmp != expected_cmp) {
1853 fprintf(stderr, "date compare \"%s\" to \"%s\" returned %d expected %d\n",
1854 value1, value2, cmp, expected_cmp);
1855 r = 1;
1856 }
1857 return r;
1858 }
1859
1860
1861 static int
test_datetime_equals(rasqal_world * world,const char * value1,const char * value2,int expected_eq)1862 test_datetime_equals(rasqal_world* world, const char *value1, const char *value2,
1863 int expected_eq)
1864 {
1865 rasqal_xsd_datetime* d1;
1866 rasqal_xsd_datetime* d2;
1867 int r = 1;
1868 int incomparable = 0;
1869 int eq;
1870
1871 d1 = rasqal_new_xsd_datetime(world, value1);
1872 d2 = rasqal_new_xsd_datetime(world, value2);
1873
1874 eq = rasqal_xsd_datetime_equals2(d1, d2, &incomparable);
1875 if(incomparable)
1876 eq = INCOMPARABLE;
1877 rasqal_free_xsd_datetime(d1);
1878 rasqal_free_xsd_datetime(d2);
1879
1880 if(eq != expected_eq) {
1881 fprintf(stderr,
1882 "datetime equals \"%s\" to \"%s\" returned %d expected %d\n",
1883 value1, value2, eq, expected_eq);
1884 r = 1;
1885 }
1886 return r;
1887 }
1888
1889
1890 static int
test_datetime_compare(rasqal_world * world,const char * value1,const char * value2,int expected_cmp)1891 test_datetime_compare(rasqal_world* world, const char *value1, const char *value2,
1892 int expected_cmp)
1893 {
1894 rasqal_xsd_datetime* d1;
1895 rasqal_xsd_datetime* d2;
1896 int r = 1;
1897 int incomparable = 0;
1898 int cmp;
1899
1900 d1 = rasqal_new_xsd_datetime(world, value1);
1901 d2 = rasqal_new_xsd_datetime(world, value2);
1902
1903 cmp = rasqal_xsd_datetime_compare2(d1, d2, &incomparable);
1904 if(incomparable)
1905 cmp = INCOMPARABLE;
1906 else if (cmp < 0)
1907 cmp = -1;
1908 else if (cmp > 0)
1909 cmp = 1;
1910
1911 rasqal_free_xsd_datetime(d1);
1912 rasqal_free_xsd_datetime(d2);
1913
1914 if(cmp != expected_cmp) {
1915 fprintf(stderr,
1916 "datetime compare \"%s\" to \"%s\" returned %d expected %d\n",
1917 value1, value2, cmp, expected_cmp);
1918 r = 1;
1919 }
1920 return r;
1921 }
1922
1923
1924 int
main(int argc,char * argv[])1925 main(int argc, char *argv[])
1926 {
1927 char const *program = rasqal_basename(*argv);
1928 rasqal_world* world;
1929 rasqal_xsd_datetime dt;
1930 rasqal_xsd_date d;
1931
1932 world = rasqal_new_world();
1933
1934 /* days_per_month */
1935
1936 MYASSERT(!days_per_month(0,287));
1937
1938 MYASSERT(days_per_month(1,467) == 31);
1939
1940 MYASSERT(days_per_month(2,1900) == 28);
1941 MYASSERT(days_per_month(2,1901) == 28);
1942 MYASSERT(days_per_month(2,2000) == 29);
1943 MYASSERT(days_per_month(2,2004) == 29);
1944
1945 MYASSERT(days_per_month(3,1955) == 31);
1946 MYASSERT(days_per_month(4,3612) == 30);
1947 MYASSERT(days_per_month(5,467) == 31);
1948 MYASSERT(days_per_month(6,398) == 30);
1949 MYASSERT(days_per_month(7,1832) == 31);
1950 MYASSERT(days_per_month(8,8579248) == 31);
1951 MYASSERT(days_per_month(9,843) == 30);
1952 MYASSERT(days_per_month(10,84409) == 31);
1953 MYASSERT(days_per_month(11,398) == 30);
1954 MYASSERT(days_per_month(12,4853) == 31);
1955 MYASSERT(!days_per_month(13,45894));
1956
1957 /* DATETIME */
1958
1959 /* rasqal_xsd_datetime_parse_and_normalize,
1960 rasqal_xsd_datetime_to_string and
1961 rasqal_xsd_datetime_string_to_canonical */
1962
1963 #define PARSE_AND_NORMALIZE_DATETIME(_s,_d) \
1964 test_datetime_parse_and_normalize(_s, _d)
1965
1966 /* generic */
1967
1968 MYASSERT(!rasqal_xsd_datetime_to_string(0));
1969
1970 MYASSERT(PARSE_AND_NORMALIZE_DATETIME(0,0));
1971 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("uhgsufi",0));
1972 MYASSERT(PARSE_AND_NORMALIZE_DATETIME(0 ,&dt));
1973 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("fsdhufhdsuifhidu", &dt));
1974
1975 /* year */
1976
1977 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("123-12-12T12:12:12Z", &dt));
1978 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("-123-12-12T12:12:12Z", &dt));
1979 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("0000-12-12T12:12:12Z", &dt));
1980 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("01234-12-12T12:12:12Z", &dt));
1981 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("-01234-12-12T12:12:12Z", &dt));
1982 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("1234a12-12T12:12:12Z", &dt));
1983 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("-1234b12-12T12:12:12Z", &dt));
1984 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("g162-12-12T12:12:12Z", &dt));
1985 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("5476574658746587465874-12-12T12:12:12Z", &dt));
1986
1987 MYASSERT(test_datetime_parser_tostring("1234-12-12T12:12:12Z", "1234-12-12T12:12:12Z") == 0);
1988 MYASSERT(test_datetime_parser_tostring("-1234-12-12T12:12:12Z", "-1234-12-12T12:12:12Z") == 0);
1989 MYASSERT(test_datetime_parser_tostring("1234567890-12-12T12:12:12Z", "1234567890-12-12T12:12:12Z") == 0);
1990 MYASSERT(test_datetime_parser_tostring("-1234567890-12-12T12:12:12Z", "-1234567890-12-12T12:12:12Z") == 0);
1991
1992 /* month */
1993
1994 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-v-12T12:12:12Z", &dt));
1995 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-00-12T12:12:12Z", &dt));
1996 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("PARSE_AND_NORMALIZE-011-12T12:12:12Z", &dt));
1997 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-13-12T12:12:12Z", &dt));
1998 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-12.12T12:12:12Z", &dt));
1999
2000 MYASSERT(test_datetime_parser_tostring("2004-01-01T12:12:12Z", "2004-01-01T12:12:12Z") == 0);
2001
2002 /* day */
2003
2004 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-ffT12:12:12Z", &dt));
2005 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-00T12:12:12Z", &dt));
2006 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-007T12:12:12Z", &dt));
2007 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-32T12:12:12Z", &dt));
2008 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01t12:12:12Z", &dt));
2009 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01- 1T12:12:12Z", &dt));
2010
2011 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2005-02-29T12:12:12Z", &dt));
2012 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2005-02-28T12:12:12Z", &dt));
2013 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-02-29T12:12:12Z", &dt));
2014 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2000-02-29T12:12:12Z", &dt));
2015 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("1900-02-29T12:12:12Z", &dt));
2016
2017 MYASSERT(test_datetime_parser_tostring("2012-04-12T12:12:12Z", "2012-04-12T12:12:12Z") == 0);
2018
2019 /* hour */
2020
2021 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01Tew:12:12Z", &dt));
2022 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T-1:12:12Z", &dt));
2023 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T001:12:12Z", &dt));
2024 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T25:12:12Z", &dt));
2025 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T01.12:12Z", &dt));
2026
2027 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T24:12:00Z", &dt));
2028 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T24:00:34Z", &dt));
2029 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T24:12:34Z", &dt));
2030 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T24:00:00Z", &dt));
2031
2032 MYASSERT(test_datetime_parser_tostring("2012-04-12T24:00:00", "2012-04-13T00:00:00") == 0);
2033
2034 /* minute */
2035
2036 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:ij:12Z", &dt));
2037 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:-1:12Z", &dt));
2038 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:042:12Z", &dt));
2039 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:69:12Z", &dt));
2040 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12.12Z", &dt));
2041
2042 /* second */
2043
2044 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:ijZ", &dt));
2045 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:-1", &dt));
2046 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:054Z", &dt));
2047 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:69Z", &dt));
2048 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12z", &dt));
2049
2050 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12", &dt));
2051
2052 /* fraction second */
2053
2054 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.", &dt));
2055 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.i", &dt));
2056 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.0", &dt));
2057 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.01", &dt));
2058 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.1", &dt));
2059 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.100", &dt));
2060 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.1000000000000000000000000000000000000000000", &dt));
2061 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.5798459847598743987549", &dt));
2062 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.1d", &dt));
2063 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12.1Z", &dt));
2064
2065 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.01Z", "2006-05-18T18:36:03.01Z") == 0);
2066 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.10Z", "2006-05-18T18:36:03.1Z") == 0);
2067 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.010Z", "2006-05-18T18:36:03.01Z") == 0);
2068 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.1234Z", "2006-05-18T18:36:03.1234Z") == 0);
2069 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.1234", "2006-05-18T18:36:03.1234") == 0);
2070 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.1239Z", "2006-05-18T18:36:03.1239Z") == 0);
2071 MYASSERT(test_datetime_parser_tostring("2006-05-18T18:36:03.1239", "2006-05-18T18:36:03.1239") == 0);
2072
2073 /* timezones + normalization */
2074
2075 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+", &dt));
2076 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12-", &dt));
2077 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+00.00", &dt));
2078 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+aa:bb", &dt));
2079 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+15:00", &dt));
2080 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+14:01", &dt));
2081 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+14:00", &dt));
2082 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12-14:01", &dt));
2083 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12-14:00", &dt));
2084 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+10:99", &dt));
2085 MYASSERT(!PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+10:59", &dt));
2086 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+10:059", &dt));
2087 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+010:59", &dt));
2088 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+10:59a", &dt));
2089 MYASSERT(PARSE_AND_NORMALIZE_DATETIME("2004-01-01T12:12:12+10:059", &dt));
2090
2091 MYASSERT(test_datetime_parser_tostring("2004-12-31T23:50:22-01:15", "2005-01-01T01:05:22Z") == 0);
2092 MYASSERT(test_datetime_parser_tostring("2005-01-01T01:00:05+02:12", "2004-12-31T22:48:05Z") == 0);
2093 MYASSERT(test_datetime_parser_tostring("0001-01-01T00:00:00+00:01", "-0001-12-31T23:59:00Z") == 0);
2094 MYASSERT(test_datetime_parser_tostring("-0001-12-31T23:59:00-00:01", "0001-01-01T00:00:00Z") == 0);
2095 MYASSERT(test_datetime_parser_tostring("2005-03-01T00:00:00+01:00", "2005-02-28T23:00:00Z") == 0);
2096 MYASSERT(test_datetime_parser_tostring("2004-03-01T00:00:00+01:00", "2004-02-29T23:00:00Z") == 0);
2097 MYASSERT(test_datetime_parser_tostring("2005-02-28T23:00:00-01:00", "2005-03-01T00:00:00Z") == 0);
2098 MYASSERT(test_datetime_parser_tostring("2004-02-29T23:00:00-01:00", "2004-03-01T00:00:00Z") == 0);
2099
2100
2101 /* DATE */
2102
2103 #define PARSE_AND_NORMALIZE_DATE(_s,_d) \
2104 test_date_parse_and_normalize(_s, _d)
2105
2106 /* generic */
2107
2108 MYASSERT(!rasqal_xsd_date_to_string(0));
2109
2110 MYASSERT(PARSE_AND_NORMALIZE_DATE(0,0));
2111 MYASSERT(PARSE_AND_NORMALIZE_DATE("uhgsufi",0));
2112 MYASSERT(PARSE_AND_NORMALIZE_DATE(0 ,&d));
2113 MYASSERT(PARSE_AND_NORMALIZE_DATE("fsdhufhdsuifhidu", &d));
2114
2115 /* year */
2116
2117 MYASSERT(PARSE_AND_NORMALIZE_DATE("123-12-12Z", &d));
2118 MYASSERT(PARSE_AND_NORMALIZE_DATE("-123-12-12Z", &d));
2119 MYASSERT(PARSE_AND_NORMALIZE_DATE("0000-12-12Z", &d));
2120 MYASSERT(PARSE_AND_NORMALIZE_DATE("01234-12-12Z", &d));
2121 MYASSERT(PARSE_AND_NORMALIZE_DATE("-01234-12-12Z", &d));
2122 MYASSERT(PARSE_AND_NORMALIZE_DATE("1234a12-12Z", &d));
2123 MYASSERT(PARSE_AND_NORMALIZE_DATE("-1234b12-12Z", &d));
2124 MYASSERT(PARSE_AND_NORMALIZE_DATE("g162-12-12Z", &d));
2125 MYASSERT(PARSE_AND_NORMALIZE_DATE("5476574658746587465874-12-12Z", &d));
2126
2127 MYASSERT(test_date_parser_tostring("1234-12-12Z", "1234-12-12Z") == 0);
2128 MYASSERT(test_date_parser_tostring("-1234-12-12Z", "-1234-12-12Z") == 0);
2129 MYASSERT(test_date_parser_tostring("1234567890-12-12Z", "1234567890-12-12Z") == 0);
2130 MYASSERT(test_date_parser_tostring("-1234567890-12-12Z", "-1234567890-12-12Z") == 0);
2131
2132 /* month */
2133
2134 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-v-12Z", &d));
2135 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-00-12Z", &d));
2136 MYASSERT(PARSE_AND_NORMALIZE_DATE("PARSE_AND_NORMALIZE-011-12Z", &d));
2137 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-13-12Z", &d));
2138 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-12.12Z", &d));
2139
2140 MYASSERT(test_date_parser_tostring("2004-01-01Z", "2004-01-01Z") == 0);
2141
2142 /* day */
2143
2144 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-ffZ", &d));
2145 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-00Z", &d));
2146 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-007Z", &d));
2147 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-32Z", &d));
2148 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01t12:12:12Z", &d));
2149 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01- 1Z", &d));
2150
2151 MYASSERT(PARSE_AND_NORMALIZE_DATE("2005-02-29Z", &d));
2152 MYASSERT(!PARSE_AND_NORMALIZE_DATE("2005-02-28Z", &d));
2153 MYASSERT(!PARSE_AND_NORMALIZE_DATE("2004-02-29Z", &d));
2154 MYASSERT(!PARSE_AND_NORMALIZE_DATE("2000-02-29Z", &d));
2155 MYASSERT(PARSE_AND_NORMALIZE_DATE("1900-02-29Z", &d));
2156
2157 MYASSERT(test_date_parser_tostring("2012-04-12Z", "2012-04-12Z") == 0);
2158
2159 /* timezones + normalization */
2160
2161 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+", &d));
2162 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01-", &d));
2163 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+00.00", &d));
2164 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+aa:bb", &d));
2165 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+15:00", &d));
2166 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+14:01", &d));
2167 MYASSERT(!PARSE_AND_NORMALIZE_DATE("2004-01-01+14:00", &d));
2168 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01-14:01", &d));
2169 MYASSERT(!PARSE_AND_NORMALIZE_DATE("2004-01-01-14:00", &d));
2170 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+10:99", &d));
2171 MYASSERT(!PARSE_AND_NORMALIZE_DATE("2004-01-01+10:59", &d));
2172 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+10:059", &d));
2173 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+010:59", &d));
2174 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+10:59a", &d));
2175 MYASSERT(PARSE_AND_NORMALIZE_DATE("2004-01-01+10:059", &d));
2176
2177 MYASSERT(test_date_parser_tostring("2004-12-31-13:00", "2005-01-01Z") == 0);
2178 MYASSERT(test_date_parser_tostring("2005-01-01+13:00", "2004-12-31Z") == 0);
2179 MYASSERT(test_date_parser_tostring("2004-12-31-11:59", "2004-12-31Z") == 0);
2180 MYASSERT(test_date_parser_tostring("2005-01-01+11:59", "2005-01-01Z") == 0);
2181
2182 /* Date equality */
2183 /* May not be comparible since <14hrs apart */
2184 MYASSERT(test_date_equals(world, "2011-01-02Z", "2011-01-02" , INCOMPARABLE));
2185 MYASSERT(test_date_equals(world, "2011-01-02" , "2011-01-02" , 1));
2186 MYASSERT(test_date_equals(world, "2011-01-02", "2011-01-02Z", INCOMPARABLE));
2187 MYASSERT(test_date_equals(world, "2011-01-02Z", "2011-01-02Z", 1));
2188
2189 /* Are comparible across timelines since >14hrs apart */
2190 MYASSERT(test_date_equals(world, "2011-01-02Z", "2011-01-03" , 0));
2191 MYASSERT(test_date_equals(world, "2011-01-02" , "2011-01-03" , 0));
2192 MYASSERT(test_date_equals(world, "2011-01-02", "2011-01-03Z", 0));
2193 MYASSERT(test_date_equals(world, "2011-01-02Z", "2011-01-03Z", 0));
2194
2195 MYASSERT(test_date_not_equals(world, "2006-08-23", "2006-08-23", 0));
2196 MYASSERT(test_date_not_equals(world, "2006-08-23", "2006-08-23Z", INCOMPARABLE));
2197 MYASSERT(test_date_not_equals(world, "2006-08-23", "2006-08-23+00:00", INCOMPARABLE));
2198 /* More than 14hrs apart so are comparible */
2199 MYASSERT(test_date_not_equals(world, "2006-08-23", "2001-01-01", 1));
2200 MYASSERT(test_date_not_equals(world, "2006-08-23", "2001-01-01Z", 1));
2201
2202 /* Date comparisons */
2203 MYASSERT(test_date_compare(world, "2011-01-02Z", "2011-01-02" , INCOMPARABLE));
2204 MYASSERT(test_date_compare(world, "2011-01-02", "2011-01-02" , 0));
2205 MYASSERT(test_date_compare(world, "2011-01-02", "2011-01-02Z", INCOMPARABLE));
2206 MYASSERT(test_date_compare(world, "2011-01-02Z", "2011-01-02Z", 0));
2207
2208 MYASSERT(test_date_compare(world, "2011-01-02Z", "2011-01-03" , -1));
2209 MYASSERT(test_date_compare(world, "2011-01-02", "2011-01-03" , -1));
2210 MYASSERT(test_date_compare(world, "2011-01-02", "2011-01-03Z", -1));
2211 MYASSERT(test_date_compare(world, "2011-01-02Z", "2011-01-03Z", -1));
2212
2213 /* DateTime equality */
2214 MYASSERT(test_datetime_equals(world, "2011-01-02T00:00:00", "2011-01-02T00:00:00", 1));
2215 MYASSERT(test_datetime_equals(world, "2011-01-02T00:00:00", "2011-01-02T00:00:00Z", INCOMPARABLE));
2216 MYASSERT(test_datetime_equals(world, "2011-01-02T00:00:00Z", "2011-01-02T00:00:00", INCOMPARABLE));
2217 MYASSERT(test_datetime_equals(world, "2011-01-02T00:00:00Z", "2011-01-02T00:00:00Z", 1));
2218
2219 /* DateTime comparisons */
2220 MYASSERT(test_datetime_compare(world, "2011-01-02T00:00:00", "2011-01-02T00:00:00" , 0));
2221 MYASSERT(test_datetime_compare(world, "2011-01-02T00:00:00", "2011-01-02T00:00:00Z", INCOMPARABLE));
2222 MYASSERT(test_datetime_compare(world, "2011-01-02T00:00:00Z", "2011-01-02T00:00:00", INCOMPARABLE));
2223 MYASSERT(test_datetime_compare(world, "2011-01-02T00:00:00Z", "2011-01-02T00:00:00Z", 0));
2224
2225
2226 if(1) {
2227 struct timeval my_tv;
2228 time_t secs;
2229 time_t new_secs;
2230 struct timeval* new_tv;
2231
2232 /* 2010-12-14T06:22:36.868099Z or 2010-12-13T22:22:36.868099+0800
2233 * when I was testing this
2234 */
2235 my_tv.tv_sec = 1292307756;
2236 my_tv.tv_usec = 868099;
2237
2238 secs = my_tv.tv_sec;
2239
2240 MYASSERT(rasqal_xsd_datetime_set_from_timeval(&dt, &my_tv) == 0);
2241
2242 MYASSERT((new_tv = rasqal_xsd_datetime_get_as_timeval(&dt)));
2243 MYASSERT(new_tv->tv_sec == my_tv.tv_sec);
2244 MYASSERT(new_tv->tv_usec == my_tv.tv_usec);
2245
2246 RASQAL_FREE(timeval, new_tv);
2247
2248 MYASSERT(rasqal_xsd_datetime_set_from_unixtime(&dt, secs) == 0);
2249
2250 MYASSERT((new_secs = rasqal_xsd_datetime_get_as_unixtime(&dt)));
2251 MYASSERT(new_secs == secs);
2252 }
2253
2254 rasqal_free_world(world);
2255
2256 return 0;
2257 }
2258
2259 #endif /* STANDALONE */
2260
2261