summaryrefslogtreecommitdiff
path: root/ext/intl/common/common_date.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/intl/common/common_date.cpp')
-rw-r--r--ext/intl/common/common_date.cpp250
1 files changed, 250 insertions, 0 deletions
diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp
new file mode 100644
index 0000000000..ee998818d9
--- /dev/null
+++ b/ext/intl/common/common_date.cpp
@@ -0,0 +1,250 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "../intl_cppshims.h"
+
+#include <unicode/calendar.h>
+
+extern "C" {
+#include "../php_intl.h"
+#define USE_CALENDAR_POINTER 1
+#include "../calendar/calendar_class.h"
+#include <ext/date/php_date.h>
+}
+
+#ifndef INFINITY
+#define INFINITY (DBL_MAX+DBL_MAX)
+#endif
+
+#ifndef NAN
+#define NAN (INFINITY-INFINITY)
+#endif
+
+/* {{{ timezone_convert_datetimezone
+ * The timezone in DateTime and DateTimeZone is not unified. */
+U_CFUNC TimeZone *timezone_convert_datetimezone(int type,
+ void *object,
+ int is_datetime,
+ intl_error *outside_error,
+ const char *func TSRMLS_DC)
+{
+ char *id = NULL,
+ offset_id[] = "GMT+00:00";
+ int id_len = 0;
+ char *message;
+ TimeZone *timeZone;
+
+ switch (type) {
+ case TIMELIB_ZONETYPE_ID:
+ id = is_datetime
+ ? ((php_date_obj*)object)->time->tz_info->name
+ : ((php_timezone_obj*)object)->tzi.tz->name;
+ id_len = strlen(id);
+ break;
+ case TIMELIB_ZONETYPE_OFFSET: {
+ int offset_mins = is_datetime
+ ? -((php_date_obj*)object)->time->z
+ : -(int)((php_timezone_obj*)object)->tzi.utc_offset,
+ hours = offset_mins / 60,
+ minutes = offset_mins - hours * 60;
+ minutes *= minutes > 0 ? 1 : -1;
+
+ if (offset_mins <= -24 * 60 || offset_mins >= 24 * 60) {
+ spprintf(&message, 0, "%s: object has an time zone offset "
+ "that's too large", func);
+ intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ return NULL;
+ }
+
+ id = offset_id;
+ id_len = slprintf(id, sizeof(offset_id), "GMT%+03d:%02d",
+ hours, minutes);
+ break;
+ }
+ case TIMELIB_ZONETYPE_ABBR:
+ id = is_datetime
+ ? ((php_date_obj*)object)->time->tz_abbr
+ : ((php_timezone_obj*)object)->tzi.z.abbr;
+ id_len = strlen(id);
+ break;
+ }
+
+ UnicodeString s = UnicodeString(id, id_len, US_INV);
+ timeZone = TimeZone::createTimeZone(s);
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+ if (*timeZone == TimeZone::getUnknown()) {
+#else
+ UnicodeString resultingId;
+ timeZone->getID(resultingId);
+ if (resultingId == UnicodeString("Etc/Unknown", -1, US_INV)
+ || resultingId == UnicodeString("GMT", -1, US_INV)) {
+#endif
+ spprintf(&message, 0, "%s: time zone id '%s' "
+ "extracted from ext/date DateTimeZone not recognized", func, id);
+ intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ delete timeZone;
+ return NULL;
+ }
+ return timeZone;
+}
+/* }}} */
+
+U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz,
+ intl_error *err, const char *func TSRMLS_DC)
+{
+ zval retval;
+ zval *zfuncname;
+ char *message;
+
+ if (err && U_FAILURE(err->code)) {
+ return FAILURE;
+ }
+
+ if (millis) {
+ *millis = NAN;
+ }
+ if (tz) {
+ *tz = NULL;
+ }
+
+ if (millis) {
+ INIT_ZVAL(retval);
+ MAKE_STD_ZVAL(zfuncname);
+ ZVAL_STRING(zfuncname, "getTimestamp", 1);
+ if (call_user_function(NULL, &(z), zfuncname, &retval, 0, NULL TSRMLS_CC)
+ != SUCCESS || Z_TYPE(retval) != IS_LONG) {
+ spprintf(&message, 0, "%s: error calling ::getTimeStamp() on the "
+ "object", func);
+ intl_errors_set(err, U_INTERNAL_PROGRAM_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ zval_ptr_dtor(&zfuncname);
+ return FAILURE;
+ }
+
+ *millis = U_MILLIS_PER_SECOND * (double)Z_LVAL(retval);
+ zval_ptr_dtor(&zfuncname);
+ }
+
+ if (tz) {
+ php_date_obj *datetime;
+ datetime = (php_date_obj*)zend_object_store_get_object(z TSRMLS_CC);
+ if (!datetime->time) {
+ spprintf(&message, 0, "%s: the DateTime object is not properly "
+ "initialized", func);
+ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ return FAILURE;
+ }
+ if (!datetime->time->is_localtime) {
+ *tz = TimeZone::getGMT()->clone();
+ } else {
+ *tz = timezone_convert_datetimezone(datetime->time->zone_type,
+ datetime, 1, NULL, func TSRMLS_CC);
+ if (*tz == NULL) {
+ spprintf(&message, 0, "%s: could not convert DateTime's "
+ "time zone", func);
+ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ return FAILURE;
+ }
+ }
+ }
+
+ return SUCCESS;
+}
+
+U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err, const char *func TSRMLS_DC)
+{
+ double rv = NAN;
+ long lv;
+ int type;
+ char *message;
+
+ if (err && U_FAILURE(err->code)) {
+ return NAN;
+ }
+
+ switch (Z_TYPE_P(z)) {
+ case IS_STRING:
+ type = is_numeric_string(Z_STRVAL_P(z), Z_STRLEN_P(z), &lv, &rv, 0);
+ if (type == IS_DOUBLE) {
+ rv *= U_MILLIS_PER_SECOND;
+ } else if (type == IS_LONG) {
+ rv = U_MILLIS_PER_SECOND * (double)lv;
+ } else {
+ spprintf(&message, 0, "%s: string '%s' is not numeric, "
+ "which would be required for it to be a valid date", func,
+ Z_STRVAL_P(z));
+ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ }
+ break;
+ case IS_LONG:
+ rv = U_MILLIS_PER_SECOND * (double)Z_LVAL_P(z);
+ break;
+ case IS_DOUBLE:
+ rv = U_MILLIS_PER_SECOND * Z_DVAL_P(z);
+ break;
+ case IS_OBJECT:
+ if (instanceof_function(Z_OBJCE_P(z), php_date_get_date_ce() TSRMLS_CC)) {
+ intl_datetime_decompose(z, &rv, NULL, err, func TSRMLS_CC);
+ } else if (instanceof_function(Z_OBJCE_P(z), Calendar_ce_ptr TSRMLS_CC)) {
+ Calendar_object *co = (Calendar_object *)
+ zend_object_store_get_object(z TSRMLS_CC );
+ if (co->ucal == NULL) {
+ spprintf(&message, 0, "%s: IntlCalendar object is not properly "
+ "constructed", func);
+ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ } else {
+ UErrorCode status = UErrorCode();
+ rv = (double)co->ucal->getTime(status);
+ if (U_FAILURE(status)) {
+ spprintf(&message, 0, "%s: call to internal "
+ "Calendar::getTime() has failed", func);
+ intl_errors_set(err, status, message, 1 TSRMLS_CC);
+ efree(message);
+ }
+ }
+ } else {
+ /* TODO: try with cast(), get() to obtain a number */
+ spprintf(&message, 0, "%s: invalid object type for date/time "
+ "(only IntlCalendar and DateTime permitted)", func);
+ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ }
+ break;
+ default:
+ spprintf(&message, 0, "%s: invalid PHP type for date", func);
+ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR,
+ message, 1 TSRMLS_CC);
+ efree(message);
+ break;
+ }
+
+ return rv;
+}
+