1#include "pyobjc.h"
2#import "OC_PythonDate.h"
3
4static PyObject* datetime_types = NULL;
5
6@implementation OC_PythonDate 
7
8+ (instancetype)depythonifyObject:(PyObject*)object
9{
10	if (datetime_types == NULL) {
11		/* Initialize the mapping table, don't worry about
12		 * import errors: if we're running in an Py2app application
13		 * that doesn't use datetime we won't be able to import the
14		 * module
15		 */
16		datetime_types = PyList_New(0);
17		if (datetime_types == NULL) {
18			return nil;
19		}
20		PyObject* name = PyText_FromString("datetime");
21		if (name == NULL) {
22			return nil;
23		}
24		PyObject* datetime = PyImport_Import(name);
25		Py_DECREF(name); name = NULL;
26
27		if (datetime == NULL) {
28			PyErr_Clear();
29			return nil;
30		}
31
32		PyList_Append(datetime_types,
33			PyObject_GetAttrString(datetime, "date"));
34		PyList_Append(datetime_types,
35			PyObject_GetAttrString(datetime, "datetime"));
36		if (PyErr_Occurred()) {
37			Py_DECREF(datetime);
38			return nil;
39		}
40		Py_DECREF(datetime);
41	}
42
43
44	if (PySequence_Contains(datetime_types, (PyObject*)(Py_TYPE(object)))) {
45		return [[[OC_PythonDate alloc] initWithPythonObject:object] autorelease];
46	}
47	return nil;
48}
49
50+ (instancetype)dateWithPythonObject:(PyObject*)v
51{
52	OC_PythonDate* res;
53
54	res = [[OC_PythonDate alloc] initWithPythonObject:v];
55	[res autorelease];
56	return res;
57}
58
59- (id)initWithPythonObject:(PyObject*)v
60{
61	self = [super init];
62	if (unlikely(self == nil)) return nil;
63
64	oc_value = nil;
65
66	Py_INCREF(v);
67	Py_XDECREF(value);
68	value = v;
69	return self;
70}
71
72-(PyObject*)__pyobjc_PythonObject__
73{
74	Py_INCREF(value);
75	return value;
76}
77-(PyObject*)__pyobjc_PythonTransient__:(int*)cookie
78{
79	*cookie = 0;
80	Py_INCREF(value);
81	return value;
82}
83
84-(BOOL)supportsWeakPointers { return YES; }
85
86-(oneway void)release
87{
88	/* See comment in OC_PythonUnicode */
89	PyObjC_BEGIN_WITH_GIL
90		[super release];
91	PyObjC_END_WITH_GIL
92}
93
94-(void)dealloc
95{
96	[oc_value  release];
97	oc_value = nil;
98
99	PyObjC_BEGIN_WITH_GIL
100		PyObjC_UnregisterObjCProxy(value, self);
101		Py_XDECREF(value);
102
103	PyObjC_END_WITH_GIL
104
105	[super dealloc];
106}
107
108
109- (void)encodeWithCoder:(NSCoder*)coder
110{
111	PyObjC_encodeWithCoder(value, coder);
112}
113
114
115/* 
116 * Helper method for initWithCoder, needed to deal with
117 * recursive objects (e.g. o.value = o)
118 */
119-(void)pyobjcSetValue:(NSObject*)other
120{
121	PyObjC_BEGIN_WITH_GIL
122		PyObject* v = PyObjC_IdToPython(other);
123		Py_XDECREF(value);
124		value = v;
125	PyObjC_END_WITH_GIL
126}
127
128-(id)initWithCoder:(NSCoder*)coder
129{
130	value = NULL;
131
132	if (PyObjC_Decoder != NULL) {
133		PyObjC_BEGIN_WITH_GIL
134			PyObject* cdr = PyObjC_IdToPython(coder);
135			if (cdr == NULL) {
136				PyObjC_GIL_FORWARD_EXC();
137			}
138
139			PyObject* setValue;
140			PyObject* selfAsPython = PyObjCObject_New(self, 0, YES);
141			setValue = PyObject_GetAttrString(selfAsPython, "pyobjcSetValue_");
142
143			PyObject* v = PyObject_CallFunction(PyObjC_Decoder, "OO", cdr, setValue);
144			Py_DECREF(cdr);
145			Py_DECREF(setValue);
146			Py_DECREF(selfAsPython);
147
148			if (v == NULL) {
149				PyObjC_GIL_FORWARD_EXC();
150			}
151
152			Py_XDECREF(value);
153			value = v;
154
155			NSObject* proxy = PyObjC_FindObjCProxy(value);
156			if (proxy == NULL) {
157				PyObjC_RegisterObjCProxy(value, self);
158			} else {
159				[self release];
160				[proxy retain];
161				self = (OC_PythonDate*)proxy;
162			}
163
164
165		PyObjC_END_WITH_GIL
166
167		return self;
168
169	} else {
170		[NSException raise:NSInvalidArgumentException
171				format:@"decoding Python objects is not supported"];
172		return nil;
173
174	}
175}
176
177-(NSDate*)_make_oc_value
178{
179	if (oc_value == nil) {
180		PyObjC_BEGIN_WITH_GIL
181			PyObject* v;
182
183			v = PyObject_CallMethod(value, "strftime", "s",
184				"%Y-%m-%d %H:%M:%S %z");
185			if (v == NULL) {
186				/* Raise ObjC exception */
187				PyObjC_GIL_FORWARD_EXC();
188			}
189
190
191			oc_value = [NSDate dateWithString: PyObjC_PythonToId(v)];
192			[oc_value retain];
193			Py_DECREF(v);
194
195			if (oc_value == nil) {
196				/* The first try will fail when the date/datetime object
197				 * isn't timezone aware, try again with a default timezone
198				 */
199				char buf[128];
200
201				NSTimeZone* zone = [NSTimeZone defaultTimeZone];
202				NSInteger offset = [zone secondsFromGMT];
203				char posneg;
204				if (offset < 0) {
205					posneg = '-';
206					offset = -offset;
207				} else {
208					posneg = '+';
209				}
210				offset = offset / 60; /* Seconds to minutes */
211
212				int minutes = offset % 60;
213				int hours = offset / 60;
214
215
216
217				snprintf(buf, sizeof(buf), "%%Y-%%m-%%d %%H:%%M:%%S %c%02d%02d",
218					posneg, hours, minutes);
219				v = PyObject_CallMethod(value, "strftime", "s", buf);
220				if (v == NULL) {
221					/* Raise ObjC exception */
222				}
223
224				oc_value = [NSDate dateWithString: PyObjC_PythonToId(v)];
225				[oc_value retain];
226				Py_DECREF(v);
227			}
228
229
230
231		PyObjC_END_WITH_GIL
232	} 
233	return oc_value;
234}
235
236-(NSTimeInterval)timeIntervalSinceReferenceDate
237{
238	return [[self _make_oc_value] timeIntervalSinceReferenceDate];
239}
240
241
242#pragma clang diagnostic push
243#pragma clang diagnostic ignored "-Wdeprecated-declarations"
244
245- (id)addTimeInterval:(NSTimeInterval)seconds
246{
247	return [[self _make_oc_value] addTimeInterval:seconds];
248}
249
250#pragma clang diagnostic pop
251
252- (NSComparisonResult)compare:(NSDate *)anotherDate
253{
254	return [[self _make_oc_value] compare:anotherDate];
255}
256
257- (NSCalendarDate *)dateWithCalendarFormat:(NSString *)formatString timeZone:(NSTimeZone *)timeZone
258{
259	return [[self _make_oc_value] dateWithCalendarFormat:formatString timeZone:timeZone];
260}
261
262- (NSString*)description
263{
264	return [[self _make_oc_value] description];
265}
266
267- (NSString *)descriptionWithCalendarFormat:(NSString *)formatString timeZone:(NSTimeZone *)aTimeZone locale:(id)localeDictionary
268{
269	return [[self _make_oc_value] descriptionWithCalendarFormat:formatString timeZone:aTimeZone locale:localeDictionary];
270}
271
272- (NSString *)descriptionWithLocale:(id)localeDictionary
273{
274	return [[self _make_oc_value] descriptionWithLocale:localeDictionary];
275}
276
277- (NSDate *)earlierDate:(NSDate *)anotherDate
278{
279	if ([[self _make_oc_value] earlierDate:anotherDate] == self) {
280		return self;
281	} else {
282		return anotherDate;
283	}
284}
285
286
287- (BOOL)isEqualToDate:(NSDate *)anotherDate
288{
289	return [[self _make_oc_value] isEqualToDate:anotherDate];
290}
291
292
293- (NSDate *)laterDate:(NSDate *)anotherDate
294{
295	if ([[self _make_oc_value] laterDate:anotherDate] == self) {
296		return self;
297	} else {
298		return anotherDate;
299	}
300}
301
302- (NSTimeInterval)timeIntervalSince1970
303{
304	return [[self _make_oc_value] timeIntervalSince1970];
305}
306
307- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate
308{
309	return [[self _make_oc_value] timeIntervalSinceDate:anotherDate];
310}
311
312- (NSTimeInterval)timeIntervalSinceNow
313{
314	return [[self _make_oc_value] timeIntervalSinceNow];
315}
316
317
318@end /* implementation OC_PythonDate */
319