1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Gustavo Lopes <cataphract@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "../intl_cppshims.h"
18
19 #include <unicode/calendar.h>
20
21 extern "C" {
22 #include "../php_intl.h"
23 #define USE_CALENDAR_POINTER 1
24 #include "../calendar/calendar_class.h"
25 #include <ext/date/php_date.h>
26 }
27
28 #ifndef INFINITY
29 #define INFINITY (DBL_MAX+DBL_MAX)
30 #endif
31
32 #ifndef NAN
33 #define NAN (INFINITY-INFINITY)
34 #endif
35
36 /* {{{ timezone_convert_datetimezone
37 * The timezone in DateTime and DateTimeZone is not unified. */
timezone_convert_datetimezone(int type,void * object,int is_datetime,intl_error * outside_error,const char * func TSRMLS_DC)38 U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
39 void *object,
40 int is_datetime,
41 intl_error *outside_error,
42 const char *func TSRMLS_DC)
43 {
44 char *id = NULL,
45 offset_id[] = "GMT+00:00";
46 int id_len = 0;
47 char *message;
48 TimeZone *timeZone;
49
50 switch (type) {
51 case TIMELIB_ZONETYPE_ID:
52 id = is_datetime
53 ? ((php_date_obj*)object)->time->tz_info->name
54 : ((php_timezone_obj*)object)->tzi.tz->name;
55 id_len = strlen(id);
56 break;
57 case TIMELIB_ZONETYPE_OFFSET: {
58 int offset_mins = is_datetime
59 ? -((php_date_obj*)object)->time->z
60 : -(int)((php_timezone_obj*)object)->tzi.utc_offset,
61 hours = offset_mins / 60,
62 minutes = offset_mins - hours * 60;
63 minutes *= minutes > 0 ? 1 : -1;
64
65 if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) {
66 spprintf(&message, 0, "%s: object has an time zone offset "
67 "that's too large", func);
68 intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
69 message, 1 TSRMLS_CC);
70 efree(message);
71 return NULL;
72 }
73
74 id = offset_id;
75 id_len = slprintf(id, sizeof(offset_id), "GMT%+03d:%02d",
76 hours, minutes);
77 break;
78 }
79 case TIMELIB_ZONETYPE_ABBR:
80 id = is_datetime
81 ? ((php_date_obj*)object)->time->tz_abbr
82 : ((php_timezone_obj*)object)->tzi.z.abbr;
83 id_len = strlen(id);
84 break;
85 }
86
87 UnicodeString s = UnicodeString(id, id_len, US_INV);
88 timeZone = TimeZone::createTimeZone(s);
89 #if U_ICU_VERSION_MAJOR_NUM >= 49
90 if (*timeZone == TimeZone::getUnknown()) {
91 #else
92 UnicodeString resultingId;
93 timeZone->getID(resultingId);
94 if (resultingId == UnicodeString("Etc/Unknown", -1, US_INV)
95 || resultingId == UnicodeString("GMT", -1, US_INV)) {
96 #endif
97 spprintf(&message, 0, "%s: time zone id '%s' "
98 "extracted from ext/date DateTimeZone not recognized", func, id);
99 intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
100 message, 1 TSRMLS_CC);
101 efree(message);
102 delete timeZone;
103 return NULL;
104 }
105 return timeZone;
106 }
107 /* }}} */
108
109 U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
110 intl_error *err, const char *func TSRMLS_DC)
111 {
112 zval retval;
113 zval *zfuncname;
114 char *message;
115
116 if (err && U_FAILURE(err->code)) {
117 return FAILURE;
118 }
119
120 if (millis) {
121 *millis = NAN;
122 }
123 if (tz) {
124 *tz = NULL;
125 }
126
127 if (millis) {
128 INIT_ZVAL(retval);
129 MAKE_STD_ZVAL(zfuncname);
130 ZVAL_STRING(zfuncname, "getTimestamp", 1);
131 if (call_user_function(NULL, &(z), zfuncname, &retval, 0, NULL TSRMLS_CC)
132 != SUCCESS || Z_TYPE(retval) != IS_LONG) {
133 spprintf(&message, 0, "%s: error calling ::getTimeStamp() on the "
134 "object", func);
135 intl_errors_set(err, U_INTERNAL_PROGRAM_ERROR,
136 message, 1 TSRMLS_CC);
137 efree(message);
138 zval_ptr_dtor(&zfuncname);
139 return FAILURE;
140 }
141
142 *millis = U_MILLIS_PER_SECOND * (double)Z_LVAL(retval);
143 zval_ptr_dtor(&zfuncname);
144 }
145
146 if (tz) {
147 php_date_obj *datetime;
148 datetime = (php_date_obj*)zend_object_store_get_object(z TSRMLS_CC);
149 if (!datetime->time) {
150 spprintf(&message, 0, "%s: the DateTime object is not properly "
151 "initialized", func);
152 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
153 message, 1 TSRMLS_CC);
154 efree(message);
155 return FAILURE;
156 }
157 if (!datetime->time->is_localtime) {
158 *tz = TimeZone::getGMT()->clone();
159 } else {
160 *tz = timezone_convert_datetimezone(datetime->time->zone_type,
161 datetime, 1, NULL, func TSRMLS_CC);
162 if (*tz == NULL) {
163 spprintf(&message, 0, "%s: could not convert DateTime's "
164 "time zone", func);
165 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
166 message, 1 TSRMLS_CC);
167 efree(message);
168 return FAILURE;
169 }
170 }
171 }
172
173 return SUCCESS;
174 }
175
176 U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func TSRMLS_DC)
177 {
178 double rv = NAN;
179 long lv;
180 int type;
181 char *message;
182
183 if (err && U_FAILURE(err->code)) {
184 return NAN;
185 }
186
187 switch (Z_TYPE_P(z)) {
188 case IS_STRING:
189 type = is_numeric_string(Z_STRVAL_P(z), Z_STRLEN_P(z), &lv, &rv, 0);
190 if (type == IS_DOUBLE) {
191 rv *= U_MILLIS_PER_SECOND;
192 } else if (type == IS_LONG) {
193 rv = U_MILLIS_PER_SECOND * (double)lv;
194 } else {
195 spprintf(&message, 0, "%s: string '%s' is not numeric, "
196 "which would be required for it to be a valid date", func,
197 Z_STRVAL_P(z));
198 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
199 message, 1 TSRMLS_CC);
200 efree(message);
201 }
202 break;
203 case IS_LONG:
204 rv = U_MILLIS_PER_SECOND * (double)Z_LVAL_P(z);
205 break;
206 case IS_DOUBLE:
207 rv = U_MILLIS_PER_SECOND * Z_DVAL_P(z);
208 break;
209 case IS_OBJECT:
210 if (instanceof_function(Z_OBJCE_P(z), php_date_get_date_ce() TSRMLS_CC)) {
211 intl_datetime_decompose(z, &rv, NULL, err, func TSRMLS_CC);
212 } else if (instanceof_function(Z_OBJCE_P(z), Calendar_ce_ptr TSRMLS_CC)) {
213 Calendar_object *co = (Calendar_object *)
214 zend_object_store_get_object(z TSRMLS_CC );
215 if (co->ucal == NULL) {
216 spprintf(&message, 0, "%s: IntlCalendar object is not properly "
217 "constructed", func);
218 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
219 message, 1 TSRMLS_CC);
220 efree(message);
221 } else {
222 UErrorCode status = UErrorCode();
223 rv = (double)co->ucal->getTime(status);
224 if (U_FAILURE(status)) {
225 spprintf(&message, 0, "%s: call to internal "
226 "Calendar::getTime() has failed", func);
227 intl_errors_set(err, status, message, 1 TSRMLS_CC);
228 efree(message);
229 }
230 }
231 } else {
232 /* TODO: try with cast(), get() to obtain a number */
233 spprintf(&message, 0, "%s: invalid object type for date/time "
234 "(only IntlCalendar and DateTime permitted)", func);
235 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
236 message, 1 TSRMLS_CC);
237 efree(message);
238 }
239 break;
240 default:
241 spprintf(&message, 0, "%s: invalid PHP type for date", func);
242 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
243 message, 1 TSRMLS_CC);
244 efree(message);
245 break;
246 }
247
248 return rv;
249 }
250
251