1 
2 //metadoc Date category Core
3 //metadoc Date copyright Steve Dekorte 2002
4 //metadoc Date license BSD revised
5 /*metadoc Date description A container for a date and time information.*/
6 //metadoc Date credits fromString method by Sean Perry
7 
8 #include "IoDate.h"
9 #include "IoState.h"
10 #include "IoCFunction.h"
11 #include "IoObject.h"
12 #include "IoSeq.h"
13 #include "IoNumber.h"
14 #include "IoDuration.h"
15 #include "PortableStrptime.h"
16 #include <string.h>
17 #include <time.h>
18 
19 static const char *protoId = "Date";
20 
21 #define DATA(self) ((Date *)IoObject_dataPointer(self))
22 
IoDate_newTag(void * state)23 IoTag *IoDate_newTag(void *state)
24 {
25 	IoTag *tag = IoTag_newWithName_(protoId);
26 	IoTag_state_(tag, state);
27 	IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoDate_rawClone);
28 	IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoDate_free);
29 	IoTag_compareFunc_(tag, (IoTagCompareFunc *)IoDate_compare);
30 	return tag;
31 }
32 
IoDate_proto(void * state)33 IoDate *IoDate_proto(void *state)
34 {
35 	IoMethodTable methodTable[] = {
36 	{"asSerialization", IoDate_asSerialization},
37 	{"fromSerialization", IoDate_fromSerialization},
38 	{"now", IoDate_now},
39 	{"clock", IoDate_clock},
40 	{"copy", IoDate_copy},
41 	{"cpuSecondsToRun", IoDate_cpuSecondsToRun},
42 	{"year", IoDate_year},
43 	{"setYear", IoDate_setYear},
44 	{"month", IoDate_month},
45 	{"setMonth", IoDate_setMonth},
46 	{"day", IoDate_day},
47 	{"setDay", IoDate_setDay},
48 	{"hour", IoDate_hour},
49 	{"setHour", IoDate_setHour},
50 	{"minute", IoDate_minute},
51 	{"setMinute", IoDate_setMinute},
52 	{"second", IoDate_second},
53 	{"setSecond", IoDate_setSecond},
54 	{"isDaylightSavingsTime", IoDate_isDaylightSavingsTime},
55 	{"zone", IoDate_zone},
56 	{"isDST", IoDate_isDST},
57 	{"setGmtOffset", IoDate_setGmtOffset},
58 	{"gmtOffset", IoDate_gmtOffset},
59 	{"gmtOffsetSeconds", IoDate_gmtOffsetSeconds},
60 	{"convertToUTC", IoDate_convertToUTC},
61 	{"convertToZone", IoDate_convertToZone},
62 	{"convertToLocal", IoDate_convertToLocal},
63 	{"setToUTC", IoDate_setToUTC},
64 	{"isValidTime", IoDate_isValidTime},
65 	{"secondsSince", IoDate_secondsSince_},
66 	{"secondsSinceNow", IoDate_secondsSinceNow},
67 	{"isPast", IoDate_isPast},
68 	//{"dateAfterSeconds", IoDate_dateAfterSeconds_},
69 	{"asString", IoDate_asString},
70 	{"asNumber", IoDate_asNumber},
71 	{"fromNumber", IoDate_fromNumber},
72 	{"fromString", IoDate_fromString},
73 	{"print", IoDate_printDate},
74 	{"+", IoDate_add},
75 	{"-", IoDate_subtract},
76 	{"+=", IoDate_addInPlace},
77 	{"-=", IoDate_subtractInPlace},
78 	{NULL, NULL},
79 	};
80 
81 	IoObject *self = IoObject_new(state);
82 
83 	IoObject_tag_(self, IoDate_newTag(state));
84 	IoObject_setDataPointer_(self, Date_new());
85 
86 	/*doc Date format
87 	Returns the format string for the receiver. The default is "%Y-%m-%d %H:%M:%S %Z".
88 	*/
89 
90 	IoObject_setSlot_to_(self, IOSYMBOL("format"), IOSYMBOL("%Y-%m-%d %H:%M:%S %Z"));
91 	IoState_registerProtoWithId_((IoState *)state, self, protoId);
92 
93 	IoObject_addMethodTable_(self, methodTable);
94 	return self;
95 }
96 
IoDate_rawClone(IoDate * proto)97 IoDate *IoDate_rawClone(IoDate *proto)
98 {
99 	IoObject *self = IoObject_rawClonePrimitive(proto);
100 	IoObject_setDataPointer_(self, Date_new());
101 	Date_copy_(DATA(self), DATA(proto));
102 	return self;
103 }
104 
IoDate_new(void * state)105 IOVM_API IoDate *IoDate_new(void *state)
106 {
107 	IoDate *proto = IoState_protoWithId_((IoState *)state, protoId);
108 	return IOCLONE(proto);
109 }
110 
IoDate_newWithTime_(void * state,time_t t)111 IOVM_API IoDate *IoDate_newWithTime_(void *state, time_t t)
112 {
113 	IoDate *self = IoDate_new(state);
114 	Date_fromTime_(DATA(self), t);
115 	return self;
116 }
117 
IoDate_newWithTimeval_(void * state,struct timeval tv)118 IOVM_API IoDate *IoDate_newWithTimeval_(void *state, struct timeval tv)
119 {
120 	IoDate *self = IoDate_new(state);
121 	DATA(self)->tv = tv;
122 	return self;
123 }
124 
IoDate_newWithLocalTime_(void * state,struct tm * t)125 IOVM_API IoDate *IoDate_newWithLocalTime_(void *state, struct tm *t)
126 {
127 	IoDate *self = IoDate_new(state);
128 	Date_fromLocalTime_(DATA(self), t);
129 	return self;
130 }
131 
IoDate_free(IoDate * self)132 void IoDate_free(IoDate *self)
133 {
134 	Date_free(DATA(self));
135 }
136 
IoDate_compare(IoDate * self,IoDate * date)137 int IoDate_compare(IoDate *self, IoDate *date)
138 {
139 	if (ISDATE(date)) return Date_compare(DATA(self), DATA(date));
140 	return IoObject_defaultCompare(self, date);
141 }
142 
143 // -----------------------------------------------------------
144 
IoDate_asSerialization(IoDate * self,IoObject * locals,IoMessage * m)145 IoSeq *IoDate_asSerialization(IoDate *self, IoObject *locals, IoMessage *m)
146 {
147 	/*doc Date asSerialization
148 	Returns a serialization (sequence) of the date that allows for perfect reconstruction of the timestamp.
149 	*/
150 
151 	return IoSeq_newWithUArray_copy_(IOSTATE, (UArray *) Date_asSerialization(DATA(self)), 0);
152 }
153 
IoDate_fromSerialization(IoDate * self,IoObject * locals,IoMessage * m)154 IoDate *IoDate_fromSerialization(IoDate *self, IoObject *locals, IoMessage *m)
155 {
156 	/*doc Date fromSerialization
157 	Sets the date based on the serialization sequence.  Return self.
158 	*/
159 
160 	IoSeq *serializationSeq = IoMessage_locals_seqArgAt_(m, locals, 0);
161 	UArray *serialization = UArray_clone(IoSeq_rawUArray(serializationSeq));
162 
163 	UArray_setItemType_(serialization, CTYPE_int32_t);
164 	if(UArray_size(serialization) != 4)
165 	{
166 		IoState_error_(IOSTATE, self, "Expected a serialization sequence comprising 4 int32 items.");
167 	}
168 
169 	Date_fromSerialization(DATA(self), serialization);
170 
171 	UArray_free(serialization);
172 
173 	return self;
174 }
175 
IO_METHOD(IoDate,now)176 IO_METHOD(IoDate, now)
177 {
178 	/*doc Date now
179 	Sets the receiver to the current time. Returns self.
180 	*/
181 
182 	Date_now(DATA(self));
183 	return self;
184 }
185 
IO_METHOD(IoDate,copy)186 IO_METHOD(IoDate, copy)
187 {
188 	/*doc Date copy(aDate)
189 	Sets the receiver to be the same date as aDate. Returns self.
190 	*/
191 
192 	IoDate *date = IoMessage_locals_dateArgAt_(m, locals, 0);
193 
194 	Date_copy_(DATA(self), DATA(date));
195 	return self;
196 }
197 
IO_METHOD(IoDate,clock)198 IO_METHOD(IoDate, clock)
199 {
200 	/*doc Date clock
201 	Returns a number containing the number of seconds
202 	of processor time since the beginning of the program or -1 if unavailable.
203 	*/
204 
205 	return IONUMBER(Date_Clock());
206 }
207 
IO_METHOD(IoDate,cpuSecondsToRun)208 IO_METHOD(IoDate, cpuSecondsToRun)
209 {
210 	/*doc Date cpuSecondsToRun(expression)
211 	Evaluates message and returns a Number whose value
212 	is the cpu seconds taken to do the evaluation.
213 	*/
214 
215 	IoMessage_assertArgCount_receiver_(m, 1, self);
216 
217 	{
218 		double t2, t1 = clock();
219 		IoMessage *doMessage = IoMessage_rawArgAt_(m, 0);
220 		IoMessage_locals_performOn_(doMessage, locals, locals);
221 		t2 = clock();
222 		return IONUMBER((t2 - t1)/((double)CLOCKS_PER_SEC));
223 	}
224 }
225 
IO_METHOD(IoDate,year)226 IO_METHOD(IoDate, year)
227 {
228 	/*doc Date year
229 	Returns a number containing the year of the receiver.
230 	*/
231 
232 	return IONUMBER(Date_year(DATA(self)));
233 }
234 
IO_METHOD(IoDate,setYear)235 IO_METHOD(IoDate, setYear)
236 {
237 	/*doc Date setYear(aNumber)
238 	Sets the year of the receiver.
239 	*/
240 
241 	Date_setYear_(DATA(self), IoMessage_locals_intArgAt_(m, locals, 0));
242 	return self;
243 }
244 
IO_METHOD(IoDate,month)245 IO_METHOD(IoDate, month)
246 {
247 	/*doc Date month
248 	Returns a number containing the month(1-12) of the year of the receiver.
249 	*/
250 
251 	return IONUMBER(Date_month(DATA(self)) + 1);
252 }
253 
IO_METHOD(IoDate,setMonth)254 IO_METHOD(IoDate, setMonth)
255 {
256 	/*doc Date setMonth(aNumber)
257 	Sets the month(1-12) of the receiver. Returns self.
258 	*/
259 
260 	int v = IoMessage_locals_intArgAt_(m, locals, 0);
261 	IOASSERT(v >= 1 && v <= 12, "month must be within range 1-12");
262 	Date_setMonth_(DATA(self), v - 1);
263 	return self;
264 }
265 
IO_METHOD(IoDate,day)266 IO_METHOD(IoDate, day)
267 {
268 	/*doc Date day
269 	Returns a number containing the day of the month of the receiver.
270 	*/
271 
272 	return IONUMBER(Date_day(DATA(self)));
273 }
274 
IO_METHOD(IoDate,setDay)275 IO_METHOD(IoDate, setDay)
276 {
277 	/*doc Date setDay(aNumber)
278 	Sets the day of the receiver. Returns self.
279 	*/
280 
281 	int v = IoMessage_locals_intArgAt_(m, locals, 0);
282 	int month = Date_month(DATA(self)) + 1;
283 
284 	IOASSERT(v >= 1 && v <= 31, "day must be within range 1-31");
285 
286 	if (month == 2)
287 	{
288 		if (Date_isLeapYear(DATA(self)))
289 		{
290 			IOASSERT(v >= 1 && v <= 29, "day must be within range 1-29");
291 		}
292 		else
293 		{
294 			IOASSERT(v >= 1 && v <= 28, "day must be within range 1-28");
295 		}
296 	}
297 	else if (month == 11)
298 	{
299 		IOASSERT(v >= 1 && v <= 30, "day must be within range 1-30");
300 	}
301 	else if (month == 12)
302 	{
303 		IOASSERT(v >= 1 && v <= 31, "day must be within range 1-31");
304 	}
305 
306 	Date_setDay_(DATA(self), v);
307 	IoObject_isDirty_(self, 1);
308 	return self;
309 }
310 
IO_METHOD(IoDate,hour)311 IO_METHOD(IoDate, hour)
312 {
313 	/*doc Date hour
314 	Returns a number containing the hour of the day(0-23) of the receiver.
315 	*/
316 
317 	return IONUMBER(Date_hour(DATA(self)));
318 }
319 
IO_METHOD(IoDate,setHour)320 IO_METHOD(IoDate, setHour)
321 {
322 	/*doc Date setHour(aNumber)
323 	Sets the hour of the receiver. Returns self.
324 	*/
325 
326 	int v = IoMessage_locals_intArgAt_(m, locals, 0);
327 	IOASSERT(v >= 0 && v <= 23, "hour must be within range 0-23");
328 	Date_setHour_(DATA(self), v);
329 	IoObject_isDirty_(self, 1);
330 	return self;
331 }
332 
IO_METHOD(IoDate,minute)333 IO_METHOD(IoDate, minute)
334 {
335 	/*doc Date minute
336 	Returns a number containing the minute of the hour(0-59) of the receiver.
337 	*/
338 
339 	return IONUMBER(Date_minute(DATA(self)));
340 }
341 
342 
IO_METHOD(IoDate,setMinute)343 IO_METHOD(IoDate, setMinute)
344 {
345 	/*doc Date setMinute(aNumber)
346 	Sets the minute of the receiver. Returns self.
347 	*/
348 
349 	int v = IoMessage_locals_intArgAt_(m, locals, 0);
350 	IOASSERT(v >= 0 && v <= 59, "minute must be within range 0-59");
351 	Date_setMinute_(DATA(self), v);
352 	IoObject_isDirty_(self, 1);
353 	return self;
354 }
355 
IO_METHOD(IoDate,second)356 IO_METHOD(IoDate, second)
357 {
358 	/*doc Date second
359 	Returns a number containing the seconds of the minute(0-59) of the receiver. This number may contain fractions of seconds.
360 	*/
361 
362 	return IONUMBER(Date_second(DATA(self)));
363 }
364 
365 
IO_METHOD(IoDate,setSecond)366 IO_METHOD(IoDate, setSecond)
367 {
368 	/*doc Date setSecond(aNumber)
369 	Sets the second of the receiver. Returns self.
370 	*/
371 
372 	int v = IoMessage_locals_intArgAt_(m, locals, 0);
373 	IOASSERT(v >= 0 && v <= 59, "second must be within range 0-59");
374 	Date_setSecond_(DATA(self), v);
375 	IoObject_isDirty_(self, 1);
376 	return self;
377 }
378 
IO_METHOD(IoDate,zone)379 IO_METHOD(IoDate, zone)
380 {
381 	/*doc Date zone
382 	Returns a string containing the system's time zone code.
383 	*/
384 
385 	time_t t = time(NULL);
386 	const struct tm *tp = localtime(&t);
387 	char s[32];
388 	strftime(s, 32,"%Z", tp);
389 	return IOSYMBOL(s);
390 }
391 
IO_METHOD(IoDate,isDST)392 IO_METHOD(IoDate, isDST)
393 {
394 	/*doc Date isDST
395 	Returns true if the Date is set to use DST.  Posix only.
396 	*/
397 
398 	struct timezone tz = Date_timeZone(DATA(self));
399 #if defined(__CYGWIN__) || defined(_WIN32)
400 	IoState_error_(IOSTATE, m, "Not implemented on Windows.");
401 	return IONIL(self);
402 #else
403 	return IOBOOL(self, tz.tz_dsttime);
404 #endif
405 }
406 
IO_METHOD(IoDate,gmtOffsetSeconds)407 IO_METHOD(IoDate, gmtOffsetSeconds)
408 {
409 	/*doc Date gmtOffsetSeconds
410 	Returns the system's seconds east of UTC.
411 	*/
412 
413 	time_t t = time(NULL);
414 	const struct tm *tp = localtime(&t);
415 #if defined(__CYGWIN__) || defined(_WIN32)
416 	return IONUMBER(_timezone);
417 #else
418 	return IONUMBER(tp->tm_gmtoff);
419 #endif
420 }
421 
IO_METHOD(IoDate,setGmtOffset)422 IO_METHOD(IoDate, setGmtOffset)
423 {
424 	/*doc Date setGmtOffset
425 	Set the number of minutes west of GMT for this Date's zone
426 	*/
427 
428 	struct timezone tz = Date_timeZone(DATA(self));
429 	tz.tz_minuteswest = IoMessage_locals_intArgAt_(m, locals, 0);
430 	Date_setTimeZone_(DATA(self), tz);
431 	IoObject_isDirty_(self, 1);
432 	return self;
433 }
434 
IO_METHOD(IoDate,gmtOffset)435 IO_METHOD(IoDate, gmtOffset)
436 {
437 	/*doc Date gmtOffset
438 	Returns the system's timezone string. E.g., +1300 or -0500.
439 	*/
440 
441 	time_t t = time(NULL);
442 	const struct tm *tp = localtime(&t);
443 
444 	char buf[6];
445 #if defined(__CYGWIN__) || defined(_WIN32)
446 	int minutes = _timezone / 60;
447 #else
448 	int minutes = (int)(tp->tm_gmtoff / 60);
449 #endif
450 	snprintf(buf, sizeof(buf), "%+03d%02d", minutes / 60, minutes % 60);
451 
452 	return IOSYMBOL(buf);
453 }
454 
IO_METHOD(IoDate,convertToUTC)455 IO_METHOD(IoDate, convertToUTC)
456 {
457 	/*doc Date convertToUTC
458 	Converts self from a local date to the equivalent UTC date
459 	*/
460 
461 	struct timezone tz;
462 	tz.tz_minuteswest = 0;
463 	tz.tz_dsttime = 0;
464 	Date_convertToTimeZone_(DATA(self), tz);
465 	IoObject_isDirty_(self, 1);
466 	return self;
467 }
468 
IO_METHOD(IoDate,convertToZone)469 IO_METHOD(IoDate, convertToZone)
470 {
471 	/*doc Date convertToZone(offset, isDST)
472 	Converts self to an equivalent data in a zone with offset (minutes west) and DST (true, false).
473 	*/
474 
475 	struct timezone tz;
476 
477 	int mw = IoMessage_locals_intArgAt_(m, locals, 0);
478 	int dst = IoMessage_locals_boolArgAt_(m, locals, 1);
479 
480 	tz.tz_minuteswest = mw;
481 	tz.tz_dsttime = dst;
482 	Date_convertToTimeZone_(DATA(self), tz);
483 	IoObject_isDirty_(self, 1);
484 	return self;
485 }
486 
IO_METHOD(IoDate,convertToLocal)487 IO_METHOD(IoDate, convertToLocal)
488 {
489 	/*doc Date convertToLocal
490 	Converts self date from a UTC date to the equivalent local date
491 	*/
492 
493 	struct timeval tv;
494 	struct timezone tz;
495 
496 	gettimeofday(&tv, &tz);
497 
498 	Date_convertToTimeZone_(DATA(self), tz);
499 	IoObject_isDirty_(self, 1);
500 	return self;
501 }
502 
IO_METHOD(IoDate,setToUTC)503 IO_METHOD(IoDate, setToUTC)
504 {
505 	/*doc Date asUTC
506 	Changes the timezone of this date to utc
507 	*/
508 
509 	struct timezone tz;
510 	tz.tz_minuteswest = 0;
511 	tz.tz_dsttime = 0;
512 
513 	Date_setTimeZone_(DATA(self), tz);
514 	IoObject_isDirty_(self, 1);
515 
516 	return self;
517 }
518 
IO_METHOD(IoDate,isDaylightSavingsTime)519 IO_METHOD(IoDate, isDaylightSavingsTime)
520 {
521 	/*doc Date isDaylightSavingsTime
522 	Returns self if Daylight Saving Time is in effect for the receiver, otherwise returns Nil.
523 	*/
524 
525 	return IOBOOL(self, Date_isDaylightSavingsTime(DATA(self)));
526 }
527 
IO_METHOD(IoDate,isValidTime)528 IO_METHOD(IoDate, isValidTime)
529 {
530 	/*doc Date isValidTime(hour, min, sec)
531 	Returns self if the specified time is valid, otherwise returns Nil.
532 	A negative value will count back; i.e., a value of -5 for the hour,
533 	will count back 5 hours to return a value of 19. No adjustment is
534 	done for values above 24.
535 	*/
536 
537 	int hour = IoMessage_locals_intArgAt_(m, locals, 0);
538 	int min = IoMessage_locals_intArgAt_(m, locals, 1);
539 	int sec = IoMessage_locals_intArgAt_(m, locals, 2);
540 
541 	if (hour < 0) hour += 24;
542 	if (min < 0) min += 60;
543 	if (sec < 0) sec += 60;
544 
545 	return IOBOOL(self, ((hour >= 0) && (hour < 24)) &&
546 		((min >= 0) && (min < 60)) &&
547 		((sec >= 0) && (sec <  60)));
548 }
549 
IO_METHOD(IoDate,secondsSince_)550 IO_METHOD(IoDate, secondsSince_)
551 {
552 	/*doc Date secondsSince(aDate)
553 	Returns a number of seconds of between aDate and the receiver.
554 	*/
555 
556 	IoDate *date = IoMessage_locals_dateArgAt_(m, locals, 0);
557 	return IONUMBER(Date_secondsSince_(DATA(self), DATA(date)));
558 }
559 
IO_METHOD(IoDate,secondsSinceNow)560 IO_METHOD(IoDate, secondsSinceNow)
561 {
562 	/*doc Date secondsSinceNow(aDate)
563 	Returns the number of seconds since aDate.
564 	*/
565 
566 	return IONUMBER(Date_secondsSinceNow(DATA(self)));
567 }
568 
IO_METHOD(IoDate,isPast)569 IO_METHOD(IoDate, isPast)
570 {
571 	/*doc Date isPast
572 	Returns true if the receiver is a date in the past.
573 	*/
574 
575 	return IOBOOL(self, Date_secondsSinceNow(DATA(self)) > 0);
576 }
577 
578 /*
579 IO_METHOD(IoDate, dateAfterSeconds_)
580 {
581 	// doc Date dateAfterSeconds(secondsNumber)
582 	Returns a new date that is secondsNumber seconds after the receiver.
583 
584 
585 	IoDate *newDate = IoDate_new(IOSTATE);
586 	Date_addSeconds_(DATA(newDate), IoMessage_locals_doubleArgAt_(m, locals, 0));
587 	return newDate;
588 }
589 */
590 
IO_METHOD(IoDate,asString)591 IO_METHOD(IoDate, asString)
592 {
593 	/*doc Date asString(optionalFormatString)
594 	Returns a string representation of the receiver using the
595 receivers format. If the optionalFormatString argument is present, the
596 receiver's format is set to it first. Formatting is according to ANSI C
597 date formatting rules.
598 <p>
599 <pre>
600 %a abbreviated weekday name (Sun, Mon, etc.)
601 %A full weekday name (Sunday, Monday, etc.)
602 %b abbreviated month name (Jan, Feb, etc.)
603 %B full month name (January, February, etc.)
604 %c full date and time string
605 %d day of the month as two-digit decimal integer (01-31)
606 %H hour as two-digit 24-hour clock decimal integer (00-23)
607 %I hour as two-digit 12-hour clock decimal integer (01-12)
608 %m month as a two-digit decimal integer (01-12)
609 %M minute as a two-digit decimal integer (00-59)
610 %p either "AM" or "PM"
611 %S second as a two-digit decimal integer (00-59)
612 %U number of week in the year as two-digit decimal integer (00-52)
613 with Sunday considered as first day of the week
614 %w weekday as one-digit decimal integer (0-6) with Sunday as 0
615 %W number of week in the year as two-digit decimal integer (00-52)
616 with Monday considered as first day of the week
617 %x full date string (no time); in the C locale, this is equivalent
618 to "%m/%d/%y".
619 %y year without century as two-digit decimal number (00-99)
620 %Y year with century as four-digit decimal number
621 %Z time zone name (e.g. EST);
622 null string if no time zone can be obtained
623 %% stands for '%' character in output string.
624 </pre>
625 */
626 
627 	char *format = "%Y-%m-%d %H:%M:%S %Z";
628 
629 	if (IoMessage_argCount(m) == 1)
630 	{
631 		format = CSTRING(IoMessage_locals_symbolArgAt_(m, locals, 0));
632 	}
633 	else
634 	{
635 		IoObject *f = IoObject_getSlot_(self, IOSYMBOL("format"));
636 		if (ISSEQ(f)) { format = CSTRING(f); }
637 	}
638 
639 	{
640 		UArray *ba = Date_asString(DATA(self), format);
641 		return IoState_symbolWithUArray_copy_convertToFixedWidth(IOSTATE, ba, 0);
642 	}
643 }
644 
IO_METHOD(IoDate,printDate)645 IO_METHOD(IoDate, printDate)
646 {
647 	/*doc Date print
648 	Prints the receiver. Returns self.
649 	*/
650 
651 	IoSymbol *s = (IoSymbol *)IoDate_asString(self, locals, m);
652 	IoSeq_print(s, locals, m);
653 	return self;
654 }
655 
IO_METHOD(IoDate,asNumber)656 IO_METHOD(IoDate, asNumber)
657 {
658 	/*doc Date asNumber
659 	Returns the date as seconds since 1970 UTC.
660 	*/
661 
662 	return IONUMBER(Date_asSeconds(DATA(self)));
663 }
664 
IO_METHOD(IoDate,fromNumber)665 IO_METHOD(IoDate, fromNumber)
666 {
667 	/*doc Date fromNumber(aNumber)
668 	Sets the receiver to be aNumber seconds since 1970.
669 	*/
670 
671 	Date_fromSeconds_(DATA(self), IoMessage_locals_doubleArgAt_(m, locals, 0));
672 	IoObject_isDirty_(self, 1);
673 	return self;
674 }
675 
IO_METHOD(IoDate,fromString)676 IO_METHOD(IoDate, fromString)
677 {
678 	/*doc Date fromString(aString, formatString)
679 	Sets the receiver to the date specified by aString as parsed according to the given formatString. See the Date asString method for formatting rules. Returns self.
680 	*/
681 
682 	IoMessage_assertArgCount_receiver_(m, 2, self);
683 	{
684 		IoSymbol *date_input = IoMessage_locals_seqArgAt_(m, locals, 0);
685 		IoSymbol *format = IoMessage_locals_seqArgAt_(m, locals, 1);
686 		Date_fromString_format_(DATA(self), CSTRING(date_input), CSTRING(format));
687 	}
688 	IoObject_isDirty_(self, 1);
689 	return self;
690 }
691 
692 /* --- Durations -------------------------------------------------------- */
693 
IO_METHOD(IoDate,subtract)694 IO_METHOD(IoDate, subtract)
695 {
696 	/*doc Date -(aDurationOrDate)
697 	Return a new Date with the receiver's value minus an amount of time specified by aDuration to the receiver. Returns self.
698 	*/
699 
700 	IoObject *v = IoMessage_locals_valueArgAt_(m, locals, 0);
701 
702 	if (ISDATE(v))
703 	{
704 		double d = Date_secondsSince_(DATA(self), DATA(v));
705 		return IoDuration_newWithSeconds_(IOSTATE, d);
706 	}
707 	else if (ISDURATION(v))
708 	{
709 		IoDate *newDate = IOCLONE(self);
710 		Date_subtractDuration_(DATA(newDate), IoDuration_duration(v));
711 		return newDate;
712 	}
713 
714 	IOASSERT(1, "Date or Duration argument required");
715 
716 	return IONIL(self);
717 }
718 
IO_METHOD(IoDate,subtractInPlace)719 IO_METHOD(IoDate, subtractInPlace)
720 {
721 	/*doc Date -=(aDuration)
722 	Subtract aDuration from the receiver. Returns self.
723 	*/
724 
725 	IoDuration *d = IoMessage_locals_durationArgAt_(m, locals, 0);
726 	Date_subtractDuration_(DATA(self), IoDuration_duration(d));
727 	IoObject_isDirty_(self, 1);
728 	return self;
729 }
730 
IO_METHOD(IoDate,addInPlace)731 IO_METHOD(IoDate, addInPlace)
732 {
733 	/*doc Date +=(aDuration)
734 	Add aDuration to the receiver. Returns self.
735 	*/
736 
737 	IoDuration *d = IoMessage_locals_durationArgAt_(m, locals, 0);
738 	Date_addDuration_(DATA(self), IoDuration_duration(d));
739 	IoObject_isDirty_(self, 1);
740 	return self;
741 }
742 
IO_METHOD(IoDate,add)743 IO_METHOD(IoDate, add)
744 {
745 	/*doc Date +(aDuration)
746 	Return a new Date with the receiver's value plus an amount
747 	of time specified by aDuration object to the receiver.
748 	*/
749 
750 	IoDate *newDate = IOCLONE(self);
751 	return IoDate_addInPlace(newDate, locals, m);
752 }
753