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