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