Updating built in Io code to use += instead of x = x + y
[io/quag.git] / libs / iovm / source / IoDate.c
blob1e1eb3c1f811486f725977e770d7a372af6f1ded
1 /*#io
2 Date ioDoc(
3 docCopyright("Steve Dekorte", 2002)
4 docLicense("BSD revised")
5 docDescription("A container for a date and time information.
6 credits: fromString method by Sean Perry")
7 docCategory("Time")
8 */
10 #include "IoDate.h"
11 #include "IoState.h"
12 #include "IoCFunction.h"
13 #include "IoObject.h"
14 #include "IoSeq.h"
15 #include "IoNumber.h"
16 #include "IoDuration.h"
17 #include "PortableStrptime.h"
18 #include <string.h>
20 #define DATA(self) ((Date *)IoObject_dataPointer(self))
22 IoTag *IoDate_newTag(void *state)
24 IoTag *tag = IoTag_newWithName_("Date");
25 IoTag_state_(tag, state);
26 IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoDate_rawClone);
27 IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoDate_free);
28 IoTag_compareFunc_(tag, (IoTagCompareFunc *)IoDate_compare);
29 IoTag_writeToStreamFunc_(tag, (IoTagWriteToStreamFunc *)IoDate_writeToStream_);
30 IoTag_readFromStreamFunc_(tag, (IoTagReadFromStreamFunc *)IoDate_readFromStream_);
31 return tag;
34 void IoDate_writeToStream_(IoDate *self, BStream *stream)
36 BStream_writeTaggedDouble_(stream, Date_asSeconds(DATA(self)));
39 void IoDate_readFromStream_(IoDate *self, BStream *stream)
41 Date_fromSeconds_(DATA(self), BStream_readTaggedDouble(stream));
44 IoDate *IoDate_proto(void *state)
46 IoMethodTable methodTable[] = {
47 {"now", IoDate_now},
48 {"clock", IoDate_clock},
49 {"copy", IoDate_copy},
50 {"cpuSecondsToRun", IoDate_cpuSecondsToRun},
51 {"year", IoDate_year},
52 {"setYear", IoDate_setYear},
53 {"month", IoDate_month},
54 {"setMonth", IoDate_setMonth},
55 {"day", IoDate_day},
56 {"setDay", IoDate_setDay},
57 {"hour", IoDate_hour},
58 {"setHour", IoDate_setHour},
59 {"minute", IoDate_minute},
60 {"setMinute", IoDate_setMinute},
61 {"second", IoDate_second},
62 {"setSecond", IoDate_setSecond},
63 {"isDaylightSavingsTime", IoDate_isDaylightSavingsTime},
64 {"zone", IoDate_zone},
65 {"gmtOffset", IoDate_gmtOffset},
66 {"gmtOffsetSeconds", IoDate_gmtOffsetSeconds},
67 {"isValidTime", IoDate_isValidTime},
68 {"secondsSince", IoDate_secondsSince_},
69 {"secondsSinceNow", IoDate_secondsSinceNow},
70 {"isPast", IoDate_isPast},
71 //{"dateAfterSeconds", IoDate_dateAfterSeconds_},
72 {"asString", IoDate_asString},
73 {"asNumber", IoDate_asNumber},
74 {"fromNumber", IoDate_fromNumber},
75 {"fromString", IoDate_fromString},
76 {"print", IoDate_printDate},
77 {"+", IoDate_add},
78 {"-", IoDate_subtract},
79 {"+=", IoDate_addInPlace},
80 {"-=", IoDate_subtractInPlace},
81 {NULL, NULL},
84 IoObject *self = IoObject_new(state);
86 IoObject_tag_(self, IoDate_newTag(state));
87 IoObject_setDataPointer_(self, Date_new());
89 /*#io
90 docSlot("format",
91 "Returns the format string for the receiver. Tthe default is \"%Y-%m-%d %H:%M:%S %Z\".")
93 IoObject_setSlot_to_(self, IOSYMBOL("format"), IOSYMBOL("%Y-%m-%d %H:%M:%S %Z"));
94 IoState_registerProtoWithFunc_((IoState *)state, self, IoDate_proto);
96 IoObject_addMethodTable_(self, methodTable);
97 return self;
100 IoDate *IoDate_rawClone(IoDate *proto)
102 IoObject *self = IoObject_rawClonePrimitive(proto);
103 IoObject_setDataPointer_(self, Date_new());
104 Date_copy_(DATA(self), DATA(proto));
105 return self;
108 IoDate *IoDate_new(void *state)
110 IoDate *proto = IoState_protoWithInitFunction_((IoState *)state, IoDate_proto);
111 return IOCLONE(proto);
114 IoDate *IoDate_newWithTime_(void *state, time_t t)
116 IoDate *self = IoDate_new(state);
117 Date_fromTime_(DATA(self), t);
118 return self;
121 IoDate *IoDate_newWithTimeval_(void *state, struct timeval tv)
123 IoDate *self = IoDate_new(state);
124 DATA(self)->tv = tv;
125 return self;
128 IoDate *IoDate_newWithLocalTime_(void *state, struct tm *t)
130 IoDate *self = IoDate_new(state);
131 Date_fromLocalTime_(DATA(self), t);
132 return self;
135 void IoDate_free(IoDate *self)
137 Date_free(DATA(self));
140 int IoDate_compare(IoDate *self, IoDate *date)
142 if (ISDATE(date)) return Date_compare(DATA(self), DATA(date));
143 return IoObject_defaultCompare(self, date);
146 // -----------------------------------------------------------
148 IoObject *IoDate_now(IoDate *self, IoObject *locals, IoMessage *m)
150 /*#io
151 docSlot("now", "Sets the receiver to the current time. Returns self.")
154 Date_now(DATA(self));
155 return self;
158 IoObject *IoDate_copy(IoDate *self, IoObject *locals, IoMessage *m)
160 /*#io
161 docSlot("copy(aDate)", "Sets the receiver to be the same date as aDate. Returns self.")
164 IoDate *date = IoMessage_locals_dateArgAt_(m, locals, 0);
166 Date_copy_(DATA(self), DATA(date));
167 return self;
170 IoObject *IoDate_clock(IoDate *self, IoObject *locals, IoMessage *m)
172 /*#io
173 docSlot("clock", "Returns a number containing the number of seconds
174 of processor time since the beginning of the program or -1 if unavailable. ")
177 return IONUMBER(Date_Clock());
180 IoObject *IoDate_cpuSecondsToRun(IoDate *self, IoObject *locals, IoMessage *m)
182 /*#io
183 docSlot("cpuSecondsToRun(expression)",
184 "Evaluates message and returns a Number whose value is the cpu seconds taken to do the evaluation.")
187 IoMessage_assertArgCount_receiver_(m, 1, self);
190 double t2, t1 = clock();
191 IoMessage *doMessage = IoMessage_rawArgAt_(m, 0);
192 IoMessage_locals_performOn_(doMessage, locals, locals);
193 t2 = clock();
194 //printf(" dt = %f / %i\n", (float)(t2 - t1), CLOCKS_PER_SEC);
195 return IONUMBER((t2 - t1)/((double)CLOCKS_PER_SEC));
199 IoObject *IoDate_year(IoDate *self, IoObject *locals, IoMessage *m)
201 /*#io
202 docSlot("year",
203 "Returns a number containing the year of the receiver. ")
206 return IONUMBER(Date_year(DATA(self)));
209 IoObject *IoDate_setYear(IoDate *self, IoObject *locals, IoMessage *m)
211 /*#io
212 docSlot("setYear(aNumber)",
213 "Sets the year of the receiver. ")
216 Date_setYear_(DATA(self), IoMessage_locals_intArgAt_(m, locals, 0));
217 return self;
220 IoObject *IoDate_month(IoDate *self, IoObject *locals, IoMessage *m)
222 /*#io
223 docSlot("month",
224 "Returns a number containing the month(1-12) of the year of the receiver. ")
227 return IONUMBER(Date_month(DATA(self)) + 1);
230 IoObject *IoDate_setMonth(IoDate *self, IoObject *locals, IoMessage *m)
232 /*#io
233 docSlot("setMonth(aNumber)",
234 "Sets the month(1-12) of the receiver. Returns self. ")
237 int v = IoMessage_locals_intArgAt_(m, locals, 0);
238 IOASSERT(v >= 1 && v <= 12, "month must be within range 1-12");
239 Date_setMonth_(DATA(self), v - 1);
240 return self;
243 IoObject *IoDate_day(IoDate *self, IoObject *locals, IoMessage *m)
245 /*#io
246 docSlot("day",
247 "Returns a number containing the day of the month of the receiver. ")
250 return IONUMBER(Date_day(DATA(self)));
253 IoObject *IoDate_setDay(IoDate *self, IoObject *locals, IoMessage *m)
255 /*#io
256 docSlot("setDay(aNumber)",
257 "Sets the day of the receiver. Returns self.")
260 int v = IoMessage_locals_intArgAt_(m, locals, 0);
261 int month = Date_month(DATA(self)) + 1;
263 IOASSERT(v >= 1 && v <= 31, "day must be within range 1-31");
265 if (month == 2)
267 if (Date_isLeapYear(DATA(self)))
269 IOASSERT(v >= 1 && v <= 29, "day must be within range 1-29");
271 else
273 IOASSERT(v >= 1 && v <= 28, "day must be within range 1-28");
276 else if (month == 11)
278 IOASSERT(v >= 1 && v <= 30, "day must be within range 1-30");
280 else if (month == 12)
282 IOASSERT(v >= 1 && v <= 31, "day must be within range 1-31");
285 Date_setDay_(DATA(self), v);
286 return self;
289 IoObject *IoDate_hour(IoDate *self, IoObject *locals, IoMessage *m)
291 /*#io
292 docSlot("hour",
293 "Returns a number containing the hour of the day(0-23) of the receiver. ")
296 return IONUMBER(Date_hour(DATA(self)));
299 IoObject *IoDate_setHour(IoDate *self, IoObject *locals, IoMessage *m)
301 /*#io
302 docSlot("setHour(aNumber)",
303 "Sets the hour of the receiver. Returns self.")
306 int v = IoMessage_locals_intArgAt_(m, locals, 0);
307 IOASSERT(v >= 0 && v <= 23, "hour must be within range 0-23");
308 Date_setHour_(DATA(self), v);
309 return self;
312 IoObject *IoDate_minute(IoDate *self, IoObject *locals, IoMessage *m)
314 /*#io
315 docSlot("minute",
316 "Returns a number containing the minute of the hour(0-59) of the receiver. ")
319 return IONUMBER(Date_minute(DATA(self)));
323 IoObject *IoDate_setMinute(IoDate *self, IoObject *locals, IoMessage *m)
325 /*#io
326 docSlot("setMinute(aNumber)",
327 "Sets the minute of the receiver. Returns self.")
330 int v = IoMessage_locals_intArgAt_(m, locals, 0);
331 IOASSERT(v >= 0 && v <= 59, "minute must be within range 0-59");
332 Date_setMinute_(DATA(self), v);
333 return self;
336 IoObject *IoDate_second(IoDate *self, IoObject *locals, IoMessage *m)
338 /*#io
339 docSlot("second",
340 "Returns a number containing the seconds of the minute(0-59) of the receiver. This number may contain fractions of seconds. ")
343 return IONUMBER(Date_second(DATA(self)));
347 IoObject *IoDate_setSecond(IoDate *self, IoObject *locals, IoMessage *m)
349 /*#io
350 docSlot("setSecond(aNumber)",
351 "Sets the second of the receiver. Returns self.")
354 int v = IoMessage_locals_intArgAt_(m, locals, 0);
355 IOASSERT(v >= 0 && v <= 59, "second must be within range 0-59");
356 Date_setSecond_(DATA(self), v);
357 return self;
360 IoObject *IoDate_zone(IoDate *self, IoObject *locals, IoMessage *m)
362 /*#io
363 docSlot("zone", "Returns a string containing the system's time zone code. ")
366 time_t t = time(NULL);
367 const struct tm *tp = localtime(&t);
368 char s[32];
369 strftime(s, 32,"%Z", tp);
370 return IOSYMBOL(s);
373 IoObject *IoDate_gmtOffsetSeconds(IoDate *self, IoObject *locals, IoMessage *m)
375 /*#io
376 docSlot("gmtOffsetSeconds", "Returns the system's seconds east of UTC.")
379 time_t t = time(NULL);
380 const struct tm *tp = localtime(&t);
381 #if defined(__CYGWIN__) || defined(_WIN32)
382 return IONUMBER(_timezone);
383 #else
384 return IONUMBER(tp->tm_gmtoff);
385 #endif
388 IoObject *IoDate_gmtOffset(IoDate *self, IoObject *locals, IoMessage *m)
390 /*#io
391 docSlot("gmtOffset", "Returns the system's timezone string. E.g., +1300 or -0500.")
394 time_t t = time(NULL);
395 const struct tm *tp = localtime(&t);
397 char buf[6];
398 #if defined(__CYGWIN__) || defined(_WIN32)
399 int minutes = _timezone / 60;
400 #else
401 int minutes = tp->tm_gmtoff / 60;
402 #endif
403 snprintf(buf, sizeof(buf), "%+03d%02d", minutes / 60, minutes % 60);
405 return IOSYMBOL(buf);
408 IoObject *IoDate_isDaylightSavingsTime(IoDate *self, IoObject *locals, IoMessage *m)
410 /*#io
411 docSlot("isDaylightSavingsTime",
412 "Returns self if Daylight Saving Time is in effect for the receiver, otherwise returns Nil. ")
415 return IOBOOL(self, Date_isDaylightSavingsTime(DATA(self)));
418 IoObject *IoDate_isValidTime(IoDate *self, IoObject *locals, IoMessage *m)
420 /*#io
421 docSlot("isValidTime(hour, min, sec)",
422 "Returns self if the specified time is valid, otherwise returns Nil. A negative value will count back; i.e., a value of -5 for the hour, will count back 5 hours to return a value of 19. No adjustment is done for values above 24.")
425 int hour = IoMessage_locals_intArgAt_(m, locals, 0);
426 int min = IoMessage_locals_intArgAt_(m, locals, 1);
427 int sec = IoMessage_locals_intArgAt_(m, locals, 2);
429 if (hour < 0) hour += 24;
430 if (min < 0) min += 60;
431 if (sec < 0) sec += 60;
433 return IOBOOL(self, ((hour >= 0) && (hour < 24)) &&
434 ((min >= 0) && (min < 60)) &&
435 ((sec >= 0) && (sec < 60)));
438 IoObject *IoDate_secondsSince_(IoDate *self, IoObject *locals, IoMessage *m)
440 /*#io
441 docSlot("secondsSince(aDate)",
442 "Returns a number of seconds of between aDate and the receiver. ")
445 IoDate *date = IoMessage_locals_dateArgAt_(m, locals, 0);
446 return IONUMBER(Date_secondsSince_(DATA(self), DATA(date)));
449 IoObject *IoDate_secondsSinceNow(IoDate *self, IoObject *locals, IoMessage *m)
451 /*#io
452 docSlot("secondsSinceNow(aDate)", "Returns the number of seconds since aDate. ")
455 return IONUMBER(Date_secondsSinceNow(DATA(self)));
458 IoObject *IoDate_isPast(IoDate *self, IoObject *locals, IoMessage *m)
460 /*#io
461 docSlot("isPast", "Returns true if the receiver is a date in the past. ")
464 return IOBOOL(self, Date_secondsSinceNow(DATA(self)) > 0);
468 IoObject *IoDate_dateAfterSeconds_(IoDate *self, IoObject *locals, IoMessage *m)
470 docSlot("dateAfterSeconds(secondsNumber)",
471 "Returns a new date that is secondsNumber seconds after the receiver. ")
473 IoDate *newDate = IoDate_new(IOSTATE);
474 Date_addSeconds_(DATA(newDate), IoMessage_locals_doubleArgAt_(m, locals, 0));
475 return newDate;
479 IoObject *IoDate_asString(IoDate *self, IoObject *locals, IoMessage *m)
481 /*#io
482 docSlot("asString(optionalFormatString)",
483 """Returns a string representation of the receiver using the
484 receivers format. If the optionalFormatString argument is present, the
485 receiver's format is set to it first. Formatting is according to ANSI C
486 date formating rules.
488 <pre>
489 %a abbreviated weekday name (Sun, Mon, etc.)
490 %A full weekday name (Sunday, Monday, etc.)
491 %b abbreviated month name (Jan, Feb, etc.)
492 %B full month name (January, February, etc.)
493 %c full date and time string
494 %d day of the month as two-digit decimal integer (01-31)
495 %H hour as two-digit 24-hour clock decimal integer (00-23)
496 %I hour as two-digit 12-hour clock decimal integer (01-12)
497 %m month as a two-digit decimal integer (01-12)
498 %M minute as a two-digit decimal integer (00-59)
499 %p either "AM" or "PM"
500 %S second as a two-digit decimal integer (00-59)
501 %U number of week in the year as two-digit decimal integer (00-52)
502 with Sunday considered as first day of the week
503 %w weekday as one-digit decimal integer (0-6) with Sunday as 0
504 %W number of week in the year as two-digit decimal integer (00-52)
505 with Monday considered as first day of the week
506 %x full date string (no time); in the C locale, this is equivalent
507 to "%m/%d/%y".
508 %y year without century as two-digit decimal number (00-99)
509 %Y year with century as four-digit decimal number
510 %Z time zone name (e.g. EST);
511 null string if no time zone can be obtained
512 %% stands for '%' character in output string.
513 </pre>
514 """)
517 char *format = "%Y-%m-%d %H:%M:%S %Z";
519 if (IoMessage_argCount(m) == 1)
521 format = CSTRING(IoMessage_locals_symbolArgAt_(m, locals, 0));
523 else
525 IoObject *f = IoObject_getSlot_(self, IOSYMBOL("format"));
526 if (ISSEQ(f)) { format = CSTRING(f); }
530 UArray *ba = Date_asString(DATA(self), format);
531 return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);
535 IoObject *IoDate_printDate(IoDate *self, IoObject *locals, IoMessage *m)
537 /*#io
538 docSlot("print", "Prints the receiver. Returns self.")
541 IoSymbol *s = (IoSymbol *)IoDate_asString(self, locals, m);
542 IoSeq_print(s, locals, m);
543 return self;
546 IoObject *IoDate_asNumber(IoDate *self, IoObject *locals, IoMessage *m)
548 /*#io
549 docSlot("asNumber", "Returns the date as seconds since 1970.")
552 return IONUMBER(Date_asSeconds(DATA(self)));
555 IoObject *IoDate_fromNumber(IoDate *self, IoObject *locals, IoMessage *m)
557 /*#io
558 docSlot("fromNumber(aNumber)", "Sets the receiver to be aNumber seconds since 1970.")
561 Date_fromSeconds_(DATA(self), IoMessage_locals_doubleArgAt_(m, locals, 0));
562 return self;
565 IoObject *IoDate_fromString(IoDate *self, IoObject *locals, IoMessage *m)
567 /*#io
568 docSlot("fromString(aString, formatString)", "Sets the receiver to the date specified by aString as parsed according to the given formatString. See the Date asString method for formating rules. Returns self. ")
571 IoMessage_assertArgCount_receiver_(m, 2, self);
573 IoSymbol *date_input = IoMessage_locals_seqArgAt_(m, locals, 0);
574 IoSymbol *format = IoMessage_locals_seqArgAt_(m, locals, 1);
575 Date_fromString_format_(DATA(self), CSTRING(date_input), CSTRING(format));
577 return self;
580 /* --- Durations -------------------------------------------------------- */
582 IoObject *IoDate_subtract(IoDate *self, IoObject *locals, IoMessage *m)
584 /*#io
585 docSlot("-(aDurationOrDate)", "Return a new Date with the receiver's value minus an amount of time specified by aDuration to the receiver. Returns self. ")
588 IoObject *v = IoMessage_locals_valueArgAt_(m, locals, 0);
590 if (ISDATE(v))
592 double d = Date_secondsSince_(DATA(self), DATA(v));
593 return IoDuration_newWithSeconds_(IOSTATE, d);
595 else if (ISDURATION(v))
597 IoDate *newDate = IOCLONE(self);
598 Date_subtractDuration_(DATA(newDate), IoDuration_duration(v));
599 return newDate;
602 IOASSERT(1, "Date or Duration argument required");
604 return IONIL(self);
607 IoObject *IoDate_subtractInPlace(IoDate *self, IoObject *locals, IoMessage *m)
609 /*#io
610 docSlot("-=(aDuration)", "Subtract aDuration from the receiver. Returns self.")
613 IoDuration *d = IoMessage_locals_durationArgAt_(m, locals, 0);
614 Date_subtractDuration_(DATA(self), IoDuration_duration(d));
615 return self;
618 IoObject *IoDate_addInPlace(IoDate *self, IoObject *locals, IoMessage *m)
620 /*#io
621 docSlot("+=(aDuration)", "Add aDuration to the receiver. Returns self. ")
624 IoDuration *d = IoMessage_locals_durationArgAt_(m, locals, 0);
625 Date_addDuration_(DATA(self), IoDuration_duration(d));
626 return self;
629 IoObject *IoDate_add(IoDate *self, IoObject *locals, IoMessage *m)
631 /*#io
632 docSlot("+(aDuration)", "Return a new Date with the receiver's value plus an amount of time specified by aDuration object to the receiver. ")
635 IoDate *newDate = IOCLONE(self);
636 return IoDate_addInPlace(newDate, locals, m);