1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
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 #include "zend_portability.h"
29
30 /* {{{ timezone_convert_datetimezone
31 * 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)32 U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
33 void *object,
34 int is_datetime,
35 intl_error *outside_error,
36 const char *func)
37 {
38 char *id = NULL,
39 offset_id[] = "GMT+00:00";
40 int32_t id_len = 0;
41 char *message;
42 TimeZone *timeZone;
43
44 switch (type) {
45 case TIMELIB_ZONETYPE_ID:
46 id = is_datetime
47 ? ((php_date_obj*)object)->time->tz_info->name
48 : ((php_timezone_obj*)object)->tzi.tz->name;
49 id_len = strlen(id);
50 break;
51 case TIMELIB_ZONETYPE_OFFSET: {
52 int offset_mins = is_datetime
53 ? ((php_date_obj*)object)->time->z / 60
54 : (int)((php_timezone_obj*)object)->tzi.utc_offset / 60,
55 hours = offset_mins / 60,
56 minutes = offset_mins - hours * 60;
57 minutes *= minutes > 0 ? 1 : -1;
58
59 if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) {
60 spprintf(&message, 0, "%s: object has an time zone offset "
61 "that's too large", func);
62 intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
63 message, 1);
64 efree(message);
65 return NULL;
66 }
67
68 id = offset_id;
69 id_len = slprintf(id, sizeof(offset_id), "GMT%+03d:%02d",
70 hours, minutes);
71 break;
72 }
73 case TIMELIB_ZONETYPE_ABBR:
74 id = is_datetime
75 ? ((php_date_obj*)object)->time->tz_abbr
76 : ((php_timezone_obj*)object)->tzi.z.abbr;
77 id_len = strlen(id);
78 break;
79 }
80
81 UnicodeString s = UnicodeString(id, id_len, US_INV);
82 timeZone = TimeZone::createTimeZone(s);
83 #if U_ICU_VERSION_MAJOR_NUM >= 49
84 if (*timeZone == TimeZone::getUnknown()) {
85 #else
86 UnicodeString resultingId;
87 timeZone->getID(resultingId);
88 if (resultingId == UnicodeString("Etc/Unknown", -1, US_INV)
89 || resultingId == UnicodeString("GMT", -1, US_INV)) {
90 #endif
91 spprintf(&message, 0, "%s: time zone id '%s' "
92 "extracted from ext/date DateTimeZone not recognized", func, id);
93 intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
94 message, 1);
95 efree(message);
96 delete timeZone;
97 return NULL;
98 }
99 return timeZone;
100 }
101 /* }}} */
102
103 U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
104 intl_error *err, const char *func)
105 {
106 zval retval;
107 zval zfuncname;
108 char *message;
109
110 if (err && U_FAILURE(err->code)) {
111 return FAILURE;
112 }
113
114 if (millis) {
115 *millis = ZEND_NAN;
116 }
117 if (tz) {
118 *tz = NULL;
119 }
120
121 if (millis) {
122 php_date_obj *datetime;
123
124 ZVAL_STRING(&zfuncname, "getTimestamp");
125 if (call_user_function(NULL, z, &zfuncname, &retval, 0, NULL)
126 != SUCCESS || Z_TYPE(retval) != IS_LONG) {
127 spprintf(&message, 0, "%s: error calling ::getTimeStamp() on the "
128 "object", func);
129 intl_errors_set(err, U_INTERNAL_PROGRAM_ERROR,
130 message, 1);
131 efree(message);
132 zval_ptr_dtor(&zfuncname);
133 return FAILURE;
134 }
135
136 datetime = Z_PHPDATE_P(z);
137 *millis = U_MILLIS_PER_SECOND * (double)Z_LVAL(retval) + (datetime->time->us / 1000);
138 zval_ptr_dtor(&zfuncname);
139 }
140
141 if (tz) {
142 php_date_obj *datetime;
143 datetime = Z_PHPDATE_P(z);
144 if (!datetime->time) {
145 spprintf(&message, 0, "%s: the %s object is not properly "
146 "initialized", func, ZSTR_VAL(Z_OBJCE_P(z)->name));
147 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
148 message, 1);
149 efree(message);
150 return FAILURE;
151 }
152 if (!datetime->time->is_localtime) {
153 *tz = TimeZone::getGMT()->clone();
154 } else {
155 *tz = timezone_convert_datetimezone(datetime->time->zone_type,
156 datetime, 1, NULL, func);
157 if (*tz == NULL) {
158 spprintf(&message, 0, "%s: could not convert DateTime's "
159 "time zone", func);
160 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
161 message, 1);
162 efree(message);
163 return FAILURE;
164 }
165 }
166 }
167
168 return SUCCESS;
169 }
170
171 U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func)
172 {
173 double rv = ZEND_NAN;
174 zend_long lv;
175 int type;
176 char *message;
177
178 if (err && U_FAILURE(err->code)) {
179 return ZEND_NAN;
180 }
181
182 switch (Z_TYPE_P(z)) {
183 case IS_STRING:
184 type = is_numeric_string(Z_STRVAL_P(z), Z_STRLEN_P(z), &lv, &rv, 0);
185 if (type == IS_DOUBLE) {
186 rv *= U_MILLIS_PER_SECOND;
187 } else if (type == IS_LONG) {
188 rv = U_MILLIS_PER_SECOND * (double)lv;
189 } else {
190 spprintf(&message, 0, "%s: string '%s' is not numeric, "
191 "which would be required for it to be a valid date", func,
192 Z_STRVAL_P(z));
193 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
194 message, 1);
195 efree(message);
196 }
197 break;
198 case IS_LONG:
199 rv = U_MILLIS_PER_SECOND * (double)Z_LVAL_P(z);
200 break;
201 case IS_DOUBLE:
202 rv = U_MILLIS_PER_SECOND * Z_DVAL_P(z);
203 break;
204 case IS_OBJECT:
205 if (instanceof_function(Z_OBJCE_P(z), php_date_get_interface_ce())) {
206 intl_datetime_decompose(z, &rv, NULL, err, func);
207 } else if (instanceof_function(Z_OBJCE_P(z), Calendar_ce_ptr)) {
208 Calendar_object *co = Z_INTL_CALENDAR_P(z);
209 if (co->ucal == NULL) {
210 spprintf(&message, 0, "%s: IntlCalendar object is not properly "
211 "constructed", func);
212 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
213 message, 1);
214 efree(message);
215 } else {
216 UErrorCode status = UErrorCode();
217 rv = (double)co->ucal->getTime(status);
218 if (U_FAILURE(status)) {
219 spprintf(&message, 0, "%s: call to internal "
220 "Calendar::getTime() has failed", func);
221 intl_errors_set(err, status, message, 1);
222 efree(message);
223 }
224 }
225 } else {
226 /* TODO: try with cast(), get() to obtain a number */
227 spprintf(&message, 0, "%s: invalid object type for date/time "
228 "(only IntlCalendar and DateTimeInterface permitted)", func);
229 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
230 message, 1);
231 efree(message);
232 }
233 break;
234 default:
235 spprintf(&message, 0, "%s: invalid PHP type for date", func);
236 intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
237 message, 1);
238 efree(message);
239 break;
240 }
241
242 return rv;
243 }
244