common_date.cpp 6.6 KB

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