|
| 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 | + | [email protected] so we can mail you a copy immediately. | |
| 12 | + +----------------------------------------------------------------------+ |
| 13 | + | Authors: Gustavo Lopes <[email protected]> | |
| 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. */ |
| 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 | + |
0 commit comments