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: Kirti Velankar <kirtig@yahoo-inc.com> |
12 +----------------------------------------------------------------------+
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include <config.h>
17 #endif
18
19 #include <unicode/ustring.h>
20 #include <math.h>
21
22 #include "php_intl.h"
23 #include "intl_convert.h"
24 #include "dateformat.h"
25 #include "dateformat_class.h"
26 #include "dateformat_data.h"
27
28 /* {{{
29 * Internal function which calls the udat_parse
30 * param int store_error acts like a boolean
31 * if set to 1 - store any error encountered in the parameter parse_error
32 * if set to 0 - no need to store any error encountered in the parameter parse_error
33 */
internal_parse_to_timestamp(IntlDateFormatter_object * dfo,char * text_to_parse,size_t text_len,int32_t * parse_pos,bool update_calendar,zval * return_value)34 static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, bool update_calendar, zval *return_value)
35 {
36 double result = 0;
37 UDate timestamp =0;
38 UChar* text_utf16 = NULL;
39 int32_t text_utf16_len = 0;
40
41 /* Convert timezone to UTF-16. */
42 intl_convert_utf8_to_utf16(&text_utf16, &text_utf16_len, text_to_parse, text_len, &INTL_DATA_ERROR_CODE(dfo));
43 INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" );
44
45 if (UNEXPECTED(update_calendar)) {
46 UCalendar *parsed_calendar = (UCalendar *)udat_getCalendar(DATE_FORMAT_OBJECT(dfo));
47 udat_parseCalendar(DATE_FORMAT_OBJECT(dfo), parsed_calendar, text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
48 if (text_utf16) {
49 efree(text_utf16);
50 }
51 INTL_METHOD_CHECK_STATUS( dfo, "Calendar parsing failed" );
52 timestamp = ucal_getMillis( parsed_calendar, &INTL_DATA_ERROR_CODE(dfo));
53 } else {
54 timestamp = udat_parse(DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
55 if (text_utf16) {
56 efree(text_utf16);
57 }
58 }
59
60 INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" );
61 /* Since return is in sec. */
62 result = (double)timestamp / U_MILLIS_PER_SECOND;
63 if (result > (double)LONG_MAX || result < (double)LONG_MIN) {
64 ZVAL_DOUBLE(return_value, result<0?ceil(result):floor(result));
65 } else {
66 ZVAL_LONG(return_value, (zend_long)result);
67 }
68 }
69 /* }}} */
70
add_to_localtime_arr(IntlDateFormatter_object * dfo,zval * return_value,const UCalendar * parsed_calendar,zend_long calendar_field,char * key_name)71 static void add_to_localtime_arr( IntlDateFormatter_object *dfo, zval* return_value, const UCalendar *parsed_calendar, zend_long calendar_field, char* key_name)
72 {
73 zend_long calendar_field_val = ucal_get( parsed_calendar, calendar_field, &INTL_DATA_ERROR_CODE(dfo));
74 INTL_METHOD_CHECK_STATUS( dfo, "Date parsing - localtime failed : could not get a field from calendar" );
75
76 if( strcmp(key_name, CALENDAR_YEAR )==0 ){
77 /* since tm_year is years from 1900 */
78 add_assoc_long( return_value, key_name,( calendar_field_val-1900) );
79 }else if( strcmp(key_name, CALENDAR_WDAY )==0 ){
80 /* since tm_wday starts from 0 whereas ICU WDAY start from 1 */
81 add_assoc_long( return_value, key_name,( calendar_field_val-1) );
82 }else{
83 add_assoc_long( return_value, key_name, calendar_field_val );
84 }
85 }
86
87 /* {{{ Internal function which calls the udat_parseCalendar */
internal_parse_to_localtime(IntlDateFormatter_object * dfo,char * text_to_parse,size_t text_len,int32_t * parse_pos,zval * return_value)88 static void internal_parse_to_localtime(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, zval *return_value)
89 {
90 UCalendar *parsed_calendar = NULL;
91 UChar* text_utf16 = NULL;
92 int32_t text_utf16_len = 0;
93 zend_long isInDST = 0;
94
95 /* Convert timezone to UTF-16. */
96 intl_convert_utf8_to_utf16(&text_utf16, &text_utf16_len, text_to_parse, text_len, &INTL_DATA_ERROR_CODE(dfo));
97 INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" );
98
99 parsed_calendar = (UCalendar *)udat_getCalendar(DATE_FORMAT_OBJECT(dfo));
100 udat_parseCalendar( DATE_FORMAT_OBJECT(dfo), parsed_calendar, text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
101
102 if (text_utf16) {
103 efree(text_utf16);
104 }
105
106 INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" );
107
108
109 array_init( return_value );
110 /* Add entries from various fields of the obtained parsed_calendar */
111 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_SECOND, CALENDAR_SEC);
112 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_MINUTE, CALENDAR_MIN);
113 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_HOUR_OF_DAY, CALENDAR_HOUR);
114 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_YEAR, CALENDAR_YEAR);
115 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_DAY_OF_MONTH, CALENDAR_MDAY);
116 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_DAY_OF_WEEK, CALENDAR_WDAY);
117 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_DAY_OF_YEAR, CALENDAR_YDAY);
118 add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_MONTH, CALENDAR_MON);
119
120 /* Is in DST? */
121 isInDST = ucal_inDaylightTime(parsed_calendar , &INTL_DATA_ERROR_CODE(dfo));
122 INTL_METHOD_CHECK_STATUS( dfo, "Date parsing - localtime failed : while checking if currently in DST." );
123 add_assoc_long( return_value, CALENDAR_ISDST,isInDST==1);
124 }
125 /* }}} */
126
127
128 /* {{{ Parse the string $value starting at parse_pos to a Unix timestamp -int */
PHP_FUNCTION(datefmt_parse)129 PHP_FUNCTION(datefmt_parse)
130 {
131 char* text_to_parse = NULL;
132 size_t text_len =0;
133 zval* z_parse_pos = NULL;
134 int32_t parse_pos = -1;
135
136 DATE_FORMAT_METHOD_INIT_VARS;
137
138 /* Parse parameters. */
139 if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Os|z!",
140 &object, IntlDateFormatter_ce_ptr, &text_to_parse, &text_len, &z_parse_pos ) == FAILURE ){
141 RETURN_THROWS();
142 }
143
144 /* Fetch the object. */
145 DATE_FORMAT_METHOD_FETCH_OBJECT;
146
147 if (z_parse_pos) {
148 zend_long long_parse_pos;
149 ZVAL_DEREF(z_parse_pos);
150 long_parse_pos = zval_get_long(z_parse_pos);
151 if (ZEND_LONG_INT_OVFL(long_parse_pos)) {
152 intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
153 intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0);
154 RETURN_FALSE;
155 }
156 parse_pos = (int32_t)long_parse_pos;
157 if ((size_t)parse_pos > text_len) {
158 RETURN_FALSE;
159 }
160 }
161 internal_parse_to_timestamp( dfo, text_to_parse, text_len, z_parse_pos ? &parse_pos : NULL, false, return_value);
162 if (z_parse_pos) {
163 zval_ptr_dtor(z_parse_pos);
164 ZVAL_LONG(z_parse_pos, parse_pos);
165 }
166 }
167 /* }}} */
168
PHP_METHOD(IntlDateFormatter,parseToCalendar)169 PHP_METHOD(IntlDateFormatter, parseToCalendar)
170 {
171 zend_string *text_to_parse = NULL;
172 zval* z_parse_pos = NULL;
173 int32_t parse_pos = -1;
174
175 DATE_FORMAT_METHOD_INIT_VARS;
176
177 ZEND_PARSE_PARAMETERS_START(1, 2)
178 Z_PARAM_STR(text_to_parse)
179 Z_PARAM_OPTIONAL
180 Z_PARAM_ZVAL(z_parse_pos)
181 ZEND_PARSE_PARAMETERS_END();
182
183 object = ZEND_THIS;
184
185 /* Fetch the object. */
186 DATE_FORMAT_METHOD_FETCH_OBJECT;
187
188 if (z_parse_pos) {
189 zend_long long_parse_pos;
190 ZVAL_DEREF(z_parse_pos);
191 bool failed = false;
192 long_parse_pos = zval_try_get_long(z_parse_pos, &failed);
193 if (failed) {
194 zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos));
195 RETURN_THROWS();
196 }
197 if (ZEND_LONG_INT_OVFL(long_parse_pos)) {
198 intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
199 intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0);
200 RETURN_FALSE;
201 }
202 parse_pos = (int32_t)long_parse_pos;
203 if (parse_pos != -1 && (size_t)parse_pos > ZSTR_LEN(text_to_parse)) {
204 RETURN_FALSE;
205 }
206 }
207 internal_parse_to_timestamp( dfo, ZSTR_VAL(text_to_parse), ZSTR_LEN(text_to_parse), z_parse_pos ? &parse_pos : NULL, true, return_value);
208 if (z_parse_pos) {
209 zval_ptr_dtor(z_parse_pos);
210 ZVAL_LONG(z_parse_pos, parse_pos);
211 }
212 }
213
214 /* {{{ Parse the string $value to a localtime array */
PHP_FUNCTION(datefmt_localtime)215 PHP_FUNCTION(datefmt_localtime)
216 {
217 char* text_to_parse = NULL;
218 size_t text_len =0;
219 zval* z_parse_pos = NULL;
220 int32_t parse_pos = -1;
221
222 DATE_FORMAT_METHOD_INIT_VARS;
223
224 /* Parse parameters. */
225 if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Os|z!",
226 &object, IntlDateFormatter_ce_ptr, &text_to_parse, &text_len, &z_parse_pos ) == FAILURE ){
227 RETURN_THROWS();
228 }
229
230 /* Fetch the object. */
231 DATE_FORMAT_METHOD_FETCH_OBJECT;
232
233 if (z_parse_pos) {
234 zend_long long_parse_pos;
235 ZVAL_DEREF(z_parse_pos);
236 long_parse_pos = zval_get_long(z_parse_pos);
237 if (ZEND_LONG_INT_OVFL(long_parse_pos)) {
238 intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
239 intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0);
240 RETURN_FALSE;
241 }
242 parse_pos = (int32_t)long_parse_pos;
243 if((size_t)parse_pos > text_len) {
244 RETURN_FALSE;
245 }
246 }
247 internal_parse_to_localtime( dfo, text_to_parse, text_len, z_parse_pos?&parse_pos:NULL, return_value);
248 if (z_parse_pos) {
249 zval_ptr_dtor(z_parse_pos);
250 ZVAL_LONG(z_parse_pos, parse_pos);
251 }
252 }
253 /* }}} */
254