common_date.cpp 6.9 KB

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