root/ext/date/php_date.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_date_llabs
  2. php_date_llabs
  3. php_date_llabs
  4. PHP_INI_BEGIN
  5. php_date_get_immutable_ce
  6. php_date_get_timezone_ce
  7. PHP_GINIT_FUNCTION
  8. _php_date_tzinfo_dtor
  9. PHP_RINIT_FUNCTION
  10. PHP_RSHUTDOWN_FUNCTION
  11. PHP_MINIT_FUNCTION
  12. PHP_MSHUTDOWN_FUNCTION
  13. PHP_MINFO_FUNCTION
  14. php_date_parse_tzfile
  15. php_date_parse_tzfile_wrapper
  16. PHP_INI_MH
  17. guess_timezone
  18. get_timezone_info
  19. english_suffix
  20. php_date_full_day_name
  21. php_date_short_day_name
  22. date_format
  23. php_date
  24. php_format_date
  25. php_idate
  26. PHP_FUNCTION
  27. PHP_FUNCTION
  28. PHP_FUNCTION
  29. php_date_set_tzdb
  30. php_parse_date
  31. PHP_FUNCTION
  32. php_mktime
  33. PHP_FUNCTION
  34. PHP_FUNCTION
  35. PHP_FUNCTION
  36. php_strftime
  37. PHP_FUNCTION
  38. PHP_FUNCTION
  39. PHP_FUNCTION
  40. PHP_FUNCTION
  41. PHP_FUNCTION
  42. date_period_it_invalidate_current
  43. date_period_it_dtor
  44. date_period_it_has_more
  45. date_period_it_current_data
  46. date_period_it_current_key
  47. date_period_it_move_forward
  48. date_period_it_rewind
  49. date_object_period_get_iterator
  50. implement_date_interface_handler
  51. date_register_classes
  52. date_object_new_date_ex
  53. date_object_new_date
  54. date_object_clone_date
  55. date_clone_immutable
  56. date_object_compare_date
  57. date_object_get_gc
  58. date_object_get_gc_timezone
  59. date_object_get_properties
  60. date_object_new_timezone_ex
  61. date_object_new_timezone
  62. date_object_clone_timezone
  63. date_object_get_properties_timezone
  64. date_object_new_interval_ex
  65. date_object_new_interval
  66. date_object_clone_interval
  67. date_object_get_gc_interval
  68. date_object_get_properties_interval
  69. date_object_new_period_ex
  70. date_object_new_period
  71. date_object_clone_period
  72. date_object_free_storage_date
  73. date_object_free_storage_timezone
  74. date_object_free_storage_interval
  75. date_object_free_storage_period
  76. php_date_instantiate
  77. update_errors_warnings
  78. php_date_initialize
  79. PHP_FUNCTION
  80. PHP_FUNCTION
  81. PHP_FUNCTION
  82. PHP_FUNCTION
  83. PHP_METHOD
  84. PHP_METHOD
  85. PHP_METHOD
  86. php_date_initialize_from_hash
  87. PHP_METHOD
  88. PHP_METHOD
  89. PHP_METHOD
  90. zval_from_error_container
  91. PHP_FUNCTION
  92. php_date_do_return_parsed_time
  93. PHP_FUNCTION
  94. PHP_FUNCTION
  95. PHP_FUNCTION
  96. php_date_modify
  97. PHP_FUNCTION
  98. PHP_METHOD
  99. php_date_add
  100. PHP_FUNCTION
  101. PHP_METHOD
  102. php_date_sub
  103. PHP_FUNCTION
  104. PHP_METHOD
  105. set_timezone_from_timelib_time
  106. PHP_FUNCTION
  107. php_date_timezone_set
  108. PHP_FUNCTION
  109. PHP_METHOD
  110. PHP_FUNCTION
  111. php_date_time_set
  112. PHP_FUNCTION
  113. PHP_METHOD
  114. php_date_date_set
  115. PHP_FUNCTION
  116. PHP_METHOD
  117. php_date_isodate_set
  118. PHP_FUNCTION
  119. PHP_METHOD
  120. php_date_timestamp_set
  121. PHP_FUNCTION
  122. PHP_METHOD
  123. PHP_FUNCTION
  124. PHP_FUNCTION
  125. timezone_initialize
  126. PHP_FUNCTION
  127. PHP_METHOD
  128. php_date_timezone_initialize_from_hash
  129. PHP_METHOD
  130. PHP_METHOD
  131. PHP_FUNCTION
  132. PHP_FUNCTION
  133. PHP_FUNCTION
  134. PHP_FUNCTION
  135. PHP_FUNCTION
  136. date_interval_initialize
  137. date_interval_read_property
  138. date_interval_write_property
  139. PHP_METHOD
  140. php_date_interval_initialize_from_hash
  141. PHP_METHOD
  142. PHP_METHOD
  143. PHP_FUNCTION
  144. date_interval_format
  145. PHP_FUNCTION
  146. date_period_initialize
  147. PHP_METHOD
  148. PHP_METHOD
  149. PHP_METHOD
  150. PHP_METHOD
  151. check_id_allowed
  152. PHP_FUNCTION
  153. PHP_FUNCTION
  154. PHP_FUNCTION
  155. PHP_FUNCTION
  156. PHP_FUNCTION
  157. php_do_date_sunrise_sunset
  158. PHP_FUNCTION
  159. PHP_FUNCTION
  160. PHP_FUNCTION
  161. date_object_get_gc_period
  162. date_object_get_properties_period
  163. php_date_period_initialize_from_hash
  164. PHP_METHOD
  165. PHP_METHOD
  166. date_period_read_property
  167. date_period_write_property

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Derick Rethans <derick@derickrethans.nl>                    |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 #include "php.h"
  22 #include "php_streams.h"
  23 #include "php_main.h"
  24 #include "php_globals.h"
  25 #include "php_ini.h"
  26 #include "ext/standard/info.h"
  27 #include "ext/standard/php_versioning.h"
  28 #include "ext/standard/php_math.h"
  29 #include "php_date.h"
  30 #include "zend_interfaces.h"
  31 #include "lib/timelib.h"
  32 #include <time.h>
  33 
  34 #ifdef PHP_WIN32
  35 static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; }
  36 #elif defined(__GNUC__) && __GNUC__ < 3
  37 static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i; }
  38 #else
  39 static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; }
  40 #endif
  41 
  42 #ifdef PHP_WIN32
  43 #define DATE_I64_BUF_LEN 65
  44 # define DATE_I64A(i, s, len) _i64toa_s(i, s, len, 10)
  45 # define DATE_A64I(i, s) i = _atoi64(s)
  46 #else
  47 #define DATE_I64_BUF_LEN 65
  48 # define DATE_I64A(i, s, len) \
  49         do { \
  50                 int st = snprintf(s, len, "%lld", i); \
  51                 s[st] = '\0'; \
  52         } while (0);
  53 #ifdef HAVE_ATOLL
  54 # define DATE_A64I(i, s) i = atoll(s)
  55 #else
  56 # define DATE_A64I(i, s) i = strtoll(s, NULL, 10)
  57 #endif
  58 #endif
  59 
  60 /* {{{ arginfo */
  61 ZEND_BEGIN_ARG_INFO_EX(arginfo_date, 0, 0, 1)
  62         ZEND_ARG_INFO(0, format)
  63         ZEND_ARG_INFO(0, timestamp)
  64 ZEND_END_ARG_INFO()
  65 
  66 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmdate, 0, 0, 1)
  67         ZEND_ARG_INFO(0, format)
  68         ZEND_ARG_INFO(0, timestamp)
  69 ZEND_END_ARG_INFO()
  70 
  71 ZEND_BEGIN_ARG_INFO_EX(arginfo_idate, 0, 0, 1)
  72         ZEND_ARG_INFO(0, format)
  73         ZEND_ARG_INFO(0, timestamp)
  74 ZEND_END_ARG_INFO()
  75 
  76 ZEND_BEGIN_ARG_INFO_EX(arginfo_strtotime, 0, 0, 1)
  77         ZEND_ARG_INFO(0, time)
  78         ZEND_ARG_INFO(0, now)
  79 ZEND_END_ARG_INFO()
  80 
  81 ZEND_BEGIN_ARG_INFO_EX(arginfo_mktime, 0, 0, 0)
  82         ZEND_ARG_INFO(0, hour)
  83         ZEND_ARG_INFO(0, min)
  84         ZEND_ARG_INFO(0, sec)
  85         ZEND_ARG_INFO(0, mon)
  86         ZEND_ARG_INFO(0, day)
  87         ZEND_ARG_INFO(0, year)
  88 ZEND_END_ARG_INFO()
  89 
  90 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmmktime, 0, 0, 0)
  91         ZEND_ARG_INFO(0, hour)
  92         ZEND_ARG_INFO(0, min)
  93         ZEND_ARG_INFO(0, sec)
  94         ZEND_ARG_INFO(0, mon)
  95         ZEND_ARG_INFO(0, day)
  96         ZEND_ARG_INFO(0, year)
  97 ZEND_END_ARG_INFO()
  98 
  99 ZEND_BEGIN_ARG_INFO(arginfo_checkdate, 0)
 100         ZEND_ARG_INFO(0, month)
 101         ZEND_ARG_INFO(0, day)
 102         ZEND_ARG_INFO(0, year)
 103 ZEND_END_ARG_INFO()
 104 
 105 ZEND_BEGIN_ARG_INFO_EX(arginfo_strftime, 0, 0, 1)
 106         ZEND_ARG_INFO(0, format)
 107         ZEND_ARG_INFO(0, timestamp)
 108 ZEND_END_ARG_INFO()
 109 
 110 ZEND_BEGIN_ARG_INFO_EX(arginfo_gmstrftime, 0, 0, 1)
 111         ZEND_ARG_INFO(0, format)
 112         ZEND_ARG_INFO(0, timestamp)
 113 ZEND_END_ARG_INFO()
 114 
 115 ZEND_BEGIN_ARG_INFO(arginfo_time, 0)
 116 ZEND_END_ARG_INFO()
 117 
 118 ZEND_BEGIN_ARG_INFO_EX(arginfo_localtime, 0, 0, 0)
 119         ZEND_ARG_INFO(0, timestamp)
 120         ZEND_ARG_INFO(0, associative_array)
 121 ZEND_END_ARG_INFO()
 122 
 123 ZEND_BEGIN_ARG_INFO_EX(arginfo_getdate, 0, 0, 0)
 124         ZEND_ARG_INFO(0, timestamp)
 125 ZEND_END_ARG_INFO()
 126 
 127 ZEND_BEGIN_ARG_INFO(arginfo_date_default_timezone_set, 0)
 128         ZEND_ARG_INFO(0, timezone_identifier)
 129 ZEND_END_ARG_INFO()
 130 
 131 ZEND_BEGIN_ARG_INFO(arginfo_date_default_timezone_get, 0)
 132 ZEND_END_ARG_INFO()
 133 
 134 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_sunrise, 0, 0, 1)
 135         ZEND_ARG_INFO(0, time)
 136         ZEND_ARG_INFO(0, format)
 137         ZEND_ARG_INFO(0, latitude)
 138         ZEND_ARG_INFO(0, longitude)
 139         ZEND_ARG_INFO(0, zenith)
 140         ZEND_ARG_INFO(0, gmt_offset)
 141 ZEND_END_ARG_INFO()
 142 
 143 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_sunset, 0, 0, 1)
 144         ZEND_ARG_INFO(0, time)
 145         ZEND_ARG_INFO(0, format)
 146         ZEND_ARG_INFO(0, latitude)
 147         ZEND_ARG_INFO(0, longitude)
 148         ZEND_ARG_INFO(0, zenith)
 149         ZEND_ARG_INFO(0, gmt_offset)
 150 ZEND_END_ARG_INFO()
 151 
 152 ZEND_BEGIN_ARG_INFO(arginfo_date_sun_info, 0)
 153         ZEND_ARG_INFO(0, time)
 154         ZEND_ARG_INFO(0, latitude)
 155         ZEND_ARG_INFO(0, longitude)
 156 ZEND_END_ARG_INFO()
 157 
 158 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create, 0, 0, 0)
 159         ZEND_ARG_INFO(0, time)
 160         ZEND_ARG_INFO(0, object)
 161 ZEND_END_ARG_INFO()
 162 
 163 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create_from_format, 0, 0, 2)
 164         ZEND_ARG_INFO(0, format)
 165         ZEND_ARG_INFO(0, time)
 166         ZEND_ARG_INFO(0, object)
 167 ZEND_END_ARG_INFO()
 168 
 169 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_parse, 0, 0, 1)
 170         ZEND_ARG_INFO(0, date)
 171 ZEND_END_ARG_INFO()
 172 
 173 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_parse_from_format, 0, 0, 2)
 174         ZEND_ARG_INFO(0, format)
 175         ZEND_ARG_INFO(0, date)
 176 ZEND_END_ARG_INFO()
 177 
 178 ZEND_BEGIN_ARG_INFO(arginfo_date_get_last_errors, 0)
 179 ZEND_END_ARG_INFO()
 180 
 181 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_format, 0, 0, 2)
 182         ZEND_ARG_INFO(0, object)
 183         ZEND_ARG_INFO(0, format)
 184 ZEND_END_ARG_INFO()
 185 
 186 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_format, 0, 0, 1)
 187         ZEND_ARG_INFO(0, format)
 188 ZEND_END_ARG_INFO()
 189 
 190 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_modify, 0, 0, 2)
 191         ZEND_ARG_INFO(0, object)
 192         ZEND_ARG_INFO(0, modify)
 193 ZEND_END_ARG_INFO()
 194 
 195 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_modify, 0, 0, 1)
 196         ZEND_ARG_INFO(0, modify)
 197 ZEND_END_ARG_INFO()
 198 
 199 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_add, 0, 0, 2)
 200         ZEND_ARG_INFO(0, object)
 201         ZEND_ARG_INFO(0, interval)
 202 ZEND_END_ARG_INFO()
 203 
 204 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_add, 0, 0, 1)
 205         ZEND_ARG_INFO(0, interval)
 206 ZEND_END_ARG_INFO()
 207 
 208 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_sub, 0, 0, 2)
 209         ZEND_ARG_INFO(0, object)
 210         ZEND_ARG_INFO(0, interval)
 211 ZEND_END_ARG_INFO()
 212 
 213 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_sub, 0, 0, 1)
 214         ZEND_ARG_INFO(0, interval)
 215 ZEND_END_ARG_INFO()
 216 
 217 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_get, 0, 0, 1)
 218         ZEND_ARG_INFO(0, object)
 219 ZEND_END_ARG_INFO()
 220 
 221 ZEND_BEGIN_ARG_INFO(arginfo_date_method_timezone_get, 0)
 222 ZEND_END_ARG_INFO()
 223 
 224 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timezone_set, 0, 0, 2)
 225         ZEND_ARG_INFO(0, object)
 226         ZEND_ARG_INFO(0, timezone)
 227 ZEND_END_ARG_INFO()
 228 
 229 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_timezone_set, 0, 0, 1)
 230         ZEND_ARG_INFO(0, timezone)
 231 ZEND_END_ARG_INFO()
 232 
 233 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_offset_get, 0, 0, 1)
 234         ZEND_ARG_INFO(0, object)
 235 ZEND_END_ARG_INFO()
 236 
 237 ZEND_BEGIN_ARG_INFO(arginfo_date_method_offset_get, 0)
 238 ZEND_END_ARG_INFO()
 239 
 240 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_diff, 0, 0, 2)
 241         ZEND_ARG_INFO(0, object)
 242         ZEND_ARG_INFO(0, object2)
 243         ZEND_ARG_INFO(0, absolute)
 244 ZEND_END_ARG_INFO()
 245 
 246 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_diff, 0, 0, 1)
 247         ZEND_ARG_INFO(0, object)
 248         ZEND_ARG_INFO(0, absolute)
 249 ZEND_END_ARG_INFO()
 250 
 251 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_time_set, 0, 0, 3)
 252         ZEND_ARG_INFO(0, object)
 253         ZEND_ARG_INFO(0, hour)
 254         ZEND_ARG_INFO(0, minute)
 255         ZEND_ARG_INFO(0, second)
 256 ZEND_END_ARG_INFO()
 257 
 258 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_time_set, 0, 0, 2)
 259         ZEND_ARG_INFO(0, hour)
 260         ZEND_ARG_INFO(0, minute)
 261         ZEND_ARG_INFO(0, second)
 262 ZEND_END_ARG_INFO()
 263 
 264 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_date_set, 0, 0, 4)
 265         ZEND_ARG_INFO(0, object)
 266         ZEND_ARG_INFO(0, year)
 267         ZEND_ARG_INFO(0, month)
 268         ZEND_ARG_INFO(0, day)
 269 ZEND_END_ARG_INFO()
 270 
 271 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_date_set, 0, 0, 3)
 272         ZEND_ARG_INFO(0, year)
 273         ZEND_ARG_INFO(0, month)
 274         ZEND_ARG_INFO(0, day)
 275 ZEND_END_ARG_INFO()
 276 
 277 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_isodate_set, 0, 0, 3)
 278         ZEND_ARG_INFO(0, object)
 279         ZEND_ARG_INFO(0, year)
 280         ZEND_ARG_INFO(0, week)
 281         ZEND_ARG_INFO(0, day)
 282 ZEND_END_ARG_INFO()
 283 
 284 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_isodate_set, 0, 0, 2)
 285         ZEND_ARG_INFO(0, year)
 286         ZEND_ARG_INFO(0, week)
 287         ZEND_ARG_INFO(0, day)
 288 ZEND_END_ARG_INFO()
 289 
 290 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_set, 0, 0, 2)
 291         ZEND_ARG_INFO(0, object)
 292         ZEND_ARG_INFO(0, unixtimestamp)
 293 ZEND_END_ARG_INFO()
 294 
 295 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_timestamp_set, 0, 0, 1)
 296         ZEND_ARG_INFO(0, unixtimestamp)
 297 ZEND_END_ARG_INFO()
 298 
 299 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_get, 0, 0, 1)
 300         ZEND_ARG_INFO(0, object)
 301 ZEND_END_ARG_INFO()
 302 
 303 ZEND_BEGIN_ARG_INFO(arginfo_date_method_timestamp_get, 0)
 304 ZEND_END_ARG_INFO()
 305 
 306 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_method_create_from_mutable, 0, 0, 1)
 307         ZEND_ARG_INFO(0, DateTime)
 308 ZEND_END_ARG_INFO()
 309 
 310 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_open, 0, 0, 1)
 311         ZEND_ARG_INFO(0, timezone)
 312 ZEND_END_ARG_INFO()
 313 
 314 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_name_get, 0, 0, 1)
 315         ZEND_ARG_INFO(0, object)
 316 ZEND_END_ARG_INFO()
 317 
 318 ZEND_BEGIN_ARG_INFO(arginfo_timezone_method_name_get, 0)
 319 ZEND_END_ARG_INFO()
 320 
 321 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_name_from_abbr, 0, 0, 1)
 322         ZEND_ARG_INFO(0, abbr)
 323         ZEND_ARG_INFO(0, gmtoffset)
 324         ZEND_ARG_INFO(0, isdst)
 325 ZEND_END_ARG_INFO()
 326 
 327 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_offset_get, 0, 0, 2)
 328         ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 0)
 329         ZEND_ARG_OBJ_INFO(0, datetime, DateTimeInterface, 0)
 330 ZEND_END_ARG_INFO()
 331 
 332 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_method_offset_get, 0, 0, 1)
 333         ZEND_ARG_INFO(0, object)
 334 ZEND_END_ARG_INFO()
 335 
 336 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_transitions_get, 0, 0, 1)
 337         ZEND_ARG_INFO(0, object)
 338         ZEND_ARG_INFO(0, timestamp_begin)
 339         ZEND_ARG_INFO(0, timestamp_end)
 340 ZEND_END_ARG_INFO()
 341 
 342 ZEND_BEGIN_ARG_INFO(arginfo_timezone_method_transitions_get, 0)
 343         ZEND_ARG_INFO(0, timestamp_begin)
 344         ZEND_ARG_INFO(0, timestamp_end)
 345 ZEND_END_ARG_INFO()
 346 
 347 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_location_get, 0, 0, 1)
 348         ZEND_ARG_INFO(0, object)
 349 ZEND_END_ARG_INFO()
 350 
 351 ZEND_BEGIN_ARG_INFO(arginfo_timezone_method_location_get, 0)
 352 ZEND_END_ARG_INFO()
 353 
 354 ZEND_BEGIN_ARG_INFO_EX(arginfo_timezone_identifiers_list, 0, 0, 0)
 355         ZEND_ARG_INFO(0, what)
 356         ZEND_ARG_INFO(0, country)
 357 ZEND_END_ARG_INFO()
 358 
 359 ZEND_BEGIN_ARG_INFO(arginfo_timezone_abbreviations_list, 0)
 360 ZEND_END_ARG_INFO()
 361 
 362 ZEND_BEGIN_ARG_INFO(arginfo_timezone_version_get, 0)
 363 ZEND_END_ARG_INFO()
 364 
 365 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_create_from_date_string, 0, 0, 1)
 366         ZEND_ARG_INFO(0, time)
 367 ZEND_END_ARG_INFO()
 368 
 369 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_format, 0, 0, 2)
 370         ZEND_ARG_INFO(0, object)
 371         ZEND_ARG_INFO(0, format)
 372 ZEND_END_ARG_INFO()
 373 
 374 ZEND_BEGIN_ARG_INFO(arginfo_date_method_interval_format, 0)
 375         ZEND_ARG_INFO(0, format)
 376 ZEND_END_ARG_INFO()
 377 
 378 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_period_construct, 0, 0, 3)
 379         ZEND_ARG_INFO(0, start)
 380         ZEND_ARG_INFO(0, interval)
 381         ZEND_ARG_INFO(0, end)
 382 ZEND_END_ARG_INFO()
 383 
 384 ZEND_BEGIN_ARG_INFO_EX(arginfo_date_interval_construct, 0, 0, 1)
 385         ZEND_ARG_INFO(0, interval_spec)
 386 ZEND_END_ARG_INFO()
 387 /* }}} */
 388 
 389 /* {{{ Function table */
 390 const zend_function_entry date_functions[] = {
 391         PHP_FE(strtotime, arginfo_strtotime)
 392         PHP_FE(date, arginfo_date)
 393         PHP_FE(idate, arginfo_idate)
 394         PHP_FE(gmdate, arginfo_gmdate)
 395         PHP_FE(mktime, arginfo_mktime)
 396         PHP_FE(gmmktime, arginfo_gmmktime)
 397         PHP_FE(checkdate, arginfo_checkdate)
 398 
 399 #ifdef HAVE_STRFTIME
 400         PHP_FE(strftime, arginfo_strftime)
 401         PHP_FE(gmstrftime, arginfo_gmstrftime)
 402 #endif
 403 
 404         PHP_FE(time, arginfo_time)
 405         PHP_FE(localtime, arginfo_localtime)
 406         PHP_FE(getdate, arginfo_getdate)
 407 
 408         /* Advanced Interface */
 409         PHP_FE(date_create, arginfo_date_create)
 410         PHP_FE(date_create_immutable, arginfo_date_create)
 411         PHP_FE(date_create_from_format, arginfo_date_create_from_format)
 412         PHP_FE(date_create_immutable_from_format, arginfo_date_create_from_format)
 413         PHP_FE(date_parse, arginfo_date_parse)
 414         PHP_FE(date_parse_from_format, arginfo_date_parse_from_format)
 415         PHP_FE(date_get_last_errors, arginfo_date_get_last_errors)
 416         PHP_FE(date_format, arginfo_date_format)
 417         PHP_FE(date_modify, arginfo_date_modify)
 418         PHP_FE(date_add, arginfo_date_add)
 419         PHP_FE(date_sub, arginfo_date_sub)
 420         PHP_FE(date_timezone_get, arginfo_date_timezone_get)
 421         PHP_FE(date_timezone_set, arginfo_date_timezone_set)
 422         PHP_FE(date_offset_get, arginfo_date_offset_get)
 423         PHP_FE(date_diff, arginfo_date_diff)
 424 
 425         PHP_FE(date_time_set, arginfo_date_time_set)
 426         PHP_FE(date_date_set, arginfo_date_date_set)
 427         PHP_FE(date_isodate_set, arginfo_date_isodate_set)
 428         PHP_FE(date_timestamp_set, arginfo_date_timestamp_set)
 429         PHP_FE(date_timestamp_get, arginfo_date_timestamp_get)
 430 
 431         PHP_FE(timezone_open, arginfo_timezone_open)
 432         PHP_FE(timezone_name_get, arginfo_timezone_name_get)
 433         PHP_FE(timezone_name_from_abbr, arginfo_timezone_name_from_abbr)
 434         PHP_FE(timezone_offset_get, arginfo_timezone_offset_get)
 435         PHP_FE(timezone_transitions_get, arginfo_timezone_transitions_get)
 436         PHP_FE(timezone_location_get, arginfo_timezone_location_get)
 437         PHP_FE(timezone_identifiers_list, arginfo_timezone_identifiers_list)
 438         PHP_FE(timezone_abbreviations_list, arginfo_timezone_abbreviations_list)
 439         PHP_FE(timezone_version_get, arginfo_timezone_version_get)
 440 
 441         PHP_FE(date_interval_create_from_date_string, arginfo_date_interval_create_from_date_string)
 442         PHP_FE(date_interval_format, arginfo_date_interval_format)
 443 
 444         /* Options and Configuration */
 445         PHP_FE(date_default_timezone_set, arginfo_date_default_timezone_set)
 446         PHP_FE(date_default_timezone_get, arginfo_date_default_timezone_get)
 447 
 448         /* Astronomical functions */
 449         PHP_FE(date_sunrise, arginfo_date_sunrise)
 450         PHP_FE(date_sunset, arginfo_date_sunset)
 451         PHP_FE(date_sun_info, arginfo_date_sun_info)
 452         PHP_FE_END
 453 };
 454 
 455 static const zend_function_entry date_funcs_interface[] = {
 456         PHP_ABSTRACT_ME(DateTimeInterface, format, arginfo_date_method_format)
 457         PHP_ABSTRACT_ME(DateTimeInterface, getTimezone, arginfo_date_method_timezone_get)
 458         PHP_ABSTRACT_ME(DateTimeInterface, getOffset, arginfo_date_method_offset_get)
 459         PHP_ABSTRACT_ME(DateTimeInterface, getTimestamp, arginfo_date_method_timestamp_get)
 460         PHP_ABSTRACT_ME(DateTimeInterface, diff, arginfo_date_method_diff)
 461         PHP_ABSTRACT_ME(DateTimeInterface, __wakeup, NULL)
 462         PHP_FE_END
 463 };
 464 
 465 const zend_function_entry date_funcs_date[] = {
 466         PHP_ME(DateTime,                        __construct,            arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
 467         PHP_ME(DateTime,                        __wakeup,                       NULL, ZEND_ACC_PUBLIC)
 468         PHP_ME(DateTime,                        __set_state,            NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 469         PHP_ME_MAPPING(createFromFormat, date_create_from_format,       arginfo_date_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 470         PHP_ME_MAPPING(getLastErrors, date_get_last_errors,     arginfo_date_get_last_errors, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 471         PHP_ME_MAPPING(format,          date_format,            arginfo_date_method_format, 0)
 472         PHP_ME_MAPPING(modify,          date_modify,            arginfo_date_method_modify, 0)
 473         PHP_ME_MAPPING(add,                     date_add,                       arginfo_date_method_add, 0)
 474         PHP_ME_MAPPING(sub,                     date_sub,                       arginfo_date_method_sub, 0)
 475         PHP_ME_MAPPING(getTimezone, date_timezone_get,  arginfo_date_method_timezone_get, 0)
 476         PHP_ME_MAPPING(setTimezone, date_timezone_set,  arginfo_date_method_timezone_set, 0)
 477         PHP_ME_MAPPING(getOffset,       date_offset_get,        arginfo_date_method_offset_get, 0)
 478         PHP_ME_MAPPING(setTime,         date_time_set,          arginfo_date_method_time_set, 0)
 479         PHP_ME_MAPPING(setDate,         date_date_set,          arginfo_date_method_date_set, 0)
 480         PHP_ME_MAPPING(setISODate,      date_isodate_set,       arginfo_date_method_isodate_set, 0)
 481         PHP_ME_MAPPING(setTimestamp,    date_timestamp_set, arginfo_date_method_timestamp_set, 0)
 482         PHP_ME_MAPPING(getTimestamp,    date_timestamp_get, arginfo_date_method_timestamp_get, 0)
 483         PHP_ME_MAPPING(diff,                    date_diff, arginfo_date_method_diff, 0)
 484         PHP_FE_END
 485 };
 486 
 487 const zend_function_entry date_funcs_immutable[] = {
 488         PHP_ME(DateTimeImmutable, __construct,   arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
 489         PHP_ME(DateTime, __wakeup,       NULL, ZEND_ACC_PUBLIC)
 490         PHP_ME(DateTimeImmutable, __set_state,   NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 491         PHP_ME_MAPPING(createFromFormat, date_create_immutable_from_format, arginfo_date_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 492         PHP_ME_MAPPING(getLastErrors,    date_get_last_errors,    arginfo_date_get_last_errors, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 493         PHP_ME_MAPPING(format,           date_format,             arginfo_date_method_format, 0)
 494         PHP_ME_MAPPING(getTimezone, date_timezone_get,  arginfo_date_method_timezone_get, 0)
 495         PHP_ME_MAPPING(getOffset,       date_offset_get,        arginfo_date_method_offset_get, 0)
 496         PHP_ME_MAPPING(getTimestamp,    date_timestamp_get, arginfo_date_method_timestamp_get, 0)
 497         PHP_ME_MAPPING(diff,                    date_diff, arginfo_date_method_diff, 0)
 498         PHP_ME(DateTimeImmutable, modify,        arginfo_date_method_modify, 0)
 499         PHP_ME(DateTimeImmutable, add,           arginfo_date_method_add, 0)
 500         PHP_ME(DateTimeImmutable, sub,           arginfo_date_method_sub, 0)
 501         PHP_ME(DateTimeImmutable, setTimezone,   arginfo_date_method_timezone_set, 0)
 502         PHP_ME(DateTimeImmutable, setTime,       arginfo_date_method_time_set, 0)
 503         PHP_ME(DateTimeImmutable, setDate,       arginfo_date_method_date_set, 0)
 504         PHP_ME(DateTimeImmutable, setISODate,    arginfo_date_method_isodate_set, 0)
 505         PHP_ME(DateTimeImmutable, setTimestamp,  arginfo_date_method_timestamp_set, 0)
 506         PHP_ME(DateTimeImmutable, createFromMutable, arginfo_date_method_create_from_mutable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 507         PHP_FE_END
 508 };
 509 
 510 const zend_function_entry date_funcs_timezone[] = {
 511         PHP_ME(DateTimeZone,              __construct,                 arginfo_timezone_open, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
 512         PHP_ME(DateTimeZone,              __wakeup,                    NULL, ZEND_ACC_PUBLIC)
 513         PHP_ME(DateTimeZone,              __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 514         PHP_ME_MAPPING(getName,           timezone_name_get,           arginfo_timezone_method_name_get, 0)
 515         PHP_ME_MAPPING(getOffset,         timezone_offset_get,         arginfo_timezone_method_offset_get, 0)
 516         PHP_ME_MAPPING(getTransitions,    timezone_transitions_get,    arginfo_timezone_method_transitions_get, 0)
 517         PHP_ME_MAPPING(getLocation,       timezone_location_get,       arginfo_timezone_method_location_get, 0)
 518         PHP_ME_MAPPING(listAbbreviations, timezone_abbreviations_list, arginfo_timezone_abbreviations_list, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 519         PHP_ME_MAPPING(listIdentifiers,   timezone_identifiers_list,   arginfo_timezone_identifiers_list, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 520         PHP_FE_END
 521 };
 522 
 523 const zend_function_entry date_funcs_interval[] = {
 524         PHP_ME(DateInterval,              __construct,                 arginfo_date_interval_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
 525         PHP_ME(DateInterval,              __wakeup,                    NULL, ZEND_ACC_PUBLIC)
 526         PHP_ME(DateInterval,              __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 527         PHP_ME_MAPPING(format,            date_interval_format,        arginfo_date_method_interval_format, 0)
 528         PHP_ME_MAPPING(createFromDateString, date_interval_create_from_date_string,     arginfo_date_interval_create_from_date_string, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 529         PHP_FE_END
 530 };
 531 
 532 const zend_function_entry date_funcs_period[] = {
 533         PHP_ME(DatePeriod,                __construct,                 arginfo_date_period_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
 534         PHP_ME(DatePeriod,                __wakeup,                    NULL, ZEND_ACC_PUBLIC)
 535         PHP_ME(DatePeriod,                __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 536         PHP_ME(DatePeriod,                getStartDate,                NULL, ZEND_ACC_PUBLIC)
 537         PHP_ME(DatePeriod,                getEndDate,                  NULL, ZEND_ACC_PUBLIC)
 538         PHP_ME(DatePeriod,                getDateInterval,             NULL, ZEND_ACC_PUBLIC)
 539         PHP_FE_END
 540 };
 541 
 542 static char* guess_timezone(const timelib_tzdb *tzdb);
 543 static void date_register_classes(void);
 544 /* }}} */
 545 
 546 ZEND_DECLARE_MODULE_GLOBALS(date)
 547 static PHP_GINIT_FUNCTION(date);
 548 
 549 /* True global */
 550 timelib_tzdb *php_date_global_timezone_db;
 551 int php_date_global_timezone_db_enabled;
 552 
 553 #define DATE_DEFAULT_LATITUDE "31.7667"
 554 #define DATE_DEFAULT_LONGITUDE "35.2333"
 555 
 556 /* on 90'35; common sunset declaration (start of sun body appear) */
 557 #define DATE_SUNSET_ZENITH "90.583333"
 558 
 559 /* on 90'35; common sunrise declaration (sun body disappeared) */
 560 #define DATE_SUNRISE_ZENITH "90.583333"
 561 
 562 static PHP_INI_MH(OnUpdate_date_timezone);
 563 
 564 /* {{{ INI Settings */
 565 PHP_INI_BEGIN()
 566         STD_PHP_INI_ENTRY("date.timezone", "", PHP_INI_ALL, OnUpdate_date_timezone, default_timezone, zend_date_globals, date_globals)
 567         PHP_INI_ENTRY("date.default_latitude",           DATE_DEFAULT_LATITUDE,        PHP_INI_ALL, NULL)
 568         PHP_INI_ENTRY("date.default_longitude",          DATE_DEFAULT_LONGITUDE,       PHP_INI_ALL, NULL)
 569         PHP_INI_ENTRY("date.sunset_zenith",              DATE_SUNSET_ZENITH,           PHP_INI_ALL, NULL)
 570         PHP_INI_ENTRY("date.sunrise_zenith",             DATE_SUNRISE_ZENITH,          PHP_INI_ALL, NULL)
 571 PHP_INI_END()
 572 /* }}} */
 573 
 574 zend_class_entry *date_ce_date, *date_ce_timezone, *date_ce_interval, *date_ce_period;
 575 zend_class_entry *date_ce_immutable, *date_ce_interface;
 576 
 577 
 578 PHPAPI zend_class_entry *php_date_get_date_ce(void)
 579 {
 580         return date_ce_date;
 581 }
 582 
 583 PHPAPI zend_class_entry *php_date_get_immutable_ce(void)
 584 {
 585         return date_ce_immutable;
 586 }
 587 
 588 PHPAPI zend_class_entry *php_date_get_timezone_ce(void)
 589 {
 590         return date_ce_timezone;
 591 }
 592 
 593 static zend_object_handlers date_object_handlers_date;
 594 static zend_object_handlers date_object_handlers_immutable;
 595 static zend_object_handlers date_object_handlers_timezone;
 596 static zend_object_handlers date_object_handlers_interval;
 597 static zend_object_handlers date_object_handlers_period;
 598 
 599 #define DATE_SET_CONTEXT \
 600         zval *object; \
 601         object = getThis(); \
 602 
 603 #define DATE_FETCH_OBJECT       \
 604         php_date_obj *obj;      \
 605         DATE_SET_CONTEXT; \
 606         if (object) {   \
 607                 if (zend_parse_parameters_none() == FAILURE) {  \
 608                         return; \
 609                 }       \
 610         } else {        \
 611                 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), NULL, "O", &object, date_ce_date) == FAILURE) {       \
 612                         RETURN_FALSE;   \
 613                 }       \
 614         }       \
 615         obj = Z_PHPDATE_P(object);      \
 616 
 617 #define DATE_CHECK_INITIALIZED(member, class_name) \
 618         if (!(member)) { \
 619                 php_error_docref(NULL, E_WARNING, "The " #class_name " object has not been correctly initialized by its constructor"); \
 620                 RETURN_FALSE; \
 621         }
 622 
 623 static void date_object_free_storage_date(zend_object *object);
 624 static void date_object_free_storage_timezone(zend_object *object);
 625 static void date_object_free_storage_interval(zend_object *object);
 626 static void date_object_free_storage_period(zend_object *object);
 627 
 628 static zend_object *date_object_new_date(zend_class_entry *class_type);
 629 static zend_object *date_object_new_timezone(zend_class_entry *class_type);
 630 static zend_object *date_object_new_interval(zend_class_entry *class_type);
 631 static zend_object *date_object_new_period(zend_class_entry *class_type);
 632 
 633 static zend_object *date_object_clone_date(zval *this_ptr);
 634 static zend_object *date_object_clone_timezone(zval *this_ptr);
 635 static zend_object *date_object_clone_interval(zval *this_ptr);
 636 static zend_object *date_object_clone_period(zval *this_ptr);
 637 
 638 static int date_object_compare_date(zval *d1, zval *d2);
 639 static HashTable *date_object_get_gc(zval *object, zval **table, int *n);
 640 static HashTable *date_object_get_properties(zval *object);
 641 static HashTable *date_object_get_gc_interval(zval *object, zval **table, int *n);
 642 static HashTable *date_object_get_properties_interval(zval *object);
 643 static HashTable *date_object_get_gc_period(zval *object, zval **table, int *n);
 644 static HashTable *date_object_get_properties_period(zval *object);
 645 static HashTable *date_object_get_properties_timezone(zval *object);
 646 static HashTable *date_object_get_gc_timezone(zval *object, zval **table, int *n);
 647 
 648 zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv);
 649 void date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot);
 650 static zval *date_period_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv);
 651 static void date_period_write_property(zval *object, zval *member, zval *value, void **cache_slot);
 652 
 653 /* {{{ Module struct */
 654 zend_module_entry date_module_entry = {
 655         STANDARD_MODULE_HEADER_EX,
 656         NULL,
 657         NULL,
 658         "date",                     /* extension name */
 659         date_functions,             /* function list */
 660         PHP_MINIT(date),            /* process startup */
 661         PHP_MSHUTDOWN(date),        /* process shutdown */
 662         PHP_RINIT(date),            /* request startup */
 663         PHP_RSHUTDOWN(date),        /* request shutdown */
 664         PHP_MINFO(date),            /* extension info */
 665         PHP_DATE_VERSION,                /* extension version */
 666         PHP_MODULE_GLOBALS(date),   /* globals descriptor */
 667         PHP_GINIT(date),            /* globals ctor */
 668         NULL,                       /* globals dtor */
 669         NULL,                       /* post deactivate */
 670         STANDARD_MODULE_PROPERTIES_EX
 671 };
 672 /* }}} */
 673 
 674 
 675 /* {{{ PHP_GINIT_FUNCTION */
 676 static PHP_GINIT_FUNCTION(date)
 677 {
 678         date_globals->default_timezone = NULL;
 679         date_globals->timezone = NULL;
 680         date_globals->tzcache = NULL;
 681         date_globals->timezone_valid = 0;
 682 }
 683 /* }}} */
 684 
 685 
 686 static void _php_date_tzinfo_dtor(zval *zv) /* {{{ */
 687 {
 688         timelib_tzinfo *tzi = (timelib_tzinfo*)Z_PTR_P(zv);
 689 
 690         timelib_tzinfo_dtor(tzi);
 691 } /* }}} */
 692 
 693 /* {{{ PHP_RINIT_FUNCTION */
 694 PHP_RINIT_FUNCTION(date)
 695 {
 696         if (DATEG(timezone)) {
 697                 efree(DATEG(timezone));
 698         }
 699         DATEG(timezone) = NULL;
 700         DATEG(tzcache) = NULL;
 701         DATEG(last_errors) = NULL;
 702 
 703         return SUCCESS;
 704 }
 705 /* }}} */
 706 
 707 /* {{{ PHP_RSHUTDOWN_FUNCTION */
 708 PHP_RSHUTDOWN_FUNCTION(date)
 709 {
 710         if (DATEG(timezone)) {
 711                 efree(DATEG(timezone));
 712         }
 713         DATEG(timezone) = NULL;
 714         if(DATEG(tzcache)) {
 715                 zend_hash_destroy(DATEG(tzcache));
 716                 FREE_HASHTABLE(DATEG(tzcache));
 717                 DATEG(tzcache) = NULL;
 718         }
 719         if (DATEG(last_errors)) {
 720                 timelib_error_container_dtor(DATEG(last_errors));
 721                 DATEG(last_errors) = NULL;
 722         }
 723 
 724         return SUCCESS;
 725 }
 726 /* }}} */
 727 
 728 #define DATE_TIMEZONEDB      php_date_global_timezone_db ? php_date_global_timezone_db : timelib_builtin_db()
 729 
 730 /*
 731  * RFC822, Section 5.1: http://www.ietf.org/rfc/rfc822.txt
 732  *  date-time   =  [ day "," ] date time        ; dd mm yy hh:mm:ss zzz
 733  *  day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"  /  "Fri"  / "Sat" /  "Sun"
 734  *  date        =  1*2DIGIT month 2DIGIT        ; day month year e.g. 20 Jun 82
 735  *  month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"  /  "May"  /  "Jun" /  "Jul"  /  "Aug"  /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
 736  *  time        =  hour zone                    ; ANSI and Military
 737  *  hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59
 738  *  zone        =  "UT"  / "GMT"  /  "EST" / "EDT"  /  "CST" / "CDT"  /  "MST" / "MDT"  /  "PST" / "PDT"  /  1ALPHA  / ( ("+" / "-") 4DIGIT )
 739  */
 740 #define DATE_FORMAT_RFC822   "D, d M y H:i:s O"
 741 
 742 /*
 743  * RFC850, Section 2.1.4: http://www.ietf.org/rfc/rfc850.txt
 744  *  Format must be acceptable both to the ARPANET and to the getdate routine.
 745  *  One format that is acceptable to both is Weekday, DD-Mon-YY HH:MM:SS TIMEZONE
 746  *  TIMEZONE can be any timezone name (3 or more letters)
 747  */
 748 #define DATE_FORMAT_RFC850   "l, d-M-y H:i:s T"
 749 
 750 /*
 751  * RFC1036, Section 2.1.2: http://www.ietf.org/rfc/rfc1036.txt
 752  *  Its format must be acceptable both in RFC-822 and to the getdate(3)
 753  *  Wdy, DD Mon YY HH:MM:SS TIMEZONE
 754  *  There is no hope of having a complete list of timezones.  Universal
 755  *  Time (GMT), the North American timezones (PST, PDT, MST, MDT, CST,
 756  *  CDT, EST, EDT) and the +/-hhmm offset specifed in RFC-822 should be supported.
 757  */
 758 #define DATE_FORMAT_RFC1036  "D, d M y H:i:s O"
 759 
 760 /*
 761  * RFC1123, Section 5.2.14: http://www.ietf.org/rfc/rfc1123.txt
 762  *  RFC-822 Date and Time Specification: RFC-822 Section 5
 763  *  The syntax for the date is hereby changed to: date = 1*2DIGIT month 2*4DIGIT
 764  */
 765 #define DATE_FORMAT_RFC1123  "D, d M Y H:i:s O"
 766 
 767 /*
 768  * RFC2822, Section 3.3: http://www.ietf.org/rfc/rfc2822.txt
 769  *  FWS             =       ([*WSP CRLF] 1*WSP) /   ; Folding white space
 770  *  CFWS            =       *([FWS] comment) (([FWS] comment) / FWS)
 771  *
 772  *  date-time       =       [ day-of-week "," ] date FWS time [CFWS]
 773  *  day-of-week     =       ([FWS] day-name)
 774  *  day-name        =       "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
 775  *  date            =       day month year
 776  *  year            =       4*DIGIT
 777  *  month           =       (FWS month-name FWS)
 778  *  month-name      =       "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
 779  *  day             =       ([FWS] 1*2DIGIT)
 780  *  time            =       time-of-day FWS zone
 781  *  time-of-day     =       hour ":" minute [ ":" second ]
 782  *  hour            =       2DIGIT
 783  *  minute          =       2DIGIT
 784  *  second          =       2DIGIT
 785  *  zone            =       (( "+" / "-" ) 4DIGIT)
 786  */
 787 #define DATE_FORMAT_RFC2822  "D, d M Y H:i:s O"
 788 /*
 789  * RFC3339, Section 5.6: http://www.ietf.org/rfc/rfc3339.txt
 790  *  date-fullyear   = 4DIGIT
 791  *  date-month      = 2DIGIT  ; 01-12
 792  *  date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year
 793  *
 794  *  time-hour       = 2DIGIT  ; 00-23
 795  *  time-minute     = 2DIGIT  ; 00-59
 796  *  time-second     = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second rules
 797  *
 798  *  time-secfrac    = "." 1*DIGIT
 799  *  time-numoffset  = ("+" / "-") time-hour ":" time-minute
 800  *  time-offset     = "Z" / time-numoffset
 801  *
 802  *  partial-time    = time-hour ":" time-minute ":" time-second [time-secfrac]
 803  *  full-date       = date-fullyear "-" date-month "-" date-mday
 804  *  full-time       = partial-time time-offset
 805  *
 806  *  date-time       = full-date "T" full-time
 807  */
 808 #define DATE_FORMAT_RFC3339  "Y-m-d\\TH:i:sP"
 809 
 810 #define DATE_FORMAT_ISO8601  "Y-m-d\\TH:i:sO"
 811 
 812 /*
 813  * RFC3339, Appendix A: http://www.ietf.org/rfc/rfc3339.txt
 814  *  ISO 8601 also requires (in section 5.3.1.3) that a decimal fraction
 815  *  be proceeded by a "0" if less than unity.  Annex B.2 of ISO 8601
 816  *  gives examples where the decimal fractions are not preceded by a "0".
 817  *  This grammar assumes section 5.3.1.3 is correct and that Annex B.2 is
 818  *  in error.
 819  */
 820 #define DATE_FORMAT_RFC3339_EXTENDED  "Y-m-d\\TH:i:s.vP"
 821 
 822 /*
 823  * This comes from various sources that like to contradict. I'm going with the
 824  * format here because of:
 825  * http://msdn.microsoft.com/en-us/library/windows/desktop/aa384321%28v=vs.85%29.aspx
 826  * and http://curl.haxx.se/rfc/cookie_spec.html
 827  */
 828 #define DATE_FORMAT_COOKIE   "l, d-M-Y H:i:s T"
 829 
 830 #define SUNFUNCS_RET_TIMESTAMP 0
 831 #define SUNFUNCS_RET_STRING    1
 832 #define SUNFUNCS_RET_DOUBLE    2
 833 
 834 /* {{{ PHP_MINIT_FUNCTION */
 835 PHP_MINIT_FUNCTION(date)
 836 {
 837         REGISTER_INI_ENTRIES();
 838         date_register_classes();
 839 /*
 840  * RFC4287, Section 3.3: http://www.ietf.org/rfc/rfc4287.txt
 841  *   A Date construct is an element whose content MUST conform to the
 842  *   "date-time" production in [RFC3339].  In addition, an uppercase "T"
 843  *   character MUST be used to separate date and time, and an uppercase
 844  *   "Z" character MUST be present in the absence of a numeric time zone offset.
 845  */
 846         REGISTER_STRING_CONSTANT("DATE_ATOM",    DATE_FORMAT_RFC3339, CONST_CS | CONST_PERSISTENT);
 847 /*
 848  * Preliminary specification: http://wp.netscape.com/newsref/std/cookie_spec.html
 849  *   "This is based on RFC 822, RFC 850,  RFC 1036, and  RFC 1123,
 850  *   with the variations that the only legal time zone is GMT
 851  *   and the separators between the elements of the date must be dashes."
 852  */
 853         REGISTER_STRING_CONSTANT("DATE_COOKIE",  DATE_FORMAT_COOKIE,  CONST_CS | CONST_PERSISTENT);
 854         REGISTER_STRING_CONSTANT("DATE_ISO8601", DATE_FORMAT_ISO8601, CONST_CS | CONST_PERSISTENT);
 855 
 856         REGISTER_STRING_CONSTANT("DATE_RFC822",  DATE_FORMAT_RFC822,  CONST_CS | CONST_PERSISTENT);
 857         REGISTER_STRING_CONSTANT("DATE_RFC850",  DATE_FORMAT_RFC850,  CONST_CS | CONST_PERSISTENT);
 858         REGISTER_STRING_CONSTANT("DATE_RFC1036", DATE_FORMAT_RFC1036, CONST_CS | CONST_PERSISTENT);
 859         REGISTER_STRING_CONSTANT("DATE_RFC1123", DATE_FORMAT_RFC1123, CONST_CS | CONST_PERSISTENT);
 860         REGISTER_STRING_CONSTANT("DATE_RFC2822", DATE_FORMAT_RFC2822, CONST_CS | CONST_PERSISTENT);
 861         REGISTER_STRING_CONSTANT("DATE_RFC3339", DATE_FORMAT_RFC3339, CONST_CS | CONST_PERSISTENT);
 862         REGISTER_STRING_CONSTANT("DATE_RFC3339_EXTENDED", DATE_FORMAT_RFC3339_EXTENDED, CONST_CS | CONST_PERSISTENT);
 863 
 864 /*
 865  * RSS 2.0 Specification: http://blogs.law.harvard.edu/tech/rss
 866  *   "All date-times in RSS conform to the Date and Time Specification of RFC 822,
 867  *   with the exception that the year may be expressed with two characters or four characters (four preferred)"
 868  */
 869         REGISTER_STRING_CONSTANT("DATE_RSS",     DATE_FORMAT_RFC1123, CONST_CS | CONST_PERSISTENT);
 870         REGISTER_STRING_CONSTANT("DATE_W3C",     DATE_FORMAT_RFC3339, CONST_CS | CONST_PERSISTENT);
 871 
 872         REGISTER_LONG_CONSTANT("SUNFUNCS_RET_TIMESTAMP", SUNFUNCS_RET_TIMESTAMP, CONST_CS | CONST_PERSISTENT);
 873         REGISTER_LONG_CONSTANT("SUNFUNCS_RET_STRING", SUNFUNCS_RET_STRING, CONST_CS | CONST_PERSISTENT);
 874         REGISTER_LONG_CONSTANT("SUNFUNCS_RET_DOUBLE", SUNFUNCS_RET_DOUBLE, CONST_CS | CONST_PERSISTENT);
 875 
 876         php_date_global_timezone_db = NULL;
 877         php_date_global_timezone_db_enabled = 0;
 878         DATEG(last_errors) = NULL;
 879         return SUCCESS;
 880 }
 881 /* }}} */
 882 
 883 /* {{{ PHP_MSHUTDOWN_FUNCTION */
 884 PHP_MSHUTDOWN_FUNCTION(date)
 885 {
 886         UNREGISTER_INI_ENTRIES();
 887 
 888         if (DATEG(last_errors)) {
 889                 timelib_error_container_dtor(DATEG(last_errors));
 890         }
 891 
 892 #ifndef ZTS
 893         DATEG(default_timezone) = NULL;
 894 #endif
 895 
 896         return SUCCESS;
 897 }
 898 /* }}} */
 899 
 900 /* {{{ PHP_MINFO_FUNCTION */
 901 PHP_MINFO_FUNCTION(date)
 902 {
 903         const timelib_tzdb *tzdb = DATE_TIMEZONEDB;
 904 
 905         php_info_print_table_start();
 906         php_info_print_table_row(2, "date/time support", "enabled");
 907         php_info_print_table_row(2, "\"Olson\" Timezone Database Version", tzdb->version);
 908         php_info_print_table_row(2, "Timezone Database", php_date_global_timezone_db_enabled ? "external" : "internal");
 909         php_info_print_table_row(2, "Default timezone", guess_timezone(tzdb));
 910         php_info_print_table_end();
 911 
 912         DISPLAY_INI_ENTRIES();
 913 }
 914 /* }}} */
 915 
 916 /* {{{ Timezone Cache functions */
 917 static timelib_tzinfo *php_date_parse_tzfile(char *formal_tzname, const timelib_tzdb *tzdb)
 918 {
 919         timelib_tzinfo *tzi;
 920 
 921         if(!DATEG(tzcache)) {
 922                 ALLOC_HASHTABLE(DATEG(tzcache));
 923                 zend_hash_init(DATEG(tzcache), 4, NULL, _php_date_tzinfo_dtor, 0);
 924         }
 925 
 926         if ((tzi = zend_hash_str_find_ptr(DATEG(tzcache), formal_tzname, strlen(formal_tzname))) != NULL) {
 927                 return tzi;
 928         }
 929 
 930         tzi = timelib_parse_tzfile(formal_tzname, tzdb);
 931         if (tzi) {
 932                 zend_hash_str_add_ptr(DATEG(tzcache), formal_tzname, strlen(formal_tzname), tzi);
 933         }
 934         return tzi;
 935 }
 936 
 937 timelib_tzinfo *php_date_parse_tzfile_wrapper(char *formal_tzname, const timelib_tzdb *tzdb)
 938 {
 939         return php_date_parse_tzfile(formal_tzname, tzdb);
 940 }
 941 /* }}} */
 942 
 943 /* Callback to check the date.timezone only when changed increases performance */
 944 /* {{{ static PHP_INI_MH(OnUpdate_date_timezone) */
 945 static PHP_INI_MH(OnUpdate_date_timezone)
 946 {
 947         if (OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
 948                 return FAILURE;
 949         }
 950 
 951         DATEG(timezone_valid) = 0;
 952         if (stage == PHP_INI_STAGE_RUNTIME) {
 953                 if (!timelib_timezone_id_is_valid(DATEG(default_timezone), DATE_TIMEZONEDB)) {
 954                         if (DATEG(default_timezone) && *DATEG(default_timezone)) {
 955                                 php_error_docref(NULL, E_WARNING, "Invalid date.timezone value '%s', we selected the timezone 'UTC' for now.", DATEG(default_timezone));
 956                         }
 957                 } else {
 958                         DATEG(timezone_valid) = 1;
 959                 }
 960         }
 961 
 962         return SUCCESS;
 963 }
 964 /* }}} */
 965 
 966 /* {{{ Helper functions */
 967 static char* guess_timezone(const timelib_tzdb *tzdb)
 968 {
 969         /* Checking configure timezone */
 970         if (DATEG(timezone) && (strlen(DATEG(timezone))) > 0) {
 971                 return DATEG(timezone);
 972         }
 973         /* Check config setting for default timezone */
 974         if (!DATEG(default_timezone)) {
 975                 /* Special case: ext/date wasn't initialized yet */
 976                 zval *ztz;
 977 
 978                 if (NULL != (ztz = cfg_get_entry("date.timezone", sizeof("date.timezone")))
 979                         && Z_TYPE_P(ztz) == IS_STRING && Z_STRLEN_P(ztz) > 0 && timelib_timezone_id_is_valid(Z_STRVAL_P(ztz), tzdb)) {
 980                         return Z_STRVAL_P(ztz);
 981                 }
 982         } else if (*DATEG(default_timezone)) {
 983                 if (DATEG(timezone_valid) == 1) {
 984                         return DATEG(default_timezone);
 985                 }
 986 
 987                 if (!timelib_timezone_id_is_valid(DATEG(default_timezone), tzdb)) {
 988                         php_error_docref(NULL, E_WARNING, "Invalid date.timezone value '%s', we selected the timezone 'UTC' for now.", DATEG(default_timezone));
 989                         return "UTC";
 990                 }
 991 
 992                 DATEG(timezone_valid) = 1;
 993                 return DATEG(default_timezone);
 994         }
 995         /* Fallback to UTC */
 996         return "UTC";
 997 }
 998 
 999 PHPAPI timelib_tzinfo *get_timezone_info(void)
1000 {
1001         char *tz;
1002         timelib_tzinfo *tzi;
1003 
1004         tz = guess_timezone(DATE_TIMEZONEDB);
1005         tzi = php_date_parse_tzfile(tz, DATE_TIMEZONEDB);
1006         if (! tzi) {
1007                 php_error_docref(NULL, E_ERROR, "Timezone database is corrupt - this should *never* happen!");
1008         }
1009         return tzi;
1010 }
1011 /* }}} */
1012 
1013 
1014 /* {{{ date() and gmdate() data */
1015 #include "zend_smart_str.h"
1016 
1017 static char *mon_full_names[] = {
1018         "January", "February", "March", "April",
1019         "May", "June", "July", "August",
1020         "September", "October", "November", "December"
1021 };
1022 
1023 static char *mon_short_names[] = {
1024         "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1025 };
1026 
1027 static char *day_full_names[] = {
1028         "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
1029 };
1030 
1031 static char *day_short_names[] = {
1032         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1033 };
1034 
1035 static char *english_suffix(timelib_sll number)
1036 {
1037         if (number >= 10 && number <= 19) {
1038                 return "th";
1039         } else {
1040                 switch (number % 10) {
1041                         case 1: return "st";
1042                         case 2: return "nd";
1043                         case 3: return "rd";
1044                 }
1045         }
1046         return "th";
1047 }
1048 /* }}} */
1049 
1050 /* {{{ day of week helpers */
1051 char *php_date_full_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
1052 {
1053         timelib_sll day_of_week = timelib_day_of_week(y, m, d);
1054         if (day_of_week < 0) {
1055                 return "Unknown";
1056         }
1057         return day_full_names[day_of_week];
1058 }
1059 
1060 char *php_date_short_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
1061 {
1062         timelib_sll day_of_week = timelib_day_of_week(y, m, d);
1063         if (day_of_week < 0) {
1064                 return "Unknown";
1065         }
1066         return day_short_names[day_of_week];
1067 }
1068 /* }}} */
1069 
1070 /* {{{ date_format - (gm)date helper */
1071 static zend_string *date_format(char *format, size_t format_len, timelib_time *t, int localtime)
1072 {
1073         smart_str            string = {0};
1074         int                  i, length = 0;
1075         char                 buffer[97];
1076         timelib_time_offset *offset = NULL;
1077         timelib_sll          isoweek, isoyear;
1078         int                  rfc_colon;
1079         int                  weekYearSet = 0;
1080 
1081         if (!format_len) {
1082                 return ZSTR_EMPTY_ALLOC();
1083         }
1084 
1085         if (localtime) {
1086                 if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
1087                         offset = timelib_time_offset_ctor();
1088                         offset->offset = (t->z - (t->dst * 60)) * -60;
1089                         offset->leap_secs = 0;
1090                         offset->is_dst = t->dst;
1091                         offset->abbr = timelib_strdup(t->tz_abbr);
1092                 } else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
1093                         offset = timelib_time_offset_ctor();
1094                         offset->offset = (t->z) * -60;
1095                         offset->leap_secs = 0;
1096                         offset->is_dst = 0;
1097                         offset->abbr = timelib_malloc(9); /* GMT±xxxx\0 */
1098                         snprintf(offset->abbr, 9, "GMT%c%02d%02d",
1099                                                   localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1100                                                   localtime ? abs(offset->offset / 3600) : 0,
1101                                                   localtime ? abs((offset->offset % 3600) / 60) : 0 );
1102                 } else {
1103                         offset = timelib_get_time_zone_info(t->sse, t->tz_info);
1104                 }
1105         }
1106 
1107         for (i = 0; i < format_len; i++) {
1108                 rfc_colon = 0;
1109                 switch (format[i]) {
1110                         /* day */
1111                         case 'd': length = slprintf(buffer, 32, "%02d", (int) t->d); break;
1112                         case 'D': length = slprintf(buffer, 32, "%s", php_date_short_day_name(t->y, t->m, t->d)); break;
1113                         case 'j': length = slprintf(buffer, 32, "%d", (int) t->d); break;
1114                         case 'l': length = slprintf(buffer, 32, "%s", php_date_full_day_name(t->y, t->m, t->d)); break;
1115                         case 'S': length = slprintf(buffer, 32, "%s", english_suffix(t->d)); break;
1116                         case 'w': length = slprintf(buffer, 32, "%d", (int) timelib_day_of_week(t->y, t->m, t->d)); break;
1117                         case 'N': length = slprintf(buffer, 32, "%d", (int) timelib_iso_day_of_week(t->y, t->m, t->d)); break;
1118                         case 'z': length = slprintf(buffer, 32, "%d", (int) timelib_day_of_year(t->y, t->m, t->d)); break;
1119 
1120                         /* week */
1121                         case 'W':
1122                                 if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
1123                                 length = slprintf(buffer, 32, "%02d", (int) isoweek); break; /* iso weeknr */
1124                         case 'o':
1125                                 if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
1126                                 length = slprintf(buffer, 32, "%d", (int) isoyear); break; /* iso year */
1127 
1128                         /* month */
1129                         case 'F': length = slprintf(buffer, 32, "%s", mon_full_names[t->m - 1]); break;
1130                         case 'm': length = slprintf(buffer, 32, "%02d", (int) t->m); break;
1131                         case 'M': length = slprintf(buffer, 32, "%s", mon_short_names[t->m - 1]); break;
1132                         case 'n': length = slprintf(buffer, 32, "%d", (int) t->m); break;
1133                         case 't': length = slprintf(buffer, 32, "%d", (int) timelib_days_in_month(t->y, t->m)); break;
1134 
1135                         /* year */
1136                         case 'L': length = slprintf(buffer, 32, "%d", timelib_is_leap((int) t->y)); break;
1137                         case 'y': length = slprintf(buffer, 32, "%02d", (int) t->y % 100); break;
1138                         case 'Y': length = slprintf(buffer, 32, "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break;
1139 
1140                         /* time */
1141                         case 'a': length = slprintf(buffer, 32, "%s", t->h >= 12 ? "pm" : "am"); break;
1142                         case 'A': length = slprintf(buffer, 32, "%s", t->h >= 12 ? "PM" : "AM"); break;
1143                         case 'B': {
1144                                 int retval = (((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10) / 864);
1145                                 while (retval < 0) {
1146                                         retval += 1000;
1147                                 }
1148                                 retval = retval % 1000;
1149                                 length = slprintf(buffer, 32, "%03d", retval);
1150                                 break;
1151                         }
1152                         case 'g': length = slprintf(buffer, 32, "%d", (t->h % 12) ? (int) t->h % 12 : 12); break;
1153                         case 'G': length = slprintf(buffer, 32, "%d", (int) t->h); break;
1154                         case 'h': length = slprintf(buffer, 32, "%02d", (t->h % 12) ? (int) t->h % 12 : 12); break;
1155                         case 'H': length = slprintf(buffer, 32, "%02d", (int) t->h); break;
1156                         case 'i': length = slprintf(buffer, 32, "%02d", (int) t->i); break;
1157                         case 's': length = slprintf(buffer, 32, "%02d", (int) t->s); break;
1158                         case 'u': length = slprintf(buffer, 32, "%06d", (int) floor(t->f * 1000000 + 0.5)); break;
1159                         case 'v': length = slprintf(buffer, 32, "%03d", (int) floor(t->f * 1000 + 0.5)); break;
1160 
1161                         /* timezone */
1162                         case 'I': length = slprintf(buffer, 32, "%d", localtime ? offset->is_dst : 0); break;
1163                         case 'P': rfc_colon = 1; /* break intentionally missing */
1164                         case 'O': length = slprintf(buffer, 32, "%c%02d%s%02d",
1165                                                                                         localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1166                                                                                         localtime ? abs(offset->offset / 3600) : 0,
1167                                                                                         rfc_colon ? ":" : "",
1168                                                                                         localtime ? abs((offset->offset % 3600) / 60) : 0
1169                                                           );
1170                                           break;
1171                         case 'T': length = slprintf(buffer, 32, "%s", localtime ? offset->abbr : "GMT"); break;
1172                         case 'e': if (!localtime) {
1173                                               length = slprintf(buffer, 32, "%s", "UTC");
1174                                           } else {
1175                                                   switch (t->zone_type) {
1176                                                           case TIMELIB_ZONETYPE_ID:
1177                                                                   length = slprintf(buffer, 32, "%s", t->tz_info->name);
1178                                                                   break;
1179                                                           case TIMELIB_ZONETYPE_ABBR:
1180                                                                   length = slprintf(buffer, 32, "%s", offset->abbr);
1181                                                                   break;
1182                                                           case TIMELIB_ZONETYPE_OFFSET:
1183                                                                   length = slprintf(buffer, 32, "%c%02d:%02d",
1184                                                                                                 ((offset->offset < 0) ? '-' : '+'),
1185                                                                                                 abs(offset->offset / 3600),
1186                                                                                                 abs((offset->offset % 3600) / 60)
1187                                                                                    );
1188                                                                   break;
1189                                                   }
1190                                           }
1191                                           break;
1192                         case 'Z': length = slprintf(buffer, 32, "%d", localtime ? offset->offset : 0); break;
1193 
1194                         /* full date/time */
1195                         case 'c': length = slprintf(buffer, 96, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
1196                                                                         (int) t->y, (int) t->m, (int) t->d,
1197                                                                                         (int) t->h, (int) t->i, (int) t->s,
1198                                                                                         localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1199                                                                                         localtime ? abs(offset->offset / 3600) : 0,
1200                                                                                         localtime ? abs((offset->offset % 3600) / 60) : 0
1201                                                           );
1202                                           break;
1203                         case 'r': length = slprintf(buffer, 96, "%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d",
1204                                                                         php_date_short_day_name(t->y, t->m, t->d),
1205                                                                                         (int) t->d, mon_short_names[t->m - 1],
1206                                                                                         (int) t->y, (int) t->h, (int) t->i, (int) t->s,
1207                                                                                         localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1208                                                                                         localtime ? abs(offset->offset / 3600) : 0,
1209                                                                                         localtime ? abs((offset->offset % 3600) / 60) : 0
1210                                                           );
1211                                           break;
1212                         case 'U': length = slprintf(buffer, 32, "%lld", (timelib_sll) t->sse); break;
1213 
1214                         case '\\': if (i < format_len) i++; /* break intentionally missing */
1215 
1216                         default: buffer[0] = format[i]; buffer[1] = '\0'; length = 1; break;
1217                 }
1218                 smart_str_appendl(&string, buffer, length);
1219         }
1220 
1221         smart_str_0(&string);
1222 
1223         if (localtime) {
1224                 timelib_time_offset_dtor(offset);
1225         }
1226 
1227         return string.s;
1228 }
1229 
1230 static void php_date(INTERNAL_FUNCTION_PARAMETERS, int localtime)
1231 {
1232         char   *format;
1233         size_t     format_len;
1234         zend_long    ts;
1235 
1236         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &format, &format_len, &ts) == FAILURE) {
1237                 RETURN_FALSE;
1238         }
1239         if (ZEND_NUM_ARGS() == 1) {
1240                 ts = time(NULL);
1241         }
1242 
1243         RETURN_STR(php_format_date(format, format_len, ts, localtime));
1244 }
1245 /* }}} */
1246 
1247 PHPAPI zend_string *php_format_date(char *format, size_t format_len, time_t ts, int localtime) /* {{{ */
1248 {
1249         timelib_time   *t;
1250         timelib_tzinfo *tzi;
1251         zend_string *string;
1252 
1253         t = timelib_time_ctor();
1254 
1255         if (localtime) {
1256                 tzi = get_timezone_info();
1257                 t->tz_info = tzi;
1258                 t->zone_type = TIMELIB_ZONETYPE_ID;
1259                 timelib_unixtime2local(t, ts);
1260         } else {
1261                 tzi = NULL;
1262                 timelib_unixtime2gmt(t, ts);
1263         }
1264 
1265         string = date_format(format, format_len, t, localtime);
1266 
1267         timelib_time_dtor(t);
1268         return string;
1269 }
1270 /* }}} */
1271 
1272 /* {{{ php_idate
1273  */
1274 PHPAPI int php_idate(char format, time_t ts, int localtime)
1275 {
1276         timelib_time   *t;
1277         timelib_tzinfo *tzi;
1278         int retval = -1;
1279         timelib_time_offset *offset = NULL;
1280         timelib_sll isoweek, isoyear;
1281 
1282         t = timelib_time_ctor();
1283 
1284         if (!localtime) {
1285                 tzi = get_timezone_info();
1286                 t->tz_info = tzi;
1287                 t->zone_type = TIMELIB_ZONETYPE_ID;
1288                 timelib_unixtime2local(t, ts);
1289         } else {
1290                 tzi = NULL;
1291                 timelib_unixtime2gmt(t, ts);
1292         }
1293 
1294         if (!localtime) {
1295                 if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
1296                         offset = timelib_time_offset_ctor();
1297                         offset->offset = (t->z - (t->dst * 60)) * -60;
1298                         offset->leap_secs = 0;
1299                         offset->is_dst = t->dst;
1300                         offset->abbr = timelib_strdup(t->tz_abbr);
1301                 } else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
1302                         offset = timelib_time_offset_ctor();
1303                         offset->offset = (t->z - (t->dst * 60)) * -60;
1304                         offset->leap_secs = 0;
1305                         offset->is_dst = t->dst;
1306                         offset->abbr = timelib_malloc(9); /* GMT±xxxx\0 */
1307                         snprintf(offset->abbr, 9, "GMT%c%02d%02d",
1308                                                   !localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
1309                                                   !localtime ? abs(offset->offset / 3600) : 0,
1310                                                   !localtime ? abs((offset->offset % 3600) / 60) : 0 );
1311                 } else {
1312                         offset = timelib_get_time_zone_info(t->sse, t->tz_info);
1313                 }
1314         }
1315 
1316         timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear);
1317 
1318         switch (format) {
1319                 /* day */
1320                 case 'd': case 'j': retval = (int) t->d; break;
1321 
1322                 case 'w': retval = (int) timelib_day_of_week(t->y, t->m, t->d); break;
1323                 case 'z': retval = (int) timelib_day_of_year(t->y, t->m, t->d); break;
1324 
1325                 /* week */
1326                 case 'W': retval = (int) isoweek; break; /* iso weeknr */
1327 
1328                 /* month */
1329                 case 'm': case 'n': retval = (int) t->m; break;
1330                 case 't': retval = (int) timelib_days_in_month(t->y, t->m); break;
1331 
1332                 /* year */
1333                 case 'L': retval = (int) timelib_is_leap((int) t->y); break;
1334                 case 'y': retval = (int) (t->y % 100); break;
1335                 case 'Y': retval = (int) t->y; break;
1336 
1337                 /* Swatch Beat a.k.a. Internet Time */
1338                 case 'B':
1339                         retval = (((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10) / 864);
1340                         while (retval < 0) {
1341                                 retval += 1000;
1342                         }
1343                         retval = retval % 1000;
1344                         break;
1345 
1346                 /* time */
1347                 case 'g': case 'h': retval = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
1348                 case 'H': case 'G': retval = (int) t->h; break;
1349                 case 'i': retval = (int) t->i; break;
1350                 case 's': retval = (int) t->s; break;
1351 
1352                 /* timezone */
1353                 case 'I': retval = (int) (!localtime ? offset->is_dst : 0); break;
1354                 case 'Z': retval = (int) (!localtime ? offset->offset : 0); break;
1355 
1356                 case 'U': retval = (int) t->sse; break;
1357         }
1358 
1359         if (!localtime) {
1360                 timelib_time_offset_dtor(offset);
1361         }
1362         timelib_time_dtor(t);
1363 
1364         return retval;
1365 }
1366 /* }}} */
1367 
1368 /* {{{ proto string date(string format [, long timestamp])
1369    Format a local date/time */
1370 PHP_FUNCTION(date)
1371 {
1372         php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1373 }
1374 /* }}} */
1375 
1376 /* {{{ proto string gmdate(string format [, long timestamp])
1377    Format a GMT date/time */
1378 PHP_FUNCTION(gmdate)
1379 {
1380         php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1381 }
1382 /* }}} */
1383 
1384 /* {{{ proto int idate(string format [, int timestamp])
1385    Format a local time/date as integer */
1386 PHP_FUNCTION(idate)
1387 {
1388         char   *format;
1389         size_t     format_len;
1390         zend_long    ts = 0;
1391         int ret;
1392 
1393         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &format, &format_len, &ts) == FAILURE) {
1394                 RETURN_FALSE;
1395         }
1396 
1397         if (format_len != 1) {
1398                 php_error_docref(NULL, E_WARNING, "idate format is one char");
1399                 RETURN_FALSE;
1400         }
1401 
1402         if (ZEND_NUM_ARGS() == 1) {
1403                 ts = time(NULL);
1404         }
1405 
1406         ret = php_idate(format[0], ts, 0);
1407         if (ret == -1) {
1408                 php_error_docref(NULL, E_WARNING, "Unrecognized date format token.");
1409                 RETURN_FALSE;
1410         }
1411         RETURN_LONG(ret);
1412 }
1413 /* }}} */
1414 
1415 /* {{{ php_date_set_tzdb - NOT THREADSAFE */
1416 PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb)
1417 {
1418         const timelib_tzdb *builtin = timelib_builtin_db();
1419 
1420         if (php_version_compare(tzdb->version, builtin->version) > 0) {
1421                 php_date_global_timezone_db = tzdb;
1422                 php_date_global_timezone_db_enabled = 1;
1423         }
1424 }
1425 /* }}} */
1426 
1427 /* {{{ php_parse_date: Backwards compatibility function */
1428 PHPAPI zend_long php_parse_date(char *string, zend_long *now)
1429 {
1430         timelib_time *parsed_time;
1431         timelib_error_container *error = NULL;
1432         int           error2;
1433         zend_long   retval;
1434 
1435         parsed_time = timelib_strtotime(string, strlen(string), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1436         if (error->error_count) {
1437                 timelib_time_dtor(parsed_time);
1438                 timelib_error_container_dtor(error);
1439                 return -1;
1440         }
1441         timelib_error_container_dtor(error);
1442         timelib_update_ts(parsed_time, NULL);
1443         retval = timelib_date_to_int(parsed_time, &error2);
1444         timelib_time_dtor(parsed_time);
1445         if (error2) {
1446                 return -1;
1447         }
1448         return retval;
1449 }
1450 /* }}} */
1451 
1452 /* {{{ proto int strtotime(string time [, int now ])
1453    Convert string representation of date and time to a timestamp */
1454 PHP_FUNCTION(strtotime)
1455 {
1456         char *times;
1457         size_t   time_len;
1458         int error1, error2;
1459         struct timelib_error_container *error;
1460         zend_long preset_ts = 0, ts;
1461         timelib_time *t, *now;
1462         timelib_tzinfo *tzi;
1463 
1464         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &times, &time_len, &preset_ts) == FAILURE || !time_len) {
1465                 RETURN_FALSE;
1466         }
1467 
1468         tzi = get_timezone_info();
1469 
1470         now = timelib_time_ctor();
1471         now->tz_info = tzi;
1472         now->zone_type = TIMELIB_ZONETYPE_ID;
1473         timelib_unixtime2local(now,
1474                 (ZEND_NUM_ARGS() == 2) ? (timelib_sll) preset_ts : (timelib_sll) time(NULL));
1475 
1476         t = timelib_strtotime(times, time_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1477         error1 = error->error_count;
1478         timelib_error_container_dtor(error);
1479         timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
1480         timelib_update_ts(t, tzi);
1481         ts = timelib_date_to_int(t, &error2);
1482 
1483         timelib_time_dtor(now);
1484         timelib_time_dtor(t);
1485 
1486         if (error1 || error2) {
1487                 RETURN_FALSE;
1488         } else {
1489                 RETURN_LONG(ts);
1490         }
1491 }
1492 /* }}} */
1493 
1494 /* {{{ php_mktime - (gm)mktime helper */
1495 PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
1496 {
1497         zend_long hou = 0, min = 0, sec = 0, mon = 0, day = 0, yea = 0;
1498         timelib_time *now;
1499         timelib_tzinfo *tzi = NULL;
1500         zend_long ts, adjust_seconds = 0;
1501         int error;
1502 
1503         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|llllll", &hou, &min, &sec, &mon, &day, &yea) == FAILURE) {
1504                 RETURN_FALSE;
1505         }
1506         /* Initialize structure with current time */
1507         now = timelib_time_ctor();
1508         if (gmt) {
1509                 timelib_unixtime2gmt(now, (timelib_sll) time(NULL));
1510         } else {
1511                 tzi = get_timezone_info();
1512                 now->tz_info = tzi;
1513                 now->zone_type = TIMELIB_ZONETYPE_ID;
1514                 timelib_unixtime2local(now, (timelib_sll) time(NULL));
1515         }
1516         /* Fill in the new data */
1517         switch (ZEND_NUM_ARGS()) {
1518                 case 7:
1519                         /* break intentionally missing */
1520                 case 6:
1521                         if (yea >= 0 && yea < 70) {
1522                                 yea += 2000;
1523                         } else if (yea >= 70 && yea <= 100) {
1524                                 yea += 1900;
1525                         }
1526                         now->y = yea;
1527                         /* break intentionally missing again */
1528                 case 5:
1529                         now->d = day;
1530                         /* break missing intentionally here too */
1531                 case 4:
1532                         now->m = mon;
1533                         /* and here */
1534                 case 3:
1535                         now->s = sec;
1536                         /* yup, this break isn't here on purpose too */
1537                 case 2:
1538                         now->i = min;
1539                         /* last intentionally missing break */
1540                 case 1:
1541                         now->h = hou;
1542                         break;
1543                 default:
1544                         php_error_docref(NULL, E_DEPRECATED, "You should be using the time() function instead");
1545         }
1546         /* Update the timestamp */
1547         if (gmt) {
1548                 timelib_update_ts(now, NULL);
1549         } else {
1550                 timelib_update_ts(now, tzi);
1551         }
1552 
1553         /* Clean up and return */
1554         ts = timelib_date_to_int(now, &error);
1555         ts += adjust_seconds;
1556         timelib_time_dtor(now);
1557 
1558         if (error) {
1559                 RETURN_FALSE;
1560         } else {
1561                 RETURN_LONG(ts);
1562         }
1563 }
1564 /* }}} */
1565 
1566 /* {{{ proto int mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])
1567    Get UNIX timestamp for a date */
1568 PHP_FUNCTION(mktime)
1569 {
1570         php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1571 }
1572 /* }}} */
1573 
1574 /* {{{ proto int gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]])
1575    Get UNIX timestamp for a GMT date */
1576 PHP_FUNCTION(gmmktime)
1577 {
1578         php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1579 }
1580 /* }}} */
1581 
1582 /* {{{ proto bool checkdate(int month, int day, int year)
1583    Returns true(1) if it is a valid date in gregorian calendar */
1584 PHP_FUNCTION(checkdate)
1585 {
1586         zend_long m, d, y;
1587 
1588         if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &m, &d, &y) == FAILURE) {
1589                 RETURN_FALSE;
1590         }
1591 
1592         if (y < 1 || y > 32767 || !timelib_valid_date(y, m, d)) {
1593                 RETURN_FALSE;
1594         }
1595         RETURN_TRUE;    /* True : This month, day, year arguments are valid */
1596 }
1597 /* }}} */
1598 
1599 #ifdef HAVE_STRFTIME
1600 /* {{{ php_strftime - (gm)strftime helper */
1601 PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
1602 {
1603         char                *format;
1604         size_t                  format_len;
1605         zend_long                 timestamp = 0;
1606         struct tm            ta;
1607         int                  max_reallocs = 5;
1608         size_t               buf_len = 256, real_len;
1609         timelib_time        *ts;
1610         timelib_tzinfo      *tzi;
1611         timelib_time_offset *offset = NULL;
1612         zend_string             *buf;
1613 
1614         timestamp = (zend_long) time(NULL);
1615 
1616         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &format, &format_len, &timestamp) == FAILURE) {
1617                 RETURN_FALSE;
1618         }
1619 
1620         if (format_len == 0) {
1621                 RETURN_FALSE;
1622         }
1623 
1624         ts = timelib_time_ctor();
1625         if (gmt) {
1626                 tzi = NULL;
1627                 timelib_unixtime2gmt(ts, (timelib_sll) timestamp);
1628         } else {
1629                 tzi = get_timezone_info();
1630                 ts->tz_info = tzi;
1631                 ts->zone_type = TIMELIB_ZONETYPE_ID;
1632                 timelib_unixtime2local(ts, (timelib_sll) timestamp);
1633         }
1634         ta.tm_sec   = ts->s;
1635         ta.tm_min   = ts->i;
1636         ta.tm_hour  = ts->h;
1637         ta.tm_mday  = ts->d;
1638         ta.tm_mon   = ts->m - 1;
1639         ta.tm_year  = ts->y - 1900;
1640         ta.tm_wday  = timelib_day_of_week(ts->y, ts->m, ts->d);
1641         ta.tm_yday  = timelib_day_of_year(ts->y, ts->m, ts->d);
1642         if (gmt) {
1643                 ta.tm_isdst = 0;
1644 #if HAVE_TM_GMTOFF
1645                 ta.tm_gmtoff = 0;
1646 #endif
1647 #if HAVE_TM_ZONE
1648                 ta.tm_zone = "GMT";
1649 #endif
1650         } else {
1651                 offset = timelib_get_time_zone_info(timestamp, tzi);
1652 
1653                 ta.tm_isdst = offset->is_dst;
1654 #if HAVE_TM_GMTOFF
1655                 ta.tm_gmtoff = offset->offset;
1656 #endif
1657 #if HAVE_TM_ZONE
1658                 ta.tm_zone = offset->abbr;
1659 #endif
1660         }
1661 
1662         /* VS2012 crt has a bug where strftime crash with %z and %Z format when the
1663            initial buffer is too small. See
1664            http://connect.microsoft.com/VisualStudio/feedback/details/759720/vs2012-strftime-crash-with-z-formatting-code */
1665         buf = zend_string_alloc(buf_len, 0);
1666         while ((real_len = strftime(ZSTR_VAL(buf), buf_len, format, &ta)) == buf_len || real_len == 0) {
1667                 buf_len *= 2;
1668                 buf = zend_string_extend(buf, buf_len, 0);
1669                 if (!--max_reallocs) {
1670                         break;
1671                 }
1672         }
1673 #ifdef PHP_WIN32
1674         /* VS2012 strftime() returns number of characters, not bytes.
1675                 See VC++11 bug id 766205. */
1676         if (real_len > 0) {
1677                 real_len = strlen(buf->val);
1678         }
1679 #endif
1680 
1681         timelib_time_dtor(ts);
1682         if (!gmt) {
1683                 timelib_time_offset_dtor(offset);
1684         }
1685 
1686         if (real_len && real_len != buf_len) {
1687                 buf = zend_string_truncate(buf, real_len, 0);
1688                 RETURN_NEW_STR(buf);
1689         }
1690         zend_string_free(buf);
1691         RETURN_FALSE;
1692 }
1693 /* }}} */
1694 
1695 /* {{{ proto string strftime(string format [, int timestamp])
1696    Format a local time/date according to locale settings */
1697 PHP_FUNCTION(strftime)
1698 {
1699         php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1700 }
1701 /* }}} */
1702 
1703 /* {{{ proto string gmstrftime(string format [, int timestamp])
1704    Format a GMT/UCT time/date according to locale settings */
1705 PHP_FUNCTION(gmstrftime)
1706 {
1707         php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1708 }
1709 /* }}} */
1710 #endif
1711 
1712 /* {{{ proto int time(void)
1713    Return current UNIX timestamp */
1714 PHP_FUNCTION(time)
1715 {
1716         RETURN_LONG((zend_long)time(NULL));
1717 }
1718 /* }}} */
1719 
1720 /* {{{ proto array localtime([int timestamp [, bool associative_array]])
1721    Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array */
1722 PHP_FUNCTION(localtime)
1723 {
1724         zend_long timestamp = (zend_long)time(NULL);
1725         zend_bool associative = 0;
1726         timelib_tzinfo *tzi;
1727         timelib_time   *ts;
1728 
1729         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lb", &timestamp, &associative) == FAILURE) {
1730                 RETURN_FALSE;
1731         }
1732 
1733         tzi = get_timezone_info();
1734         ts = timelib_time_ctor();
1735         ts->tz_info = tzi;
1736         ts->zone_type = TIMELIB_ZONETYPE_ID;
1737         timelib_unixtime2local(ts, (timelib_sll) timestamp);
1738 
1739         array_init(return_value);
1740 
1741         if (associative) {
1742                 add_assoc_long(return_value, "tm_sec",   ts->s);
1743                 add_assoc_long(return_value, "tm_min",   ts->i);
1744                 add_assoc_long(return_value, "tm_hour",  ts->h);
1745                 add_assoc_long(return_value, "tm_mday",  ts->d);
1746                 add_assoc_long(return_value, "tm_mon",   ts->m - 1);
1747                 add_assoc_long(return_value, "tm_year",  ts->y - 1900);
1748                 add_assoc_long(return_value, "tm_wday",  timelib_day_of_week(ts->y, ts->m, ts->d));
1749                 add_assoc_long(return_value, "tm_yday",  timelib_day_of_year(ts->y, ts->m, ts->d));
1750                 add_assoc_long(return_value, "tm_isdst", ts->dst);
1751         } else {
1752                 add_next_index_long(return_value, ts->s);
1753                 add_next_index_long(return_value, ts->i);
1754                 add_next_index_long(return_value, ts->h);
1755                 add_next_index_long(return_value, ts->d);
1756                 add_next_index_long(return_value, ts->m - 1);
1757                 add_next_index_long(return_value, ts->y- 1900);
1758                 add_next_index_long(return_value, timelib_day_of_week(ts->y, ts->m, ts->d));
1759                 add_next_index_long(return_value, timelib_day_of_year(ts->y, ts->m, ts->d));
1760                 add_next_index_long(return_value, ts->dst);
1761         }
1762 
1763         timelib_time_dtor(ts);
1764 }
1765 /* }}} */
1766 
1767 /* {{{ proto array getdate([int timestamp])
1768    Get date/time information */
1769 PHP_FUNCTION(getdate)
1770 {
1771         zend_long timestamp = (zend_long)time(NULL);
1772         timelib_tzinfo *tzi;
1773         timelib_time   *ts;
1774 
1775         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &timestamp) == FAILURE) {
1776                 RETURN_FALSE;
1777         }
1778 
1779         tzi = get_timezone_info();
1780         ts = timelib_time_ctor();
1781         ts->tz_info = tzi;
1782         ts->zone_type = TIMELIB_ZONETYPE_ID;
1783         timelib_unixtime2local(ts, (timelib_sll) timestamp);
1784 
1785         array_init(return_value);
1786 
1787         add_assoc_long(return_value, "seconds", ts->s);
1788         add_assoc_long(return_value, "minutes", ts->i);
1789         add_assoc_long(return_value, "hours", ts->h);
1790         add_assoc_long(return_value, "mday", ts->d);
1791         add_assoc_long(return_value, "wday", timelib_day_of_week(ts->y, ts->m, ts->d));
1792         add_assoc_long(return_value, "mon", ts->m);
1793         add_assoc_long(return_value, "year", ts->y);
1794         add_assoc_long(return_value, "yday", timelib_day_of_year(ts->y, ts->m, ts->d));
1795         add_assoc_string(return_value, "weekday", php_date_full_day_name(ts->y, ts->m, ts->d));
1796         add_assoc_string(return_value, "month", mon_full_names[ts->m - 1]);
1797         add_index_long(return_value, 0, timestamp);
1798 
1799         timelib_time_dtor(ts);
1800 }
1801 /* }}} */
1802 
1803 #define PHP_DATE_TIMEZONE_GROUP_AFRICA     0x0001
1804 #define PHP_DATE_TIMEZONE_GROUP_AMERICA    0x0002
1805 #define PHP_DATE_TIMEZONE_GROUP_ANTARCTICA 0x0004
1806 #define PHP_DATE_TIMEZONE_GROUP_ARCTIC     0x0008
1807 #define PHP_DATE_TIMEZONE_GROUP_ASIA       0x0010
1808 #define PHP_DATE_TIMEZONE_GROUP_ATLANTIC   0x0020
1809 #define PHP_DATE_TIMEZONE_GROUP_AUSTRALIA  0x0040
1810 #define PHP_DATE_TIMEZONE_GROUP_EUROPE     0x0080
1811 #define PHP_DATE_TIMEZONE_GROUP_INDIAN     0x0100
1812 #define PHP_DATE_TIMEZONE_GROUP_PACIFIC    0x0200
1813 #define PHP_DATE_TIMEZONE_GROUP_UTC        0x0400
1814 #define PHP_DATE_TIMEZONE_GROUP_ALL        0x07FF
1815 #define PHP_DATE_TIMEZONE_GROUP_ALL_W_BC   0x0FFF
1816 #define PHP_DATE_TIMEZONE_PER_COUNTRY      0x1000
1817 
1818 #define PHP_DATE_PERIOD_EXCLUDE_START_DATE 0x0001
1819 
1820 
1821 /* define an overloaded iterator structure */
1822 typedef struct {
1823         zend_object_iterator  intern;
1824         zval                  current;
1825         php_period_obj       *object;
1826         int                   current_index;
1827 } date_period_it;
1828 
1829 /* {{{ date_period_it_invalidate_current */
1830 static void date_period_it_invalidate_current(zend_object_iterator *iter)
1831 {
1832         date_period_it *iterator = (date_period_it *)iter;
1833 
1834         if (Z_TYPE(iterator->current) != IS_UNDEF) {
1835                 zval_ptr_dtor(&iterator->current);
1836                 ZVAL_UNDEF(&iterator->current);
1837         }
1838 }
1839 /* }}} */
1840 
1841 /* {{{ date_period_it_dtor */
1842 static void date_period_it_dtor(zend_object_iterator *iter)
1843 {
1844         date_period_it *iterator = (date_period_it *)iter;
1845 
1846         date_period_it_invalidate_current(iter);
1847 
1848         zval_ptr_dtor(&iterator->intern.data);
1849 }
1850 /* }}} */
1851 
1852 /* {{{ date_period_it_has_more */
1853 static int date_period_it_has_more(zend_object_iterator *iter)
1854 {
1855         date_period_it *iterator = (date_period_it *)iter;
1856         php_period_obj *object   = Z_PHPPERIOD_P(&iterator->intern.data);
1857         timelib_time   *it_time = object->current;
1858 
1859         /* apply modification if it's not the first iteration */
1860         if (!object->include_start_date || iterator->current_index > 0) {
1861                 it_time->have_relative = 1;
1862                 it_time->relative = *object->interval;
1863                 it_time->sse_uptodate = 0;
1864                 timelib_update_ts(it_time, NULL);
1865                 timelib_update_from_sse(it_time);
1866         }
1867 
1868         if (object->end) {
1869                 return object->current->sse < object->end->sse ? SUCCESS : FAILURE;
1870         } else {
1871                 return (iterator->current_index < object->recurrences) ? SUCCESS : FAILURE;
1872         }
1873 }
1874 /* }}} */
1875 
1876 /* {{{ date_period_it_current_data */
1877 static zval *date_period_it_current_data(zend_object_iterator *iter)
1878 {
1879         date_period_it *iterator = (date_period_it *)iter;
1880         php_period_obj *object   = Z_PHPPERIOD_P(&iterator->intern.data);
1881         timelib_time   *it_time = object->current;
1882         php_date_obj   *newdateobj;
1883 
1884         /* Create new object */
1885         php_date_instantiate(object->start_ce, &iterator->current);
1886         newdateobj = Z_PHPDATE_P(&iterator->current);
1887         newdateobj->time = timelib_time_ctor();
1888         *newdateobj->time = *it_time;
1889         if (it_time->tz_abbr) {
1890                 newdateobj->time->tz_abbr = timelib_strdup(it_time->tz_abbr);
1891         }
1892         if (it_time->tz_info) {
1893                 newdateobj->time->tz_info = it_time->tz_info;
1894         }
1895 
1896         return &iterator->current;
1897 }
1898 /* }}} */
1899 
1900 /* {{{ date_period_it_current_key */
1901 static void date_period_it_current_key(zend_object_iterator *iter, zval *key)
1902 {
1903         date_period_it *iterator = (date_period_it *)iter;
1904         ZVAL_LONG(key, iterator->current_index);
1905 }
1906 /* }}} */
1907 
1908 /* {{{ date_period_it_move_forward */
1909 static void date_period_it_move_forward(zend_object_iterator *iter)
1910 {
1911         date_period_it   *iterator = (date_period_it *)iter;
1912 
1913         iterator->current_index++;
1914         date_period_it_invalidate_current(iter);
1915 }
1916 /* }}} */
1917 
1918 /* {{{ date_period_it_rewind */
1919 static void date_period_it_rewind(zend_object_iterator *iter)
1920 {
1921         date_period_it *iterator = (date_period_it *)iter;
1922 
1923         iterator->current_index = 0;
1924         if (iterator->object->current) {
1925                 timelib_time_dtor(iterator->object->current);
1926         }
1927         iterator->object->current = timelib_time_clone(iterator->object->start);
1928         date_period_it_invalidate_current(iter);
1929 }
1930 /* }}} */
1931 
1932 /* iterator handler table */
1933 zend_object_iterator_funcs date_period_it_funcs = {
1934         date_period_it_dtor,
1935         date_period_it_has_more,
1936         date_period_it_current_data,
1937         date_period_it_current_key,
1938         date_period_it_move_forward,
1939         date_period_it_rewind,
1940         date_period_it_invalidate_current
1941 };
1942 
1943 zend_object_iterator *date_object_period_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1944 {
1945         date_period_it *iterator = emalloc(sizeof(date_period_it));
1946 
1947         if (by_ref) {
1948                 zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
1949         }
1950 
1951         zend_iterator_init((zend_object_iterator*)iterator);
1952 
1953         ZVAL_COPY(&iterator->intern.data, object);
1954         iterator->intern.funcs = &date_period_it_funcs;
1955         iterator->object = Z_PHPPERIOD_P(object);
1956         ZVAL_UNDEF(&iterator->current);
1957 
1958         return (zend_object_iterator*)iterator;
1959 } /* }}} */
1960 
1961 static int implement_date_interface_handler(zend_class_entry *interface, zend_class_entry *implementor) /* {{{ */
1962 {
1963         if (implementor->type == ZEND_USER_CLASS &&
1964                 !instanceof_function(implementor, date_ce_date) &&
1965                 !instanceof_function(implementor, date_ce_immutable)
1966         ) {
1967                 zend_error(E_ERROR, "DateTimeInterface can't be implemented by user classes");
1968         }
1969 
1970         return SUCCESS;
1971 } /* }}} */
1972 
1973 static void date_register_classes(void) /* {{{ */
1974 {
1975         zend_class_entry ce_date, ce_immutable, ce_timezone, ce_interval, ce_period, ce_interface;
1976 
1977         INIT_CLASS_ENTRY(ce_interface, "DateTimeInterface", date_funcs_interface);
1978         date_ce_interface = zend_register_internal_interface(&ce_interface);
1979         date_ce_interface->interface_gets_implemented = implement_date_interface_handler;
1980 
1981         INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date);
1982         ce_date.create_object = date_object_new_date;
1983         date_ce_date = zend_register_internal_class_ex(&ce_date, NULL);
1984         memcpy(&date_object_handlers_date, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1985         date_object_handlers_date.offset = XtOffsetOf(php_date_obj, std);
1986         date_object_handlers_date.free_obj = date_object_free_storage_date;
1987         date_object_handlers_date.clone_obj = date_object_clone_date;
1988         date_object_handlers_date.compare_objects = date_object_compare_date;
1989         date_object_handlers_date.get_properties = date_object_get_properties;
1990         date_object_handlers_date.get_gc = date_object_get_gc;
1991         zend_class_implements(date_ce_date, 1, date_ce_interface);
1992 
1993 #define REGISTER_DATE_CLASS_CONST_STRING(const_name, value) \
1994         zend_declare_class_constant_stringl(date_ce_date, const_name, sizeof(const_name)-1, value, sizeof(value)-1);
1995 
1996         REGISTER_DATE_CLASS_CONST_STRING("ATOM",             DATE_FORMAT_RFC3339);
1997         REGISTER_DATE_CLASS_CONST_STRING("COOKIE",           DATE_FORMAT_COOKIE);
1998         REGISTER_DATE_CLASS_CONST_STRING("ISO8601",          DATE_FORMAT_ISO8601);
1999         REGISTER_DATE_CLASS_CONST_STRING("RFC822",           DATE_FORMAT_RFC822);
2000         REGISTER_DATE_CLASS_CONST_STRING("RFC850",           DATE_FORMAT_RFC850);
2001         REGISTER_DATE_CLASS_CONST_STRING("RFC1036",          DATE_FORMAT_RFC1036);
2002         REGISTER_DATE_CLASS_CONST_STRING("RFC1123",          DATE_FORMAT_RFC1123);
2003         REGISTER_DATE_CLASS_CONST_STRING("RFC2822",          DATE_FORMAT_RFC2822);
2004         REGISTER_DATE_CLASS_CONST_STRING("RFC3339",          DATE_FORMAT_RFC3339);
2005         REGISTER_DATE_CLASS_CONST_STRING("RFC3339_EXTENDED", DATE_FORMAT_RFC3339_EXTENDED);
2006         REGISTER_DATE_CLASS_CONST_STRING("RSS",              DATE_FORMAT_RFC1123);
2007         REGISTER_DATE_CLASS_CONST_STRING("W3C",              DATE_FORMAT_RFC3339);
2008 
2009         INIT_CLASS_ENTRY(ce_immutable, "DateTimeImmutable", date_funcs_immutable);
2010         ce_immutable.create_object = date_object_new_date;
2011         date_ce_immutable = zend_register_internal_class_ex(&ce_immutable, NULL);
2012         memcpy(&date_object_handlers_immutable, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2013         date_object_handlers_immutable.clone_obj = date_object_clone_date;
2014         date_object_handlers_immutable.compare_objects = date_object_compare_date;
2015         date_object_handlers_immutable.get_properties = date_object_get_properties;
2016         zend_class_implements(date_ce_immutable, 1, date_ce_interface);
2017 
2018         INIT_CLASS_ENTRY(ce_timezone, "DateTimeZone", date_funcs_timezone);
2019         ce_timezone.create_object = date_object_new_timezone;
2020         date_ce_timezone = zend_register_internal_class_ex(&ce_timezone, NULL);
2021         memcpy(&date_object_handlers_timezone, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2022         date_object_handlers_timezone.offset = XtOffsetOf(php_timezone_obj, std);
2023         date_object_handlers_timezone.free_obj = date_object_free_storage_timezone;
2024         date_object_handlers_timezone.clone_obj = date_object_clone_timezone;
2025         date_object_handlers_timezone.get_properties = date_object_get_properties_timezone;
2026         date_object_handlers_timezone.get_gc = date_object_get_gc_timezone;
2027 
2028 #define REGISTER_TIMEZONE_CLASS_CONST_STRING(const_name, value) \
2029         zend_declare_class_constant_long(date_ce_timezone, const_name, sizeof(const_name)-1, value);
2030 
2031         REGISTER_TIMEZONE_CLASS_CONST_STRING("AFRICA",      PHP_DATE_TIMEZONE_GROUP_AFRICA);
2032         REGISTER_TIMEZONE_CLASS_CONST_STRING("AMERICA",     PHP_DATE_TIMEZONE_GROUP_AMERICA);
2033         REGISTER_TIMEZONE_CLASS_CONST_STRING("ANTARCTICA",  PHP_DATE_TIMEZONE_GROUP_ANTARCTICA);
2034         REGISTER_TIMEZONE_CLASS_CONST_STRING("ARCTIC",      PHP_DATE_TIMEZONE_GROUP_ARCTIC);
2035         REGISTER_TIMEZONE_CLASS_CONST_STRING("ASIA",        PHP_DATE_TIMEZONE_GROUP_ASIA);
2036         REGISTER_TIMEZONE_CLASS_CONST_STRING("ATLANTIC",    PHP_DATE_TIMEZONE_GROUP_ATLANTIC);
2037         REGISTER_TIMEZONE_CLASS_CONST_STRING("AUSTRALIA",   PHP_DATE_TIMEZONE_GROUP_AUSTRALIA);
2038         REGISTER_TIMEZONE_CLASS_CONST_STRING("EUROPE",      PHP_DATE_TIMEZONE_GROUP_EUROPE);
2039         REGISTER_TIMEZONE_CLASS_CONST_STRING("INDIAN",      PHP_DATE_TIMEZONE_GROUP_INDIAN);
2040         REGISTER_TIMEZONE_CLASS_CONST_STRING("PACIFIC",     PHP_DATE_TIMEZONE_GROUP_PACIFIC);
2041         REGISTER_TIMEZONE_CLASS_CONST_STRING("UTC",         PHP_DATE_TIMEZONE_GROUP_UTC);
2042         REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL",         PHP_DATE_TIMEZONE_GROUP_ALL);
2043         REGISTER_TIMEZONE_CLASS_CONST_STRING("ALL_WITH_BC", PHP_DATE_TIMEZONE_GROUP_ALL_W_BC);
2044         REGISTER_TIMEZONE_CLASS_CONST_STRING("PER_COUNTRY", PHP_DATE_TIMEZONE_PER_COUNTRY);
2045 
2046         INIT_CLASS_ENTRY(ce_interval, "DateInterval", date_funcs_interval);
2047         ce_interval.create_object = date_object_new_interval;
2048         date_ce_interval = zend_register_internal_class_ex(&ce_interval, NULL);
2049         memcpy(&date_object_handlers_interval, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2050         date_object_handlers_interval.offset = XtOffsetOf(php_interval_obj, std);
2051         date_object_handlers_interval.free_obj = date_object_free_storage_interval;
2052         date_object_handlers_interval.clone_obj = date_object_clone_interval;
2053         date_object_handlers_interval.read_property = date_interval_read_property;
2054         date_object_handlers_interval.write_property = date_interval_write_property;
2055         date_object_handlers_interval.get_properties = date_object_get_properties_interval;
2056         date_object_handlers_interval.get_property_ptr_ptr = NULL;
2057         date_object_handlers_interval.get_gc = date_object_get_gc_interval;
2058 
2059         INIT_CLASS_ENTRY(ce_period, "DatePeriod", date_funcs_period);
2060         ce_period.create_object = date_object_new_period;
2061         date_ce_period = zend_register_internal_class_ex(&ce_period, NULL);
2062         date_ce_period->get_iterator = date_object_period_get_iterator;
2063         date_ce_period->iterator_funcs.funcs = &date_period_it_funcs;
2064         zend_class_implements(date_ce_period, 1, zend_ce_traversable);
2065         memcpy(&date_object_handlers_period, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
2066         date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
2067         date_object_handlers_period.free_obj = date_object_free_storage_period;
2068         date_object_handlers_period.clone_obj = date_object_clone_period;
2069         date_object_handlers_period.get_properties = date_object_get_properties_period;
2070         date_object_handlers_period.get_property_ptr_ptr = NULL;
2071         date_object_handlers_period.get_gc = date_object_get_gc_period;
2072         date_object_handlers_period.read_property = date_period_read_property;
2073         date_object_handlers_period.write_property = date_period_write_property;
2074 
2075 #define REGISTER_PERIOD_CLASS_CONST_STRING(const_name, value) \
2076         zend_declare_class_constant_long(date_ce_period, const_name, sizeof(const_name)-1, value);
2077 
2078         REGISTER_PERIOD_CLASS_CONST_STRING("EXCLUDE_START_DATE", PHP_DATE_PERIOD_EXCLUDE_START_DATE);
2079 } /* }}} */
2080 
2081 static inline zend_object *date_object_new_date_ex(zend_class_entry *class_type, int init_props) /* {{{ */
2082 {
2083         php_date_obj *intern;
2084 
2085         intern = ecalloc(1, sizeof(php_date_obj) + zend_object_properties_size(class_type));
2086 
2087         zend_object_std_init(&intern->std, class_type);
2088         if (init_props) {
2089                 object_properties_init(&intern->std, class_type);
2090         }
2091         intern->std.handlers = &date_object_handlers_date;
2092 
2093         return &intern->std;
2094 } /* }}} */
2095 
2096 static zend_object *date_object_new_date(zend_class_entry *class_type) /* {{{ */
2097 {
2098         return date_object_new_date_ex(class_type, 1);
2099 } /* }}} */
2100 
2101 static zend_object *date_object_clone_date(zval *this_ptr) /* {{{ */
2102 {
2103         php_date_obj *old_obj = Z_PHPDATE_P(this_ptr);
2104         php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date_ex(old_obj->std.ce, 0));
2105 
2106         zend_objects_clone_members(&new_obj->std, &old_obj->std);
2107         if (!old_obj->time) {
2108                 return &new_obj->std;
2109         }
2110 
2111         /* this should probably moved to a new `timelib_time *timelime_time_clone(timelib_time *)` */
2112         new_obj->time = timelib_time_ctor();
2113         *new_obj->time = *old_obj->time;
2114         if (old_obj->time->tz_abbr) {
2115                 new_obj->time->tz_abbr = timelib_strdup(old_obj->time->tz_abbr);
2116         }
2117         if (old_obj->time->tz_info) {
2118                 new_obj->time->tz_info = old_obj->time->tz_info;
2119         }
2120 
2121         return &new_obj->std;
2122 } /* }}} */
2123 
2124 static void date_clone_immutable(zval *object, zval *new_object) /* {{{ */
2125 {
2126         ZVAL_OBJ(new_object, date_object_clone_date(object));
2127 } /* }}} */
2128 
2129 static int date_object_compare_date(zval *d1, zval *d2) /* {{{ */
2130 {
2131         php_date_obj *o1 = Z_PHPDATE_P(d1);
2132         php_date_obj *o2 = Z_PHPDATE_P(d2);
2133 
2134         if (!o1->time || !o2->time) {
2135                 php_error_docref(NULL, E_WARNING, "Trying to compare an incomplete DateTime or DateTimeImmutable object");
2136                 return 1;
2137         }
2138         if (!o1->time->sse_uptodate) {
2139                 timelib_update_ts(o1->time, o1->time->tz_info);
2140         }
2141         if (!o2->time->sse_uptodate) {
2142                 timelib_update_ts(o2->time, o2->time->tz_info);
2143         }
2144 
2145         return timelib_time_compare(o1->time, o2->time);
2146 } /* }}} */
2147 
2148 static HashTable *date_object_get_gc(zval *object, zval **table, int *n) /* {{{ */
2149 {
2150         *table = NULL;
2151         *n = 0;
2152         return zend_std_get_properties(object);
2153 } /* }}} */
2154 
2155 static HashTable *date_object_get_gc_timezone(zval *object, zval **table, int *n) /* {{{ */
2156 {
2157        *table = NULL;
2158        *n = 0;
2159        return zend_std_get_properties(object);
2160 } /* }}} */
2161 
2162 static HashTable *date_object_get_properties(zval *object) /* {{{ */
2163 {
2164         HashTable *props;
2165         zval zv;
2166         php_date_obj     *dateobj;
2167 
2168 
2169         dateobj = Z_PHPDATE_P(object);
2170 
2171         props = zend_std_get_properties(object);
2172 
2173         if (!dateobj->time || GC_G(gc_active)) {
2174                 return props;
2175         }
2176 
2177         /* first we add the date and time in ISO format */
2178         ZVAL_STR(&zv, date_format("Y-m-d H:i:s.u", sizeof("Y-m-d H:i:s.u")-1, dateobj->time, 1));
2179         zend_hash_str_update(props, "date", sizeof("date")-1, &zv);
2180 
2181         /* then we add the timezone name (or similar) */
2182         if (dateobj->time->is_localtime) {
2183                 ZVAL_LONG(&zv, dateobj->time->zone_type);
2184                 zend_hash_str_update(props, "timezone_type", sizeof("timezone_type")-1, &zv);
2185 
2186                 switch (dateobj->time->zone_type) {
2187                         case TIMELIB_ZONETYPE_ID:
2188                                 ZVAL_STRING(&zv, dateobj->time->tz_info->name);
2189                                 break;
2190                         case TIMELIB_ZONETYPE_OFFSET: {
2191                                 zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
2192                                 timelib_sll utc_offset = dateobj->time->z;
2193 
2194                                 ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
2195                                         utc_offset > 0 ? '-' : '+',
2196                                         abs(utc_offset / 60),
2197                                         abs((utc_offset % 60)));
2198 
2199                                 ZVAL_NEW_STR(&zv, tmpstr);
2200                                 }
2201                                 break;
2202                         case TIMELIB_ZONETYPE_ABBR:
2203                                 ZVAL_STRING(&zv, dateobj->time->tz_abbr);
2204                                 break;
2205                 }
2206                 zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
2207         }
2208 
2209         return props;
2210 } /* }}} */
2211 
2212 static inline zend_object *date_object_new_timezone_ex(zend_class_entry *class_type, int init_props) /* {{{ */
2213 {
2214         php_timezone_obj *intern;
2215 
2216         intern = ecalloc(1, sizeof(php_timezone_obj) + zend_object_properties_size(class_type));
2217 
2218         zend_object_std_init(&intern->std, class_type);
2219         if (init_props) {
2220                 object_properties_init(&intern->std, class_type);
2221         }
2222         intern->std.handlers = &date_object_handlers_timezone;
2223 
2224         return &intern->std;
2225 } /* }}} */
2226 
2227 static zend_object *date_object_new_timezone(zend_class_entry *class_type) /* {{{ */
2228 {
2229         return date_object_new_timezone_ex(class_type, 1);
2230 } /* }}} */
2231 
2232 static zend_object *date_object_clone_timezone(zval *this_ptr) /* {{{ */
2233 {
2234         php_timezone_obj *old_obj = Z_PHPTIMEZONE_P(this_ptr);
2235         php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone_ex(old_obj->std.ce, 0));
2236 
2237         zend_objects_clone_members(&new_obj->std, &old_obj->std);
2238         if (!old_obj->initialized) {
2239                 return &new_obj->std;
2240         }
2241 
2242         new_obj->type = old_obj->type;
2243         new_obj->initialized = 1;
2244         switch (new_obj->type) {
2245                 case TIMELIB_ZONETYPE_ID:
2246                         new_obj->tzi.tz = old_obj->tzi.tz;
2247                         break;
2248                 case TIMELIB_ZONETYPE_OFFSET:
2249                         new_obj->tzi.utc_offset = old_obj->tzi.utc_offset;
2250                         break;
2251                 case TIMELIB_ZONETYPE_ABBR:
2252                         new_obj->tzi.z.utc_offset = old_obj->tzi.z.utc_offset;
2253                         new_obj->tzi.z.dst        = old_obj->tzi.z.dst;
2254                         new_obj->tzi.z.abbr       = timelib_strdup(old_obj->tzi.z.abbr);
2255                         break;
2256         }
2257 
2258         return &new_obj->std;
2259 } /* }}} */
2260 
2261 static HashTable *date_object_get_properties_timezone(zval *object) /* {{{ */
2262 {
2263         HashTable *props;
2264         zval zv;
2265         php_timezone_obj     *tzobj;
2266 
2267 
2268         tzobj = Z_PHPTIMEZONE_P(object);
2269 
2270         props = zend_std_get_properties(object);
2271 
2272         if (!tzobj->initialized) {
2273                 return props;
2274         }
2275 
2276         ZVAL_LONG(&zv, tzobj->type);
2277         zend_hash_str_update(props, "timezone_type", sizeof("timezone_type")-1, &zv);
2278 
2279         switch (tzobj->type) {
2280                 case TIMELIB_ZONETYPE_ID:
2281                         ZVAL_STRING(&zv, tzobj->tzi.tz->name);
2282                         break;
2283                 case TIMELIB_ZONETYPE_OFFSET: {
2284                         zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
2285 
2286                         ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
2287                         tzobj->tzi.utc_offset > 0 ? '-' : '+',
2288                         abs(tzobj->tzi.utc_offset / 60),
2289                         abs((tzobj->tzi.utc_offset % 60)));
2290 
2291                         ZVAL_NEW_STR(&zv, tmpstr);
2292                         }
2293                         break;
2294                 case TIMELIB_ZONETYPE_ABBR:
2295                         ZVAL_STRING(&zv, tzobj->tzi.z.abbr);
2296                         break;
2297         }
2298         zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
2299 
2300         return props;
2301 } /* }}} */
2302 
2303 static inline zend_object *date_object_new_interval_ex(zend_class_entry *class_type, int init_props) /* {{{ */
2304 {
2305         php_interval_obj *intern;
2306 
2307         intern = ecalloc(1, sizeof(php_interval_obj) + zend_object_properties_size(class_type));
2308 
2309         zend_object_std_init(&intern->std, class_type);
2310         if (init_props) {
2311                 object_properties_init(&intern->std, class_type);
2312         }
2313         intern->std.handlers = &date_object_handlers_interval;
2314 
2315         return &intern->std;
2316 } /* }}} */
2317 
2318 static zend_object *date_object_new_interval(zend_class_entry *class_type) /* {{{ */
2319 {
2320         return date_object_new_interval_ex(class_type, 1);
2321 } /* }}} */
2322 
2323 static zend_object *date_object_clone_interval(zval *this_ptr) /* {{{ */
2324 {
2325         php_interval_obj *old_obj = Z_PHPINTERVAL_P(this_ptr);
2326         php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval_ex(old_obj->std.ce, 0));
2327 
2328         zend_objects_clone_members(&new_obj->std, &old_obj->std);
2329 
2330         /** FIX ME ADD CLONE STUFF **/
2331         return &new_obj->std;
2332 } /* }}} */
2333 
2334 static HashTable *date_object_get_gc_interval(zval *object, zval **table, int *n) /* {{{ */
2335 {
2336 
2337         *table = NULL;
2338         *n = 0;
2339         return zend_std_get_properties(object);
2340 } /* }}} */
2341 
2342 static HashTable *date_object_get_properties_interval(zval *object) /* {{{ */
2343 {
2344         HashTable *props;
2345         zval zv;
2346         php_interval_obj     *intervalobj;
2347 
2348         intervalobj = Z_PHPINTERVAL_P(object);
2349 
2350         props = zend_std_get_properties(object);
2351 
2352         if (!intervalobj->initialized) {
2353                 return props;
2354         }
2355 
2356 #define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \
2357         ZVAL_LONG(&zv, (zend_long)intervalobj->diff->f); \
2358         zend_hash_str_update(props, n, sizeof(n)-1, &zv);
2359 
2360         PHP_DATE_INTERVAL_ADD_PROPERTY("y", y);
2361         PHP_DATE_INTERVAL_ADD_PROPERTY("m", m);
2362         PHP_DATE_INTERVAL_ADD_PROPERTY("d", d);
2363         PHP_DATE_INTERVAL_ADD_PROPERTY("h", h);
2364         PHP_DATE_INTERVAL_ADD_PROPERTY("i", i);
2365         PHP_DATE_INTERVAL_ADD_PROPERTY("s", s);
2366         PHP_DATE_INTERVAL_ADD_PROPERTY("weekday", weekday);
2367         PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior);
2368         PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of);
2369         PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert);
2370         if (intervalobj->diff->days != -99999) {
2371                 PHP_DATE_INTERVAL_ADD_PROPERTY("days", days);
2372         } else {
2373                 ZVAL_FALSE(&zv);
2374                 zend_hash_str_update(props, "days", sizeof("days")-1, &zv);
2375         }
2376         PHP_DATE_INTERVAL_ADD_PROPERTY("special_type", special.type);
2377         PHP_DATE_INTERVAL_ADD_PROPERTY("special_amount", special.amount);
2378         PHP_DATE_INTERVAL_ADD_PROPERTY("have_weekday_relative", have_weekday_relative);
2379         PHP_DATE_INTERVAL_ADD_PROPERTY("have_special_relative", have_special_relative);
2380 
2381         return props;
2382 } /* }}} */
2383 
2384 static inline zend_object *date_object_new_period_ex(zend_class_entry *class_type, int init_props) /* {{{ */
2385 {
2386         php_period_obj *intern;
2387 
2388         intern = ecalloc(1, sizeof(php_period_obj) + zend_object_properties_size(class_type));
2389 
2390         zend_object_std_init(&intern->std, class_type);
2391         if (init_props) {
2392                 object_properties_init(&intern->std, class_type);
2393         }
2394 
2395         intern->std.handlers = &date_object_handlers_period;
2396 
2397         return &intern->std;
2398 } /* }}} */
2399 
2400 static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ */
2401 {
2402         return date_object_new_period_ex(class_type, 1);
2403 } /* }}} */
2404 
2405 static zend_object *date_object_clone_period(zval *this_ptr) /* {{{ */
2406 {
2407         php_period_obj *old_obj = Z_PHPPERIOD_P(this_ptr);
2408         php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period_ex(old_obj->std.ce, 0));
2409 
2410         zend_objects_clone_members(&new_obj->std, &old_obj->std);
2411 
2412         /** FIX ME ADD CLONE STUFF **/
2413         return &new_obj->std;
2414 } /* }}} */
2415 
2416 static void date_object_free_storage_date(zend_object *object) /* {{{ */
2417 {
2418         php_date_obj *intern = php_date_obj_from_obj(object);
2419 
2420         if (intern->time) {
2421                 timelib_time_dtor(intern->time);
2422         }
2423 
2424         zend_object_std_dtor(&intern->std);
2425 } /* }}} */
2426 
2427 static void date_object_free_storage_timezone(zend_object *object) /* {{{ */
2428 {
2429         php_timezone_obj *intern = php_timezone_obj_from_obj(object);
2430 
2431         if (intern->type == TIMELIB_ZONETYPE_ABBR) {
2432                 timelib_free(intern->tzi.z.abbr);
2433         }
2434         zend_object_std_dtor(&intern->std);
2435 } /* }}} */
2436 
2437 static void date_object_free_storage_interval(zend_object *object) /* {{{ */
2438 {
2439         php_interval_obj *intern = php_interval_obj_from_obj(object);
2440 
2441         timelib_rel_time_dtor(intern->diff);
2442         zend_object_std_dtor(&intern->std);
2443 } /* }}} */
2444 
2445 static void date_object_free_storage_period(zend_object *object) /* {{{ */
2446 {
2447         php_period_obj *intern = php_period_obj_from_obj(object);
2448 
2449         if (intern->start) {
2450                 timelib_time_dtor(intern->start);
2451         }
2452 
2453         if (intern->current) {
2454                 timelib_time_dtor(intern->current);
2455         }
2456 
2457         if (intern->end) {
2458                 timelib_time_dtor(intern->end);
2459         }
2460 
2461         timelib_rel_time_dtor(intern->interval);
2462         zend_object_std_dtor(&intern->std);
2463 } /* }}} */
2464 
2465 /* Advanced Interface */
2466 PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object) /* {{{ */
2467 {
2468         object_init_ex(object, pce);
2469         return object;
2470 } /* }}} */
2471 
2472 /* Helper function used to store the latest found warnings and errors while
2473  * parsing, from either strtotime or parse_from_format. */
2474 static void update_errors_warnings(timelib_error_container *last_errors) /* {{{ */
2475 {
2476         if (DATEG(last_errors)) {
2477                 timelib_error_container_dtor(DATEG(last_errors));
2478                 DATEG(last_errors) = NULL;
2479         }
2480         DATEG(last_errors) = last_errors;
2481 } /* }}} */
2482 
2483 PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, size_t time_str_len, char *format, zval *timezone_object, int ctor) /* {{{ */
2484 {
2485         timelib_time   *now;
2486         timelib_tzinfo *tzi = NULL;
2487         timelib_error_container *err = NULL;
2488         int type = TIMELIB_ZONETYPE_ID, new_dst = 0;
2489         char *new_abbr = NULL;
2490         timelib_sll new_offset = 0;
2491 
2492         if (dateobj->time) {
2493                 timelib_time_dtor(dateobj->time);
2494         }
2495         if (format) {
2496                 dateobj->time = timelib_parse_from_format(format, time_str_len ? time_str : "", time_str_len ? time_str_len : 0, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2497         } else {
2498                 dateobj->time = timelib_strtotime(time_str_len ? time_str : "now", time_str_len ? time_str_len : sizeof("now") -1, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2499         }
2500 
2501         /* update last errors and warnings */
2502         update_errors_warnings(err);
2503 
2504 
2505         if (ctor && err && err->error_count) {
2506                 /* spit out the first library error message, at least */
2507                 php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", time_str,
2508                         err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
2509         }
2510         if (err && err->error_count) {
2511                 timelib_time_dtor(dateobj->time);
2512                 dateobj->time = 0;
2513                 return 0;
2514         }
2515 
2516         if (timezone_object) {
2517                 php_timezone_obj *tzobj;
2518 
2519                 tzobj = Z_PHPTIMEZONE_P(timezone_object);
2520                 switch (tzobj->type) {
2521                         case TIMELIB_ZONETYPE_ID:
2522                                 tzi = tzobj->tzi.tz;
2523                                 break;
2524                         case TIMELIB_ZONETYPE_OFFSET:
2525                                 new_offset = tzobj->tzi.utc_offset;
2526                                 break;
2527                         case TIMELIB_ZONETYPE_ABBR:
2528                                 new_offset = tzobj->tzi.z.utc_offset;
2529                                 new_dst    = tzobj->tzi.z.dst;
2530                                 new_abbr   = timelib_strdup(tzobj->tzi.z.abbr);
2531                                 break;
2532                 }
2533                 type = tzobj->type;
2534         } else if (dateobj->time->tz_info) {
2535                 tzi = dateobj->time->tz_info;
2536         } else {
2537                 tzi = get_timezone_info();
2538         }
2539 
2540         now = timelib_time_ctor();
2541         now->zone_type = type;
2542         switch (type) {
2543                 case TIMELIB_ZONETYPE_ID:
2544                         now->tz_info = tzi;
2545                         break;
2546                 case TIMELIB_ZONETYPE_OFFSET:
2547                         now->z = new_offset;
2548                         break;
2549                 case TIMELIB_ZONETYPE_ABBR:
2550                         now->z = new_offset;
2551                         now->dst = new_dst;
2552                         now->tz_abbr = new_abbr;
2553                         break;
2554         }
2555         timelib_unixtime2local(now, (timelib_sll) time(NULL));
2556 
2557         timelib_fill_holes(dateobj->time, now, TIMELIB_NO_CLONE);
2558         timelib_update_ts(dateobj->time, tzi);
2559         timelib_update_from_sse(dateobj->time);
2560 
2561         dateobj->time->have_relative = 0;
2562 
2563         timelib_time_dtor(now);
2564 
2565         return 1;
2566 } /* }}} */
2567 
2568 /* {{{ proto DateTime date_create([string time[, DateTimeZone object]])
2569    Returns new DateTime object
2570 */
2571 PHP_FUNCTION(date_create)
2572 {
2573         zval           *timezone_object = NULL;
2574         char           *time_str = NULL;
2575         size_t          time_str_len = 0;
2576 
2577         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2578                 RETURN_FALSE;
2579         }
2580 
2581         php_date_instantiate(date_ce_date, return_value);
2582         if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) {
2583                 zval_ptr_dtor(return_value);
2584                 RETURN_FALSE;
2585         }
2586 }
2587 /* }}} */
2588 
2589 /* {{{ proto DateTime date_create_immutable([string time[, DateTimeZone object]])
2590    Returns new DateTime object
2591 */
2592 PHP_FUNCTION(date_create_immutable)
2593 {
2594         zval           *timezone_object = NULL;
2595         char           *time_str = NULL;
2596         size_t          time_str_len = 0;
2597 
2598         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2599                 RETURN_FALSE;
2600         }
2601 
2602         php_date_instantiate(date_ce_immutable, return_value);
2603         if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) {
2604                 zval_ptr_dtor(return_value);
2605                 RETURN_FALSE;
2606         }
2607 }
2608 /* }}} */
2609 
2610 /* {{{ proto DateTime date_create_from_format(string format, string time[, DateTimeZone object])
2611    Returns new DateTime object formatted according to the specified format
2612 */
2613 PHP_FUNCTION(date_create_from_format)
2614 {
2615         zval           *timezone_object = NULL;
2616         char           *time_str = NULL, *format_str = NULL;
2617         size_t             time_str_len = 0, format_str_len = 0;
2618 
2619         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2620                 RETURN_FALSE;
2621         }
2622 
2623         php_date_instantiate(date_ce_date, return_value);
2624         if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, 0)) {
2625                 zval_ptr_dtor(return_value);
2626                 RETURN_FALSE;
2627         }
2628 }
2629 /* }}} */
2630 
2631 /* {{{ proto DateTime date_create_immutable_from_format(string format, string time[, DateTimeZone object])
2632    Returns new DateTime object formatted according to the specified format
2633 */
2634 PHP_FUNCTION(date_create_immutable_from_format)
2635 {
2636         zval           *timezone_object = NULL;
2637         char           *time_str = NULL, *format_str = NULL;
2638         size_t          time_str_len = 0, format_str_len = 0;
2639 
2640         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
2641                 RETURN_FALSE;
2642         }
2643 
2644         php_date_instantiate(date_ce_immutable, return_value);
2645         if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, 0)) {
2646                 zval_ptr_dtor(return_value);
2647                 RETURN_FALSE;
2648         }
2649 }
2650 /* }}} */
2651 
2652 /* {{{ proto DateTime::__construct([string time[, DateTimeZone object]])
2653    Creates new DateTime object
2654 */
2655 PHP_METHOD(DateTime, __construct)
2656 {
2657         zval *timezone_object = NULL;
2658         char *time_str = NULL;
2659         size_t time_str_len = 0;
2660         zend_error_handling error_handling;
2661 
2662         if (FAILURE == zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
2663                 return;
2664         }
2665 
2666         zend_replace_error_handling(EH_THROW, NULL, &error_handling);
2667         php_date_initialize(Z_PHPDATE_P(getThis()), time_str, time_str_len, NULL, timezone_object, 1);
2668         zend_restore_error_handling(&error_handling);
2669 }
2670 /* }}} */
2671 
2672 /* {{{ proto DateTimeImmutable::__construct([string time[, DateTimeZone object]])
2673    Creates new DateTimeImmutable object
2674 */
2675 PHP_METHOD(DateTimeImmutable, __construct)
2676 {
2677         zval *timezone_object = NULL;
2678         char *time_str = NULL;
2679         size_t time_str_len = 0;
2680         zend_error_handling error_handling;
2681 
2682         if (FAILURE == zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
2683                 return;
2684         }
2685 
2686         zend_replace_error_handling(EH_THROW, NULL, &error_handling);
2687         php_date_initialize(Z_PHPDATE_P(getThis()), time_str, time_str_len, NULL, timezone_object, 1);
2688         zend_restore_error_handling(&error_handling);
2689 }
2690 /* }}} */
2691 
2692 /* {{{ proto DateTimeImmutable::createFromMutable(DateTimeZone object)
2693    Creates new DateTimeImmutable object from an existing mutable DateTime object.
2694 */
2695 PHP_METHOD(DateTimeImmutable, createFromMutable)
2696 {
2697         zval *datetime_object = NULL;
2698         php_date_obj *new_obj = NULL;
2699         php_date_obj *old_obj = NULL;
2700 
2701         if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &datetime_object, date_ce_date) == FAILURE) {
2702                 return;
2703         }
2704 
2705         php_date_instantiate(date_ce_immutable, return_value);
2706         old_obj = Z_PHPDATE_P(datetime_object);
2707         new_obj = Z_PHPDATE_P(return_value);
2708 
2709         new_obj->time = timelib_time_ctor();
2710         *new_obj->time = *old_obj->time;
2711         if (old_obj->time->tz_abbr) {
2712                 new_obj->time->tz_abbr = timelib_strdup(old_obj->time->tz_abbr);
2713         }
2714         if (old_obj->time->tz_info) {
2715                 new_obj->time->tz_info = old_obj->time->tz_info;
2716         }
2717 }
2718 /* }}} */
2719 
2720 static int php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht)
2721 {
2722         zval             *z_date;
2723         zval             *z_timezone;
2724         zval             *z_timezone_type;
2725         zval              tmp_obj;
2726         timelib_tzinfo   *tzi;
2727         php_timezone_obj *tzobj;
2728 
2729         z_date = zend_hash_str_find(myht, "date", sizeof("data")-1);
2730         if (z_date && Z_TYPE_P(z_date) == IS_STRING) {
2731                 z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type")-1);
2732                 if (z_timezone_type && Z_TYPE_P(z_timezone_type) == IS_LONG) {
2733                         z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone")-1);
2734                         if (z_timezone && Z_TYPE_P(z_timezone) == IS_STRING) {
2735                                 switch (Z_LVAL_P(z_timezone_type)) {
2736                                         case TIMELIB_ZONETYPE_OFFSET:
2737                                         case TIMELIB_ZONETYPE_ABBR: {
2738                                                 char *tmp = emalloc(Z_STRLEN_P(z_date) + Z_STRLEN_P(z_timezone) + 2);
2739                                                 int ret;
2740                                                 snprintf(tmp, Z_STRLEN_P(z_date) + Z_STRLEN_P(z_timezone) + 2, "%s %s", Z_STRVAL_P(z_date), Z_STRVAL_P(z_timezone));
2741                                                 ret = php_date_initialize(*dateobj, tmp, Z_STRLEN_P(z_date) + Z_STRLEN_P(z_timezone) + 1, NULL, NULL, 0);
2742                                                 efree(tmp);
2743                                                 return 1 == ret;
2744                                         }
2745 
2746                                         case TIMELIB_ZONETYPE_ID: {
2747                                                 int ret;
2748 
2749                                                 tzi = php_date_parse_tzfile(Z_STRVAL_P(z_timezone), DATE_TIMEZONEDB);
2750 
2751                                                 if (tzi == NULL) {
2752                                                         return 0;
2753                                                 }
2754 
2755                                                 tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, &tmp_obj));
2756                                                 tzobj->type = TIMELIB_ZONETYPE_ID;
2757                                                 tzobj->tzi.tz = tzi;
2758                                                 tzobj->initialized = 1;
2759 
2760                                                 ret = php_date_initialize(*dateobj, Z_STRVAL_P(z_date), Z_STRLEN_P(z_date), NULL, &tmp_obj, 0);
2761                                                 zval_ptr_dtor(&tmp_obj);
2762                                                 return 1 == ret;
2763                                         }
2764                                 }
2765                         }
2766                 }
2767         }
2768         return 0;
2769 } /* }}} */
2770 
2771 /* {{{ proto DateTime::__set_state()
2772 */
2773 PHP_METHOD(DateTime, __set_state)
2774 {
2775         php_date_obj     *dateobj;
2776         zval             *array;
2777         HashTable        *myht;
2778 
2779         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
2780                 RETURN_FALSE;
2781         }
2782 
2783         myht = Z_ARRVAL_P(array);
2784 
2785         php_date_instantiate(date_ce_date, return_value);
2786         dateobj = Z_PHPDATE_P(return_value);
2787         if (!php_date_initialize_from_hash(&dateobj, myht)) {
2788                 php_error(E_ERROR, "Invalid serialization data for DateTime object");
2789         }
2790 }
2791 /* }}} */
2792 
2793 /* {{{ proto DateTimeImmutable::__set_state()
2794 */
2795 PHP_METHOD(DateTimeImmutable, __set_state)
2796 {
2797         php_date_obj     *dateobj;
2798         zval             *array;
2799         HashTable        *myht;
2800 
2801         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
2802                 RETURN_FALSE;
2803         }
2804 
2805         myht = Z_ARRVAL_P(array);
2806 
2807         php_date_instantiate(date_ce_immutable, return_value);
2808         dateobj = Z_PHPDATE_P(return_value);
2809         if (!php_date_initialize_from_hash(&dateobj, myht)) {
2810                 php_error(E_ERROR, "Invalid serialization data for DateTimeImmutable object");
2811         }
2812 }
2813 /* }}} */
2814 
2815 /* {{{ proto DateTime::__wakeup()
2816 */
2817 PHP_METHOD(DateTime, __wakeup)
2818 {
2819         zval             *object = getThis();
2820         php_date_obj     *dateobj;
2821         HashTable        *myht;
2822 
2823         dateobj = Z_PHPDATE_P(object);
2824 
2825         myht = Z_OBJPROP_P(object);
2826 
2827         if (!php_date_initialize_from_hash(&dateobj, myht)) {
2828                 php_error(E_ERROR, "Invalid serialization data for DateTime object");
2829         }
2830 }
2831 /* }}} */
2832 
2833 /* Helper function used to add an associative array of warnings and errors to a zval */
2834 static void zval_from_error_container(zval *z, timelib_error_container *error) /* {{{ */
2835 {
2836         int   i;
2837         zval element;
2838 
2839         add_assoc_long(z, "warning_count", error->warning_count);
2840         array_init(&element);
2841         for (i = 0; i < error->warning_count; i++) {
2842                 add_index_string(&element, error->warning_messages[i].position, error->warning_messages[i].message);
2843         }
2844         add_assoc_zval(z, "warnings", &element);
2845 
2846         add_assoc_long(z, "error_count", error->error_count);
2847         array_init(&element);
2848         for (i = 0; i < error->error_count; i++) {
2849                 add_index_string(&element, error->error_messages[i].position, error->error_messages[i].message);
2850         }
2851         add_assoc_zval(z, "errors", &element);
2852 } /* }}} */
2853 
2854 /* {{{ proto array date_get_last_errors()
2855    Returns the warnings and errors found while parsing a date/time string.
2856 */
2857 PHP_FUNCTION(date_get_last_errors)
2858 {
2859         if (DATEG(last_errors)) {
2860                 array_init(return_value);
2861                 zval_from_error_container(return_value, DATEG(last_errors));
2862         } else {
2863                 RETURN_FALSE;
2864         }
2865 }
2866 /* }}} */
2867 
2868 void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time *parsed_time, struct timelib_error_container *error) /* {{{ */
2869 {
2870         zval element;
2871 
2872         array_init(return_value);
2873 #define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
2874         if (parsed_time->elem == -99999) {               \
2875                 add_assoc_bool(return_value, #name, 0); \
2876         } else {                                       \
2877                 add_assoc_long(return_value, #name, parsed_time->elem); \
2878         }
2879         PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(year,      y);
2880         PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(month,     m);
2881         PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(day,       d);
2882         PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(hour,      h);
2883         PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(minute,    i);
2884         PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(second,    s);
2885 
2886         if (parsed_time->f == -99999) {
2887                 add_assoc_bool(return_value, "fraction", 0);
2888         } else {
2889                 add_assoc_double(return_value, "fraction", parsed_time->f);
2890         }
2891 
2892         zval_from_error_container(return_value, error);
2893 
2894         timelib_error_container_dtor(error);
2895 
2896         add_assoc_bool(return_value, "is_localtime", parsed_time->is_localtime);
2897 
2898         if (parsed_time->is_localtime) {
2899                 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone_type, zone_type);
2900                 switch (parsed_time->zone_type) {
2901                         case TIMELIB_ZONETYPE_OFFSET:
2902                                 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2903                                 add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2904                                 break;
2905                         case TIMELIB_ZONETYPE_ID:
2906                                 if (parsed_time->tz_abbr) {
2907                                         add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr);
2908                                 }
2909                                 if (parsed_time->tz_info) {
2910                                         add_assoc_string(return_value, "tz_id", parsed_time->tz_info->name);
2911                                 }
2912                                 break;
2913                         case TIMELIB_ZONETYPE_ABBR:
2914                                 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
2915                                 add_assoc_bool(return_value, "is_dst", parsed_time->dst);
2916                                 add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr);
2917                                 break;
2918                 }
2919         }
2920         if (parsed_time->have_relative) {
2921                 array_init(&element);
2922                 add_assoc_long(&element, "year",   parsed_time->relative.y);
2923                 add_assoc_long(&element, "month",  parsed_time->relative.m);
2924                 add_assoc_long(&element, "day",    parsed_time->relative.d);
2925                 add_assoc_long(&element, "hour",   parsed_time->relative.h);
2926                 add_assoc_long(&element, "minute", parsed_time->relative.i);
2927                 add_assoc_long(&element, "second", parsed_time->relative.s);
2928                 if (parsed_time->relative.have_weekday_relative) {
2929                         add_assoc_long(&element, "weekday", parsed_time->relative.weekday);
2930                 }
2931                 if (parsed_time->relative.have_special_relative && (parsed_time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY)) {
2932                         add_assoc_long(&element, "weekdays", parsed_time->relative.special.amount);
2933                 }
2934                 if (parsed_time->relative.first_last_day_of) {
2935                         add_assoc_bool(&element, parsed_time->relative.first_last_day_of == TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH ? "first_day_of_month" : "last_day_of_month", 1);
2936                 }
2937                 add_assoc_zval(return_value, "relative", &element);
2938         }
2939         timelib_time_dtor(parsed_time);
2940 } /* }}} */
2941 
2942 /* {{{ proto array date_parse(string date)
2943    Returns associative array with detailed info about given date
2944 */
2945 PHP_FUNCTION(date_parse)
2946 {
2947         char                           *date;
2948         size_t                          date_len;
2949         struct timelib_error_container *error;
2950         timelib_time                   *parsed_time;
2951 
2952         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &date, &date_len) == FAILURE) {
2953                 RETURN_FALSE;
2954         }
2955 
2956         parsed_time = timelib_strtotime(date, date_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2957         php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
2958 }
2959 /* }}} */
2960 
2961 /* {{{ proto array date_parse_from_format(string format, string date)
2962    Returns associative array with detailed info about given date
2963 */
2964 PHP_FUNCTION(date_parse_from_format)
2965 {
2966         char                           *date, *format;
2967         size_t                          date_len, format_len;
2968         struct timelib_error_container *error;
2969         timelib_time                   *parsed_time;
2970 
2971         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &format, &format_len, &date, &date_len) == FAILURE) {
2972                 RETURN_FALSE;
2973         }
2974 
2975         parsed_time = timelib_parse_from_format(format, date, date_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2976         php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
2977 }
2978 /* }}} */
2979 
2980 /* {{{ proto string date_format(DateTimeInterface object, string format)
2981    Returns date formatted according to given format
2982 */
2983 PHP_FUNCTION(date_format)
2984 {
2985         zval         *object;
2986         php_date_obj *dateobj;
2987         char         *format;
2988         size_t       format_len;
2989 
2990         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interface, &format, &format_len) == FAILURE) {
2991                 RETURN_FALSE;
2992         }
2993         dateobj = Z_PHPDATE_P(object);
2994         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
2995         RETURN_STR(date_format(format, format_len, dateobj->time, dateobj->time->is_localtime));
2996 }
2997 /* }}} */
2998 
2999 static int php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{ */
3000 {
3001         php_date_obj *dateobj;
3002         timelib_time *tmp_time;
3003         timelib_error_container *err = NULL;
3004 
3005         dateobj = Z_PHPDATE_P(object);
3006 
3007         if (!(dateobj->time)) {
3008                 php_error_docref(NULL, E_WARNING, "The DateTime object has not been correctly initialized by its constructor");
3009                 return 0;
3010         }
3011 
3012         tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3013 
3014         /* update last errors and warnings */
3015         update_errors_warnings(err);
3016         if (err && err->error_count) {
3017                 /* spit out the first library error message, at least */
3018                 php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
3019                         err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
3020                 timelib_time_dtor(tmp_time);
3021                 return 0;
3022         }
3023 
3024         memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(struct timelib_rel_time));
3025         dateobj->time->have_relative = tmp_time->have_relative;
3026         dateobj->time->sse_uptodate = 0;
3027 
3028         if (tmp_time->y != -99999) {
3029                 dateobj->time->y = tmp_time->y;
3030         }
3031         if (tmp_time->m != -99999) {
3032                 dateobj->time->m = tmp_time->m;
3033         }
3034         if (tmp_time->d != -99999) {
3035                 dateobj->time->d = tmp_time->d;
3036         }
3037 
3038         if (tmp_time->h != -99999) {
3039                 dateobj->time->h = tmp_time->h;
3040                 if (tmp_time->i != -99999) {
3041                         dateobj->time->i = tmp_time->i;
3042                         if (tmp_time->s != -99999) {
3043                                 dateobj->time->s = tmp_time->s;
3044                         } else {
3045                                 dateobj->time->s = 0;
3046                         }
3047                 } else {
3048                         dateobj->time->i = 0;
3049                         dateobj->time->s = 0;
3050                 }
3051         }
3052         timelib_time_dtor(tmp_time);
3053 
3054         timelib_update_ts(dateobj->time, NULL);
3055         timelib_update_from_sse(dateobj->time);
3056         dateobj->time->have_relative = 0;
3057         memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3058 
3059         return 1;
3060 } /* }}} */
3061 
3062 /* {{{ proto DateTime date_modify(DateTime object, string modify)
3063    Alters the timestamp.
3064 */
3065 PHP_FUNCTION(date_modify)
3066 {
3067         zval         *object;
3068         char         *modify;
3069         size_t        modify_len;
3070 
3071         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) {
3072                 RETURN_FALSE;
3073         }
3074 
3075         if (!php_date_modify(object, modify, modify_len)) {
3076                 RETURN_FALSE;
3077         }
3078 
3079         Z_ADDREF_P(object);
3080         ZVAL_COPY_VALUE(return_value, object);
3081 }
3082 /* }}} */
3083 
3084 /* {{{ proto DateTimeImmutable::modify()
3085 */
3086 PHP_METHOD(DateTimeImmutable, modify)
3087 {
3088         zval *object, new_object;
3089         char *modify;
3090         size_t   modify_len;
3091 
3092         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_immutable, &modify, &modify_len) == FAILURE) {
3093                 RETURN_FALSE;
3094         }
3095 
3096         date_clone_immutable(object, &new_object);
3097         if (!php_date_modify(&new_object, modify, modify_len)) {
3098                 RETURN_FALSE;
3099         }
3100 
3101         ZVAL_COPY_VALUE(return_value, &new_object);
3102 }
3103 /* }}} */
3104 
3105 static void php_date_add(zval *object, zval *interval, zval *return_value) /* {{{ */
3106 {
3107         php_date_obj     *dateobj;
3108         php_interval_obj *intobj;
3109         timelib_time     *new_time;
3110 
3111         dateobj = Z_PHPDATE_P(object);
3112         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3113         intobj = Z_PHPINTERVAL_P(interval);
3114         DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3115 
3116         new_time = timelib_add(dateobj->time, intobj->diff);
3117         timelib_time_dtor(dateobj->time);
3118         dateobj->time = new_time;
3119 } /* }}} */
3120 
3121 /* {{{ proto DateTime date_add(DateTime object, DateInterval interval)
3122    Adds an interval to the current date in object.
3123 */
3124 PHP_FUNCTION(date_add)
3125 {
3126         zval *object, *interval;
3127 
3128         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3129                 RETURN_FALSE;
3130         }
3131 
3132         php_date_add(object, interval, return_value);
3133 
3134         Z_ADDREF_P(object);
3135         ZVAL_COPY_VALUE(return_value, object);
3136 }
3137 /* }}} */
3138 
3139 /* {{{ proto DateTimeImmutable::add()
3140 */
3141 PHP_METHOD(DateTimeImmutable, add)
3142 {
3143         zval *object, *interval, new_object;
3144 
3145         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_immutable, &interval, date_ce_interval) == FAILURE) {
3146                 RETURN_FALSE;
3147         }
3148 
3149         date_clone_immutable(object, &new_object);
3150         php_date_add(&new_object, interval, return_value);
3151 
3152         ZVAL_COPY_VALUE(return_value, &new_object);
3153 }
3154 /* }}} */
3155 
3156 static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{{ */
3157 {
3158         php_date_obj     *dateobj;
3159         php_interval_obj *intobj;
3160         timelib_time     *new_time;
3161 
3162         dateobj = Z_PHPDATE_P(object);
3163         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3164         intobj = Z_PHPINTERVAL_P(interval);
3165         DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3166 
3167         if (intobj->diff->have_special_relative) {
3168                 php_error_docref(NULL, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
3169                 return;
3170         }
3171 
3172         new_time = timelib_sub(dateobj->time, intobj->diff);
3173         timelib_time_dtor(dateobj->time);
3174         dateobj->time = new_time;
3175 } /* }}} */
3176 
3177 /* {{{ proto DateTime date_sub(DateTime object, DateInterval interval)
3178    Subtracts an interval to the current date in object.
3179 */
3180 PHP_FUNCTION(date_sub)
3181 {
3182         zval *object, *interval;
3183 
3184         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3185                 RETURN_FALSE;
3186         }
3187 
3188         php_date_sub(object, interval, return_value);
3189 
3190         Z_ADDREF_P(object);
3191         ZVAL_COPY_VALUE(return_value, object);
3192 }
3193 /* }}} */
3194 
3195 /* {{{ proto DateTimeImmutable::sub()
3196 */
3197 PHP_METHOD(DateTimeImmutable, sub)
3198 {
3199         zval *object, *interval, new_object;
3200 
3201         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_immutable, &interval, date_ce_interval) == FAILURE) {
3202                 RETURN_FALSE;
3203         }
3204 
3205         date_clone_immutable(object, &new_object);
3206         php_date_sub(&new_object, interval, return_value);
3207 
3208         ZVAL_COPY_VALUE(return_value, &new_object);
3209 }
3210 /* }}} */
3211 
3212 static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
3213 {
3214        tzobj->initialized = 1;
3215        tzobj->type = t->zone_type;
3216        switch (t->zone_type) {
3217                case TIMELIB_ZONETYPE_ID:
3218                        tzobj->tzi.tz = t->tz_info;
3219                        break;
3220                case TIMELIB_ZONETYPE_OFFSET:
3221                        tzobj->tzi.utc_offset = t->z;
3222                        break;
3223                case TIMELIB_ZONETYPE_ABBR:
3224                        tzobj->tzi.z.utc_offset = t->z;
3225                        tzobj->tzi.z.dst = t->dst;
3226                        tzobj->tzi.z.abbr = timelib_strdup(t->tz_abbr);
3227                        break;
3228        }
3229 }
3230 
3231 
3232 /* {{{ proto DateTimeZone date_timezone_get(DateTimeInterface object)
3233    Return new DateTimeZone object relative to give DateTime
3234 */
3235 PHP_FUNCTION(date_timezone_get)
3236 {
3237         zval             *object;
3238         php_date_obj     *dateobj;
3239         php_timezone_obj *tzobj;
3240 
3241         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3242                 RETURN_FALSE;
3243         }
3244         dateobj = Z_PHPDATE_P(object);
3245         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3246         if (dateobj->time->is_localtime/* && dateobj->time->tz_info*/) {
3247                 php_date_instantiate(date_ce_timezone, return_value);
3248                 tzobj = Z_PHPTIMEZONE_P(return_value);
3249                 set_timezone_from_timelib_time(tzobj, dateobj->time);
3250         } else {
3251                 RETURN_FALSE;
3252         }
3253 }
3254 /* }}} */
3255 
3256 static void php_date_timezone_set(zval *object, zval *timezone_object, zval *return_value) /* {{{ */
3257 {
3258         php_date_obj     *dateobj;
3259         php_timezone_obj *tzobj;
3260 
3261         dateobj = Z_PHPDATE_P(object);
3262         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3263         tzobj = Z_PHPTIMEZONE_P(timezone_object);
3264 
3265         switch (tzobj->type) {
3266                 case TIMELIB_ZONETYPE_OFFSET:
3267                         timelib_set_timezone_from_offset(dateobj->time, tzobj->tzi.utc_offset);
3268                         break;
3269                 case TIMELIB_ZONETYPE_ABBR:
3270                         timelib_set_timezone_from_abbr(dateobj->time, tzobj->tzi.z);
3271                         break;
3272                 case TIMELIB_ZONETYPE_ID:
3273                         timelib_set_timezone(dateobj->time, tzobj->tzi.tz);
3274                         break;
3275         }
3276         timelib_unixtime2local(dateobj->time, dateobj->time->sse);
3277 } /* }}} */
3278 
3279 /* {{{ proto DateTime date_timezone_set(DateTime object, DateTimeZone object)
3280    Sets the timezone for the DateTime object.
3281 */
3282 PHP_FUNCTION(date_timezone_set)
3283 {
3284         zval *object;
3285         zval *timezone_object;
3286 
3287         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) {
3288                 RETURN_FALSE;
3289         }
3290 
3291         php_date_timezone_set(object, timezone_object, return_value);
3292 
3293         Z_ADDREF_P(object);
3294         ZVAL_COPY_VALUE(return_value, object);
3295 }
3296 /* }}} */
3297 
3298 /* {{{ proto DateTimeImmutable::setTimezone()
3299 */
3300 PHP_METHOD(DateTimeImmutable, setTimezone)
3301 {
3302         zval *object, new_object;
3303         zval *timezone_object;
3304 
3305         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_immutable, &timezone_object, date_ce_timezone) == FAILURE) {
3306                 RETURN_FALSE;
3307         }
3308 
3309         date_clone_immutable(object, &new_object);
3310         php_date_timezone_set(&new_object, timezone_object, return_value);
3311 
3312         ZVAL_COPY_VALUE(return_value, &new_object);
3313 }
3314 /* }}} */
3315 
3316 /* {{{ proto long date_offset_get(DateTimeInterface object)
3317    Returns the DST offset.
3318 */
3319 PHP_FUNCTION(date_offset_get)
3320 {
3321         zval                *object;
3322         php_date_obj        *dateobj;
3323         timelib_time_offset *offset;
3324 
3325         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3326                 RETURN_FALSE;
3327         }
3328         dateobj = Z_PHPDATE_P(object);
3329         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3330         if (dateobj->time->is_localtime/* && dateobj->time->tz_info*/) {
3331                 switch (dateobj->time->zone_type) {
3332                         case TIMELIB_ZONETYPE_ID:
3333                                 offset = timelib_get_time_zone_info(dateobj->time->sse, dateobj->time->tz_info);
3334                                 RETVAL_LONG(offset->offset);
3335                                 timelib_time_offset_dtor(offset);
3336                                 break;
3337                         case TIMELIB_ZONETYPE_OFFSET:
3338                                 RETVAL_LONG(dateobj->time->z * -60);
3339                                 break;
3340                         case TIMELIB_ZONETYPE_ABBR:
3341                                 RETVAL_LONG((dateobj->time->z - (60 * dateobj->time->dst)) * -60);
3342                                 break;
3343                 }
3344                 return;
3345         } else {
3346                 RETURN_LONG(0);
3347         }
3348 }
3349 /* }}} */
3350 
3351 static void php_date_time_set(zval *object, zend_long h, zend_long i, zend_long s, zval *return_value) /* {{{ */
3352 {
3353         php_date_obj *dateobj;
3354 
3355         dateobj = Z_PHPDATE_P(object);
3356         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3357         dateobj->time->h = h;
3358         dateobj->time->i = i;
3359         dateobj->time->s = s;
3360         timelib_update_ts(dateobj->time, NULL);
3361 } /* }}} */
3362 
3363 /* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second])
3364    Sets the time.
3365 */
3366 PHP_FUNCTION(date_time_set)
3367 {
3368         zval *object;
3369         zend_long  h, i, s = 0;
3370 
3371         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) {
3372                 RETURN_FALSE;
3373         }
3374 
3375         php_date_time_set(object, h, i, s, return_value);
3376 
3377         Z_ADDREF_P(object);
3378         ZVAL_COPY_VALUE(return_value, object);
3379 }
3380 /* }}} */
3381 
3382 /* {{{ proto DateTimeImmutable::setTime()
3383 */
3384 PHP_METHOD(DateTimeImmutable, setTime)
3385 {
3386         zval *object, new_object;
3387         zend_long  h, i, s = 0;
3388 
3389         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_immutable, &h, &i, &s) == FAILURE) {
3390                 RETURN_FALSE;
3391         }
3392 
3393         date_clone_immutable(object, &new_object);
3394         php_date_time_set(&new_object, h, i, s, return_value);
3395 
3396         ZVAL_COPY_VALUE(return_value, &new_object);
3397 }
3398 /* }}} */
3399 
3400 static void php_date_date_set(zval *object, zend_long y, zend_long m, zend_long d, zval *return_value) /* {{{ */
3401 {
3402         php_date_obj *dateobj;
3403 
3404         dateobj = Z_PHPDATE_P(object);
3405         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3406         dateobj->time->y = y;
3407         dateobj->time->m = m;
3408         dateobj->time->d = d;
3409         timelib_update_ts(dateobj->time, NULL);
3410 } /* }}} */
3411 
3412 /* {{{ proto DateTime date_date_set(DateTime object, long year, long month, long day)
3413    Sets the date.
3414 */
3415 PHP_FUNCTION(date_date_set)
3416 {
3417         zval *object;
3418         zend_long  y, m, d;
3419 
3420         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) {
3421                 RETURN_FALSE;
3422         }
3423 
3424         php_date_date_set(object, y, m, d, return_value);
3425 
3426         Z_ADDREF_P(object);
3427         ZVAL_COPY_VALUE(return_value, object);
3428 }
3429 /* }}} */
3430 
3431 /* {{{ proto DateTimeImmutable::setDate()
3432 */
3433 PHP_METHOD(DateTimeImmutable, setDate)
3434 {
3435         zval *object, new_object;
3436         zend_long  y, m, d;
3437 
3438         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Olll", &object, date_ce_immutable, &y, &m, &d) == FAILURE) {
3439                 RETURN_FALSE;
3440         }
3441 
3442         date_clone_immutable(object, &new_object);
3443         php_date_date_set(&new_object, y, m, d, return_value);
3444 
3445         ZVAL_COPY_VALUE(return_value, &new_object);
3446 }
3447 /* }}} */
3448 
3449 static void php_date_isodate_set(zval *object, zend_long y, zend_long w, zend_long d, zval *return_value) /* {{{ */
3450 {
3451         php_date_obj *dateobj;
3452 
3453         dateobj = Z_PHPDATE_P(object);
3454         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3455         dateobj->time->y = y;
3456         dateobj->time->m = 1;
3457         dateobj->time->d = 1;
3458         memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3459         dateobj->time->relative.d = timelib_daynr_from_weeknr(y, w, d);
3460         dateobj->time->have_relative = 1;
3461 
3462         timelib_update_ts(dateobj->time, NULL);
3463 } /* }}} */
3464 
3465 /* {{{ proto DateTime date_isodate_set(DateTime object, long year, long week[, long day])
3466    Sets the ISO date.
3467 */
3468 PHP_FUNCTION(date_isodate_set)
3469 {
3470         zval *object;
3471         zend_long  y, w, d = 1;
3472 
3473         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) {
3474                 RETURN_FALSE;
3475         }
3476 
3477         php_date_isodate_set(object, y, w, d, return_value);
3478 
3479         Z_ADDREF_P(object);
3480         ZVAL_COPY_VALUE(return_value, object);
3481 }
3482 /* }}} */
3483 
3484 /* {{{ proto DateTimeImmutable::setISODate()
3485 */
3486 PHP_METHOD(DateTimeImmutable, setISODate)
3487 {
3488         zval *object, new_object;
3489         zend_long  y, w, d = 1;
3490 
3491         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_immutable, &y, &w, &d) == FAILURE) {
3492                 RETURN_FALSE;
3493         }
3494 
3495         date_clone_immutable(object, &new_object);
3496         php_date_isodate_set(&new_object, y, w, d, return_value);
3497 
3498         ZVAL_COPY_VALUE(return_value, &new_object);
3499 }
3500 /* }}} */
3501 
3502 static void php_date_timestamp_set(zval *object, zend_long timestamp, zval *return_value) /* {{{ */
3503 {
3504         php_date_obj *dateobj;
3505 
3506         dateobj = Z_PHPDATE_P(object);
3507         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3508         timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);
3509         timelib_update_ts(dateobj->time, NULL);
3510 } /* }}} */
3511 
3512 /* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp)
3513    Sets the date and time based on an Unix timestamp.
3514 */
3515 PHP_FUNCTION(date_timestamp_set)
3516 {
3517         zval *object;
3518         zend_long  timestamp;
3519 
3520         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, date_ce_date, &timestamp) == FAILURE) {
3521                 RETURN_FALSE;
3522         }
3523 
3524         php_date_timestamp_set(object, timestamp, return_value);
3525 
3526         Z_ADDREF_P(object);
3527         ZVAL_COPY_VALUE(return_value, object);
3528 }
3529 /* }}} */
3530 
3531 /* {{{ proto DateTimeImmutable::setTimestamp()
3532 */
3533 PHP_METHOD(DateTimeImmutable, setTimestamp)
3534 {
3535         zval *object, new_object;
3536         zend_long  timestamp;
3537 
3538         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, date_ce_immutable, &timestamp) == FAILURE) {
3539                 RETURN_FALSE;
3540         }
3541 
3542         date_clone_immutable(object, &new_object);
3543         php_date_timestamp_set(&new_object, timestamp, return_value);
3544 
3545         ZVAL_COPY_VALUE(return_value, &new_object);
3546 }
3547 /* }}} */
3548 
3549 /* {{{ proto long date_timestamp_get(DateTimeInterface object)
3550    Gets the Unix timestamp.
3551 */
3552 PHP_FUNCTION(date_timestamp_get)
3553 {
3554         zval         *object;
3555         php_date_obj *dateobj;
3556         zend_long          timestamp;
3557         int           error;
3558 
3559         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3560                 RETURN_FALSE;
3561         }
3562         dateobj = Z_PHPDATE_P(object);
3563         DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3564         timelib_update_ts(dateobj->time, NULL);
3565 
3566         timestamp = timelib_date_to_int(dateobj->time, &error);
3567         if (error) {
3568                 RETURN_FALSE;
3569         } else {
3570                 RETVAL_LONG(timestamp);
3571         }
3572 }
3573 /* }}} */
3574 
3575 /* {{{ proto DateInterval date_diff(DateTime object [, bool absolute])
3576    Returns the difference between two DateTime objects.
3577 */
3578 PHP_FUNCTION(date_diff)
3579 {
3580         zval         *object1, *object2;
3581         php_date_obj *dateobj1, *dateobj2;
3582         php_interval_obj *interval;
3583         zend_bool      absolute = 0;
3584 
3585         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO|b", &object1, date_ce_interface, &object2, date_ce_interface, &absolute) == FAILURE) {
3586                 RETURN_FALSE;
3587         }
3588         dateobj1 = Z_PHPDATE_P(object1);
3589         dateobj2 = Z_PHPDATE_P(object2);
3590         DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface);
3591         DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface);
3592         timelib_update_ts(dateobj1->time, NULL);
3593         timelib_update_ts(dateobj2->time, NULL);
3594 
3595         php_date_instantiate(date_ce_interval, return_value);
3596         interval = Z_PHPINTERVAL_P(return_value);
3597         interval->diff = timelib_diff(dateobj1->time, dateobj2->time);
3598         if (absolute) {
3599                 interval->diff->invert = 0;
3600         }
3601         interval->initialized = 1;
3602 }
3603 /* }}} */
3604 
3605 static int timezone_initialize(php_timezone_obj *tzobj, /*const*/ char *tz, size_t tz_len) /* {{{ */
3606 {
3607         timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
3608         int           dst, not_found;
3609         char         *orig_tz = tz;
3610 
3611         if (strlen(tz) != tz_len) {
3612                 php_error_docref(NULL, E_WARNING, "Timezone must not contain null bytes");
3613                 return FAILURE;
3614         }
3615 
3616         dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, &not_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3617         if (not_found) {
3618                 php_error_docref(NULL, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3619                 efree(dummy_t);
3620                 return FAILURE;
3621         } else {
3622                 set_timezone_from_timelib_time(tzobj, dummy_t);
3623                 timelib_free(dummy_t->tz_abbr);
3624                 efree(dummy_t);
3625                 return SUCCESS;
3626         }
3627 } /* }}} */
3628 
3629 /* {{{ proto DateTimeZone timezone_open(string timezone)
3630    Returns new DateTimeZone object
3631 */
3632 PHP_FUNCTION(timezone_open)
3633 {
3634         char *tz;
3635         size_t tz_len;
3636         php_timezone_obj *tzobj;
3637 
3638         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &tz, &tz_len) == FAILURE) {
3639                 RETURN_FALSE;
3640         }
3641         tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, return_value));
3642         if (SUCCESS != timezone_initialize(tzobj, tz, tz_len)) {
3643                 zval_ptr_dtor(return_value);
3644                 RETURN_FALSE;
3645         }
3646 }
3647 /* }}} */
3648 
3649 /* {{{ proto DateTimeZone::__construct(string timezone)
3650    Creates new DateTimeZone object.
3651 */
3652 PHP_METHOD(DateTimeZone, __construct)
3653 {
3654         char *tz;
3655         size_t tz_len;
3656         php_timezone_obj *tzobj;
3657         zend_error_handling error_handling;
3658 
3659         if (FAILURE == zend_parse_parameters_throw(ZEND_NUM_ARGS(), "s", &tz, &tz_len)) {
3660                 return;
3661         }
3662 
3663         zend_replace_error_handling(EH_THROW, NULL, &error_handling);
3664         tzobj = Z_PHPTIMEZONE_P(getThis());
3665         timezone_initialize(tzobj, tz, tz_len);
3666         zend_restore_error_handling(&error_handling);
3667 }
3668 /* }}} */
3669 
3670 static int php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht) /* {{{ */
3671 {
3672         zval            *z_timezone;
3673         zval            *z_timezone_type;
3674 
3675         if ((z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type") - 1)) != NULL) {
3676                 if ((z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone") - 1)) != NULL) {
3677                         if (Z_TYPE_P(z_timezone_type) != IS_LONG) {
3678                                 return FAILURE;
3679                         }
3680                         if (Z_TYPE_P(z_timezone) != IS_STRING) {
3681                                 return FAILURE;
3682                         }
3683                         if (SUCCESS == timezone_initialize(*tzobj, Z_STRVAL_P(z_timezone), Z_STRLEN_P(z_timezone))) {
3684                                 return SUCCESS;
3685                         }
3686                 }
3687         }
3688         return FAILURE;
3689 } /* }}} */
3690 
3691 /* {{{ proto DateTimeZone::__set_state()
3692  *  */
3693 PHP_METHOD(DateTimeZone, __set_state)
3694 {
3695         php_timezone_obj *tzobj;
3696         zval             *array;
3697         HashTable        *myht;
3698 
3699         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
3700                 RETURN_FALSE;
3701         }
3702 
3703         myht = Z_ARRVAL_P(array);
3704 
3705         php_date_instantiate(date_ce_timezone, return_value);
3706         tzobj = Z_PHPTIMEZONE_P(return_value);
3707         if(php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) != SUCCESS) {
3708                 php_error_docref(NULL, E_ERROR, "Timezone initialization failed");
3709         }
3710 }
3711 /* }}} */
3712 
3713 /* {{{ proto DateTimeZone::__wakeup()
3714  *  */
3715 PHP_METHOD(DateTimeZone, __wakeup)
3716 {
3717         zval             *object = getThis();
3718         php_timezone_obj *tzobj;
3719         HashTable        *myht;
3720 
3721         tzobj = Z_PHPTIMEZONE_P(object);
3722 
3723         myht = Z_OBJPROP_P(object);
3724 
3725         if(php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht) != SUCCESS) {
3726                 php_error_docref(NULL, E_ERROR, "Timezone initialization failed");
3727         }
3728 }
3729 /* }}} */
3730 
3731 /* {{{ proto string timezone_name_get(DateTimeZone object)
3732    Returns the name of the timezone.
3733 */
3734 PHP_FUNCTION(timezone_name_get)
3735 {
3736         zval             *object;
3737         php_timezone_obj *tzobj;
3738 
3739         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3740                 RETURN_FALSE;
3741         }
3742         tzobj = Z_PHPTIMEZONE_P(object);
3743         DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3744 
3745         switch (tzobj->type) {
3746                 case TIMELIB_ZONETYPE_ID:
3747                         RETURN_STRING(tzobj->tzi.tz->name);
3748                         break;
3749                 case TIMELIB_ZONETYPE_OFFSET: {
3750                         zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
3751                         timelib_sll utc_offset = tzobj->tzi.utc_offset;
3752 
3753                         ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
3754                                 utc_offset > 0 ? '-' : '+',
3755                                 abs(utc_offset / 60),
3756                                 abs((utc_offset % 60)));
3757 
3758                         RETURN_NEW_STR(tmpstr);
3759                         }
3760                         break;
3761                 case TIMELIB_ZONETYPE_ABBR:
3762                         RETURN_STRING(tzobj->tzi.z.abbr);
3763                         break;
3764         }
3765 }
3766 /* }}} */
3767 
3768 /* {{{ proto string timezone_name_from_abbr(string abbr[, long gmtOffset[, long isdst]])
3769    Returns the timezone name from abbrevation
3770 */
3771 PHP_FUNCTION(timezone_name_from_abbr)
3772 {
3773         char    *abbr;
3774         char    *tzid;
3775         size_t      abbr_len;
3776         zend_long     gmtoffset = -1;
3777         zend_long     isdst = -1;
3778 
3779         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &abbr, &abbr_len, &gmtoffset, &isdst) == FAILURE) {
3780                 RETURN_FALSE;
3781         }
3782         tzid = timelib_timezone_id_from_abbr(abbr, gmtoffset, isdst);
3783 
3784         if (tzid) {
3785                 RETURN_STRING(tzid);
3786         } else {
3787                 RETURN_FALSE;
3788         }
3789 }
3790 /* }}} */
3791 
3792 /* {{{ proto long timezone_offset_get(DateTimeZone object, DateTimeInterface datetime)
3793    Returns the timezone offset.
3794 */
3795 PHP_FUNCTION(timezone_offset_get)
3796 {
3797         zval                *object, *dateobject;
3798         php_timezone_obj    *tzobj;
3799         php_date_obj        *dateobj;
3800         timelib_time_offset *offset;
3801 
3802         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_timezone, &dateobject, date_ce_interface) == FAILURE) {
3803                 RETURN_FALSE;
3804         }
3805         tzobj = Z_PHPTIMEZONE_P(object);
3806         DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3807         dateobj = Z_PHPDATE_P(dateobject);
3808         DATE_CHECK_INITIALIZED(dateobj->time, DateTimeInterface);
3809 
3810         switch (tzobj->type) {
3811                 case TIMELIB_ZONETYPE_ID:
3812                         offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz);
3813                         RETVAL_LONG(offset->offset);
3814                         timelib_time_offset_dtor(offset);
3815                         break;
3816                 case TIMELIB_ZONETYPE_OFFSET:
3817                         RETURN_LONG(tzobj->tzi.utc_offset * -60);
3818                         break;
3819                 case TIMELIB_ZONETYPE_ABBR:
3820                         RETURN_LONG((tzobj->tzi.z.utc_offset - (tzobj->tzi.z.dst*60)) * -60);
3821                         break;
3822         }
3823 }
3824 /* }}} */
3825 
3826 /* {{{ proto array timezone_transitions_get(DateTimeZone object [, long timestamp_begin [, long timestamp_end ]])
3827    Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone.
3828 */
3829 PHP_FUNCTION(timezone_transitions_get)
3830 {
3831         zval                *object, element;
3832         php_timezone_obj    *tzobj;
3833         unsigned int         i, begin = 0, found;
3834         zend_long            timestamp_begin = ZEND_LONG_MIN, timestamp_end = ZEND_LONG_MAX;
3835 
3836         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ll", &object, date_ce_timezone, &timestamp_begin, &timestamp_end) == FAILURE) {
3837                 RETURN_FALSE;
3838         }
3839         tzobj = Z_PHPTIMEZONE_P(object);
3840         DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3841         if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3842                 RETURN_FALSE;
3843         }
3844 
3845 #define add_nominal() \
3846                 array_init(&element); \
3847                 add_assoc_long(&element, "ts",     timestamp_begin); \
3848                 add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, timestamp_begin, 0)); \
3849                 add_assoc_long(&element, "offset", tzobj->tzi.tz->type[0].offset); \
3850                 add_assoc_bool(&element, "isdst",  tzobj->tzi.tz->type[0].isdst); \
3851                 add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx]); \
3852                 add_next_index_zval(return_value, &element);
3853 
3854 #define add(i,ts) \
3855                 array_init(&element); \
3856                 add_assoc_long(&element, "ts",     ts); \
3857                 add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601, 13, ts, 0)); \
3858                 add_assoc_long(&element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \
3859                 add_assoc_bool(&element, "isdst",  tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \
3860                 add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx]); \
3861                 add_next_index_zval(return_value, &element);
3862 
3863 #define add_last() add(tzobj->tzi.tz->bit32.timecnt - 1, timestamp_begin)
3864 
3865         array_init(return_value);
3866 
3867         if (timestamp_begin == ZEND_LONG_MIN) {
3868                 add_nominal();
3869                 begin = 0;
3870                 found = 1;
3871         } else {
3872                 begin = 0;
3873                 found = 0;
3874                 if (tzobj->tzi.tz->bit32.timecnt > 0) {
3875                         do {
3876                                 if (tzobj->tzi.tz->trans[begin] > timestamp_begin) {
3877                                         if (begin > 0) {
3878                                                 add(begin - 1, timestamp_begin);
3879                                         } else {
3880                                                 add_nominal();
3881                                         }
3882                                         found = 1;
3883                                         break;
3884                                 }
3885                                 begin++;
3886                         } while (begin < tzobj->tzi.tz->bit32.timecnt);
3887                 }
3888         }
3889 
3890         if (!found) {
3891                 if (tzobj->tzi.tz->bit32.timecnt > 0) {
3892                         add_last();
3893                 } else {
3894                         add_nominal();
3895                 }
3896         } else {
3897                 for (i = begin; i < tzobj->tzi.tz->bit32.timecnt; ++i) {
3898                         if (tzobj->tzi.tz->trans[i] < timestamp_end) {
3899                                 add(i, tzobj->tzi.tz->trans[i]);
3900                         }
3901                 }
3902         }
3903 }
3904 /* }}} */
3905 
3906 /* {{{ proto array timezone_location_get()
3907    Returns location information for a timezone, including country code, latitude/longitude and comments
3908 */
3909 PHP_FUNCTION(timezone_location_get)
3910 {
3911         zval                *object;
3912         php_timezone_obj    *tzobj;
3913 
3914         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3915                 RETURN_FALSE;
3916         }
3917         tzobj = Z_PHPTIMEZONE_P(object);
3918         DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3919         if (tzobj->type != TIMELIB_ZONETYPE_ID) {
3920                 RETURN_FALSE;
3921         }
3922 
3923         array_init(return_value);
3924         add_assoc_string(return_value, "country_code", tzobj->tzi.tz->location.country_code);
3925         add_assoc_double(return_value, "latitude", tzobj->tzi.tz->location.latitude);
3926         add_assoc_double(return_value, "longitude", tzobj->tzi.tz->location.longitude);
3927         add_assoc_string(return_value, "comments", tzobj->tzi.tz->location.comments);
3928 }
3929 /* }}} */
3930 
3931 static int date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, size_t format_length) /* {{{ */
3932 {
3933         timelib_time     *b = NULL, *e = NULL;
3934         timelib_rel_time *p = NULL;
3935         int               r = 0;
3936         int               retval = 0;
3937         struct timelib_error_container *errors;
3938 
3939         timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
3940 
3941         if (errors->error_count > 0) {
3942                 php_error_docref(NULL, E_WARNING, "Unknown or bad format (%s)", format);
3943                 retval = FAILURE;
3944         } else {
3945                 if(p) {
3946                         *rt = p;
3947                         retval = SUCCESS;
3948                 } else {
3949                         if(b && e) {
3950                                 timelib_update_ts(b, NULL);
3951                                 timelib_update_ts(e, NULL);
3952                                 *rt = timelib_diff(b, e);
3953                                 retval = SUCCESS;
3954                         } else {
3955                                 php_error_docref(NULL, E_WARNING, "Failed to parse interval (%s)", format);
3956                                 retval = FAILURE;
3957                         }
3958                 }
3959         }
3960         timelib_error_container_dtor(errors);
3961         timelib_free(b);
3962         timelib_free(e);
3963         return retval;
3964 } /* }}} */
3965 
3966 /* {{{ date_interval_read_property */
3967 zval *date_interval_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv)
3968 {
3969         php_interval_obj *obj;
3970         zval *retval;
3971         zval tmp_member;
3972         timelib_sll value = -1;
3973 
3974         if (Z_TYPE_P(member) != IS_STRING) {
3975                 tmp_member = *member;
3976                 zval_copy_ctor(&tmp_member);
3977                 convert_to_string(&tmp_member);
3978                 member = &tmp_member;
3979                 cache_slot = NULL;
3980         }
3981 
3982         obj = Z_PHPINTERVAL_P(object);
3983 
3984         if (!obj->initialized) {
3985                 retval = (zend_get_std_object_handlers())->read_property(object, member, type, cache_slot, rv);
3986                 if (member == &tmp_member) {
3987                         zval_dtor(member);
3988                 }
3989                 return retval;
3990         }
3991 
3992 #define GET_VALUE_FROM_STRUCT(n,m)            \
3993         if (strcmp(Z_STRVAL_P(member), m) == 0) { \
3994                 value = obj->diff->n;                 \
3995                 break;                                                            \
3996         }
3997         do {
3998                 GET_VALUE_FROM_STRUCT(y, "y");
3999                 GET_VALUE_FROM_STRUCT(m, "m");
4000                 GET_VALUE_FROM_STRUCT(d, "d");
4001                 GET_VALUE_FROM_STRUCT(h, "h");
4002                 GET_VALUE_FROM_STRUCT(i, "i");
4003                 GET_VALUE_FROM_STRUCT(s, "s");
4004                 GET_VALUE_FROM_STRUCT(invert, "invert");
4005                 GET_VALUE_FROM_STRUCT(days, "days");
4006                 /* didn't find any */
4007                 retval = (zend_get_std_object_handlers())->read_property(object, member, type, cache_slot, rv);
4008 
4009                 if (member == &tmp_member) {
4010                         zval_dtor(member);
4011                 }
4012 
4013                 return retval;
4014         } while(0);
4015 
4016         retval = rv;
4017 
4018         if (value != -99999) {
4019                 ZVAL_LONG(retval, value);
4020         } else {
4021                 ZVAL_FALSE(retval);
4022         }
4023 
4024         if (member == &tmp_member) {
4025                 zval_dtor(member);
4026         }
4027 
4028         return retval;
4029 }
4030 /* }}} */
4031 
4032 /* {{{ date_interval_write_property */
4033 void date_interval_write_property(zval *object, zval *member, zval *value, void **cache_slot)
4034 {
4035         php_interval_obj *obj;
4036         zval tmp_member;
4037 
4038         if (Z_TYPE_P(member) != IS_STRING) {
4039                 tmp_member = *member;
4040                 zval_copy_ctor(&tmp_member);
4041                 convert_to_string(&tmp_member);
4042                 member = &tmp_member;
4043                 cache_slot = NULL;
4044         }
4045 
4046         obj = Z_PHPINTERVAL_P(object);
4047 
4048         if (!obj->initialized) {
4049                 (zend_get_std_object_handlers())->write_property(object, member, value, cache_slot);
4050                 if (member == &tmp_member) {
4051                         zval_dtor(member);
4052                 }
4053                 return;
4054         }
4055 
4056 #define SET_VALUE_FROM_STRUCT(n,m)            \
4057         if (strcmp(Z_STRVAL_P(member), m) == 0) { \
4058                 obj->diff->n = zval_get_long(value); \
4059                 break;                                                            \
4060         }
4061 
4062         do {
4063                 SET_VALUE_FROM_STRUCT(y, "y");
4064                 SET_VALUE_FROM_STRUCT(m, "m");
4065                 SET_VALUE_FROM_STRUCT(d, "d");
4066                 SET_VALUE_FROM_STRUCT(h, "h");
4067                 SET_VALUE_FROM_STRUCT(i, "i");
4068                 SET_VALUE_FROM_STRUCT(s, "s");
4069                 SET_VALUE_FROM_STRUCT(invert, "invert");
4070                 /* didn't find any */
4071                 (zend_get_std_object_handlers())->write_property(object, member, value, cache_slot);
4072         } while(0);
4073 
4074         if (member == &tmp_member) {
4075                 zval_dtor(member);
4076         }
4077 }
4078 /* }}} */
4079 
4080 
4081 /* {{{ proto DateInterval::__construct([string interval_spec])
4082    Creates new DateInterval object.
4083 */
4084 PHP_METHOD(DateInterval, __construct)
4085 {
4086         char *interval_string = NULL;
4087         size_t   interval_string_length;
4088         php_interval_obj *diobj;
4089         timelib_rel_time *reltime;
4090         zend_error_handling error_handling;
4091 
4092         if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "s", &interval_string, &interval_string_length) == FAILURE) {
4093                 return;
4094         }
4095 
4096         zend_replace_error_handling(EH_THROW, NULL, &error_handling);
4097         if (date_interval_initialize(&reltime, interval_string, interval_string_length) == SUCCESS) {
4098                 diobj = Z_PHPINTERVAL_P(getThis());
4099                 diobj->diff = reltime;
4100                 diobj->initialized = 1;
4101         }
4102         zend_restore_error_handling(&error_handling);
4103 }
4104 /* }}} */
4105 
4106 
4107 static int php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht) /* {{{ */
4108 {
4109         (*intobj)->diff = timelib_rel_time_ctor();
4110 
4111 #define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \
4112         do { \
4113                 zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
4114                 if (z_arg) { \
4115                         (*intobj)->diff->member = (itype)zval_get_long(z_arg); \
4116                 } else { \
4117                         (*intobj)->diff->member = (itype)def; \
4118                 } \
4119         } while (0);
4120 
4121 #define PHP_DATE_INTERVAL_READ_PROPERTY_I64(element, member) \
4122         do { \
4123                 zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
4124                 if (z_arg) { \
4125                         zend_string *str = zval_get_string(z_arg); \
4126                         DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \
4127                         zend_string_release(str); \
4128                 } else { \
4129                         (*intobj)->diff->member = -1LL; \
4130                 } \
4131         } while (0);
4132 
4133         PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1)
4134         PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1)
4135         PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1)
4136         PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1)
4137         PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
4138         PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
4139         PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1)
4140         PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1)
4141         PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1)
4142         PHP_DATE_INTERVAL_READ_PROPERTY("invert", invert, int, 0);
4143         PHP_DATE_INTERVAL_READ_PROPERTY_I64("days", days);
4144         PHP_DATE_INTERVAL_READ_PROPERTY("special_type", special.type, unsigned int, 0);
4145         PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount);
4146         PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
4147         PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
4148         (*intobj)->initialized = 1;
4149 
4150         return 0;
4151 } /* }}} */
4152 
4153 /* {{{ proto DateInterval::__set_state()
4154 */
4155 PHP_METHOD(DateInterval, __set_state)
4156 {
4157         php_interval_obj *intobj;
4158         zval             *array;
4159         HashTable        *myht;
4160 
4161         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
4162                 RETURN_FALSE;
4163         }
4164 
4165         myht = Z_ARRVAL_P(array);
4166 
4167         php_date_instantiate(date_ce_interval, return_value);
4168         intobj = Z_PHPINTERVAL_P(return_value);
4169         php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
4170 }
4171 /* }}} */
4172 
4173 /* {{{ proto DateInterval::__wakeup()
4174 */
4175 PHP_METHOD(DateInterval, __wakeup)
4176 {
4177         zval             *object = getThis();
4178         php_interval_obj *intobj;
4179         HashTable        *myht;
4180 
4181         intobj = Z_PHPINTERVAL_P(object);
4182 
4183         myht = Z_OBJPROP_P(object);
4184 
4185         php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
4186 }
4187 /* }}} */
4188 
4189 /* {{{ proto DateInterval date_interval_create_from_date_string(string time)
4190    Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string
4191 */
4192 PHP_FUNCTION(date_interval_create_from_date_string)
4193 {
4194         char           *time_str = NULL;
4195         size_t          time_str_len = 0;
4196         timelib_time   *time;
4197         timelib_error_container *err = NULL;
4198         php_interval_obj *diobj;
4199 
4200         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &time_str, &time_str_len) == FAILURE) {
4201                 RETURN_FALSE;
4202         }
4203 
4204         php_date_instantiate(date_ce_interval, return_value);
4205 
4206         time = timelib_strtotime(time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4207         diobj = Z_PHPINTERVAL_P(return_value);
4208         diobj->diff = timelib_rel_time_clone(&time->relative);
4209         diobj->initialized = 1;
4210         timelib_time_dtor(time);
4211         timelib_error_container_dtor(err);
4212 }
4213 /* }}} */
4214 
4215 /* {{{ date_interval_format -  */
4216 static zend_string *date_interval_format(char *format, size_t format_len, timelib_rel_time *t)
4217 {
4218         smart_str            string = {0};
4219         int                  i, length, have_format_spec = 0;
4220         char                 buffer[33];
4221 
4222         if (!format_len) {
4223                 return ZSTR_EMPTY_ALLOC();
4224         }
4225 
4226         for (i = 0; i < format_len; i++) {
4227                 if (have_format_spec) {
4228                         switch (format[i]) {
4229                                 case 'Y': length = slprintf(buffer, 32, "%02d", (int) t->y); break;
4230                                 case 'y': length = slprintf(buffer, 32, "%d", (int) t->y); break;
4231 
4232                                 case 'M': length = slprintf(buffer, 32, "%02d", (int) t->m); break;
4233                                 case 'm': length = slprintf(buffer, 32, "%d", (int) t->m); break;
4234 
4235                                 case 'D': length = slprintf(buffer, 32, "%02d", (int) t->d); break;
4236                                 case 'd': length = slprintf(buffer, 32, "%d", (int) t->d); break;
4237 
4238                                 case 'H': length = slprintf(buffer, 32, "%02d", (int) t->h); break;
4239                                 case 'h': length = slprintf(buffer, 32, "%d", (int) t->h); break;
4240 
4241                                 case 'I': length = slprintf(buffer, 32, "%02d", (int) t->i); break;
4242                                 case 'i': length = slprintf(buffer, 32, "%d", (int) t->i); break;
4243 
4244                                 case 'S': length = slprintf(buffer, 32, "%02" ZEND_LONG_FMT_SPEC, (zend_long) t->s); break;
4245                                 case 's': length = slprintf(buffer, 32, ZEND_LONG_FMT, (zend_long) t->s); break;
4246 
4247                                 case 'a': {
4248                                         if ((int) t->days != -99999) {
4249                                                 length = slprintf(buffer, 32, "%d", (int) t->days);
4250                                         } else {
4251                                                 length = slprintf(buffer, 32, "(unknown)");
4252                                         }
4253                                 } break;
4254                                 case 'r': length = slprintf(buffer, 32, "%s", t->invert ? "-" : ""); break;
4255                                 case 'R': length = slprintf(buffer, 32, "%c", t->invert ? '-' : '+'); break;
4256 
4257                                 case '%': length = slprintf(buffer, 32, "%%"); break;
4258                                 default: buffer[0] = '%'; buffer[1] = format[i]; buffer[2] = '\0'; length = 2; break;
4259                         }
4260                         smart_str_appendl(&string, buffer, length);
4261                         have_format_spec = 0;
4262                 } else {
4263                         if (format[i] == '%') {
4264                                 have_format_spec = 1;
4265                         } else {
4266                                 smart_str_appendc(&string, format[i]);
4267                         }
4268                 }
4269         }
4270 
4271         smart_str_0(&string);
4272 
4273         return string.s;
4274 }
4275 /* }}} */
4276 
4277 /* {{{ proto string date_interval_format(DateInterval object, string format)
4278    Formats the interval.
4279 */
4280 PHP_FUNCTION(date_interval_format)
4281 {
4282         zval             *object;
4283         php_interval_obj *diobj;
4284         char             *format;
4285         size_t            format_len;
4286 
4287         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) {
4288                 RETURN_FALSE;
4289         }
4290         diobj = Z_PHPINTERVAL_P(object);
4291         DATE_CHECK_INITIALIZED(diobj->initialized, DateInterval);
4292 
4293         RETURN_STR(date_interval_format(format, format_len, diobj->diff));
4294 }
4295 /* }}} */
4296 
4297 static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, zend_long *recurrences, /*const*/ char *format, size_t format_length) /* {{{ */
4298 {
4299         timelib_time     *b = NULL, *e = NULL;
4300         timelib_rel_time *p = NULL;
4301         int               r = 0;
4302         int               retval = 0;
4303         struct timelib_error_container *errors;
4304 
4305         timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
4306 
4307         if (errors->error_count > 0) {
4308                 php_error_docref(NULL, E_WARNING, "Unknown or bad format (%s)", format);
4309                 retval = FAILURE;
4310         } else {
4311                 *st = b;
4312                 *et = e;
4313                 *d  = p;
4314                 *recurrences = r;
4315                 retval = SUCCESS;
4316         }
4317         timelib_error_container_dtor(errors);
4318         return retval;
4319 } /* }}} */
4320 
4321 /* {{{ proto DatePeriod::__construct(DateTime $start, DateInterval $interval, int recurrences|DateTime $end)
4322    Creates new DatePeriod object.
4323 */
4324 PHP_METHOD(DatePeriod, __construct)
4325 {
4326         php_period_obj   *dpobj;
4327         php_date_obj     *dateobj;
4328         php_interval_obj *intobj;
4329         zval *start, *end = NULL, *interval;
4330         zend_long  recurrences = 0, options = 0;
4331         char *isostr = NULL;
4332         size_t   isostr_len = 0;
4333         timelib_time *clone;
4334         zend_error_handling error_handling;
4335 
4336         zend_replace_error_handling(EH_THROW, NULL, &error_handling);
4337         if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OOl|l", &start, date_ce_interface, &interval, date_ce_interval, &recurrences, &options) == FAILURE) {
4338                 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OOO|l", &start, date_ce_interface, &interval, date_ce_interval, &end, date_ce_interface, &options) == FAILURE) {
4339                         if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|l", &isostr, &isostr_len, &options) == FAILURE) {
4340                                 php_error_docref(NULL, E_WARNING, "This constructor accepts either (DateTimeInterface, DateInterval, int) OR (DateTimeInterface, DateInterval, DateTime) OR (string) as arguments.");
4341                                 zend_restore_error_handling(&error_handling);
4342                                 return;
4343                         }
4344                 }
4345         }
4346 
4347         dpobj = Z_PHPPERIOD_P(getThis());
4348         dpobj->current = NULL;
4349 
4350         if (isostr) {
4351                 date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len);
4352                 if (dpobj->start == NULL) {
4353                         php_error_docref(NULL, E_WARNING, "The ISO interval '%s' did not contain a start date.", isostr);
4354                 }
4355                 if (dpobj->interval == NULL) {
4356                         php_error_docref(NULL, E_WARNING, "The ISO interval '%s' did not contain an interval.", isostr);
4357                 }
4358                 if (dpobj->end == NULL && recurrences == 0) {
4359                         php_error_docref(NULL, E_WARNING, "The ISO interval '%s' did not contain an end date or a recurrence count.", isostr);
4360                 }
4361 
4362                 if (dpobj->start) {
4363                         timelib_update_ts(dpobj->start, NULL);
4364                 }
4365                 if (dpobj->end) {
4366                         timelib_update_ts(dpobj->end, NULL);
4367                 }
4368                 dpobj->start_ce = date_ce_date;
4369         } else {
4370                 /* init */
4371                 intobj  = Z_PHPINTERVAL_P(interval);
4372 
4373                 /* start date */
4374                 dateobj = Z_PHPDATE_P(start);
4375                 clone = timelib_time_ctor();
4376                 memcpy(clone, dateobj->time, sizeof(timelib_time));
4377                 if (dateobj->time->tz_abbr) {
4378                         clone->tz_abbr = timelib_strdup(dateobj->time->tz_abbr);
4379                 }
4380                 if (dateobj->time->tz_info) {
4381                         clone->tz_info = dateobj->time->tz_info;
4382                 }
4383                 dpobj->start = clone;
4384                 dpobj->start_ce = Z_OBJCE_P(start);
4385 
4386                 /* interval */
4387                 dpobj->interval = timelib_rel_time_clone(intobj->diff);
4388 
4389                 /* end date */
4390                 if (end) {
4391                         dateobj = Z_PHPDATE_P(end);
4392                         clone = timelib_time_clone(dateobj->time);
4393                         dpobj->end = clone;
4394                 }
4395         }
4396 
4397         /* options */
4398         dpobj->include_start_date = !(options & PHP_DATE_PERIOD_EXCLUDE_START_DATE);
4399 
4400         /* recurrrences */
4401         dpobj->recurrences = recurrences + dpobj->include_start_date;
4402 
4403         dpobj->initialized = 1;
4404 
4405         zend_restore_error_handling(&error_handling);
4406 }
4407 /* }}} */
4408 
4409 /* {{{ proto DatePeriod::getStartDate()
4410    Get start date.
4411 */
4412 PHP_METHOD(DatePeriod, getStartDate)
4413 {
4414         php_period_obj   *dpobj;
4415         php_date_obj     *dateobj;
4416 
4417         if (zend_parse_parameters_none() == FAILURE) {
4418                 return;
4419         }
4420 
4421         dpobj = Z_PHPPERIOD_P(getThis());
4422 
4423         php_date_instantiate(dpobj->start_ce, return_value);
4424         dateobj = Z_PHPDATE_P(return_value);
4425         dateobj->time = timelib_time_ctor();
4426         *dateobj->time = *dpobj->start;
4427         if (dpobj->start->tz_abbr) {
4428                 dateobj->time->tz_abbr = timelib_strdup(dpobj->start->tz_abbr);
4429         }
4430         if (dpobj->start->tz_info) {
4431                 dateobj->time->tz_info = dpobj->start->tz_info;
4432         }
4433 }
4434 /* }}} */
4435 
4436 /* {{{ proto DatePeriod::getEndDate()
4437    Get end date.
4438 */
4439 PHP_METHOD(DatePeriod, getEndDate)
4440 {
4441         php_period_obj   *dpobj;
4442         php_date_obj     *dateobj;
4443 
4444         if (zend_parse_parameters_none() == FAILURE) {
4445                 return;
4446         }
4447 
4448         dpobj = Z_PHPPERIOD_P(getThis());
4449 
4450         if (!dpobj->end) {
4451                 return;
4452         }
4453 
4454         php_date_instantiate(dpobj->start_ce, return_value);
4455         dateobj = Z_PHPDATE_P(return_value);
4456         dateobj->time = timelib_time_ctor();
4457         *dateobj->time = *dpobj->end;
4458         if (dpobj->end->tz_abbr) {
4459                 dateobj->time->tz_abbr = timelib_strdup(dpobj->end->tz_abbr);
4460         }
4461         if (dpobj->end->tz_info) {
4462                 dateobj->time->tz_info = dpobj->end->tz_info;
4463         }
4464 }
4465 /* }}} */
4466 
4467 /* {{{ proto DatePeriod::getDateInterval()
4468    Get date interval.
4469 */
4470 PHP_METHOD(DatePeriod, getDateInterval)
4471 {
4472         php_period_obj   *dpobj;
4473         php_interval_obj *diobj;
4474 
4475         if (zend_parse_parameters_none() == FAILURE) {
4476                 return;
4477         }
4478 
4479         dpobj = Z_PHPPERIOD_P(getThis());
4480 
4481         php_date_instantiate(date_ce_interval, return_value);
4482         diobj = Z_PHPINTERVAL_P(return_value);
4483         diobj->diff = timelib_rel_time_clone(dpobj->interval);
4484         diobj->initialized = 1;
4485 }
4486 /* }}} */
4487 
4488 static int check_id_allowed(char *id, zend_long what) /* {{{ */
4489 {
4490         if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA     && strncasecmp(id, "Africa/",      7) == 0) return 1;
4491         if (what & PHP_DATE_TIMEZONE_GROUP_AMERICA    && strncasecmp(id, "America/",     8) == 0) return 1;
4492         if (what & PHP_DATE_TIMEZONE_GROUP_ANTARCTICA && strncasecmp(id, "Antarctica/", 11) == 0) return 1;
4493         if (what & PHP_DATE_TIMEZONE_GROUP_ARCTIC     && strncasecmp(id, "Arctic/",      7) == 0) return 1;
4494         if (what & PHP_DATE_TIMEZONE_GROUP_ASIA       && strncasecmp(id, "Asia/",        5) == 0) return 1;
4495         if (what & PHP_DATE_TIMEZONE_GROUP_ATLANTIC   && strncasecmp(id, "Atlantic/",    9) == 0) return 1;
4496         if (what & PHP_DATE_TIMEZONE_GROUP_AUSTRALIA  && strncasecmp(id, "Australia/",  10) == 0) return 1;
4497         if (what & PHP_DATE_TIMEZONE_GROUP_EUROPE     && strncasecmp(id, "Europe/",      7) == 0) return 1;
4498         if (what & PHP_DATE_TIMEZONE_GROUP_INDIAN     && strncasecmp(id, "Indian/",      7) == 0) return 1;
4499         if (what & PHP_DATE_TIMEZONE_GROUP_PACIFIC    && strncasecmp(id, "Pacific/",     8) == 0) return 1;
4500         if (what & PHP_DATE_TIMEZONE_GROUP_UTC        && strncasecmp(id, "UTC",          3) == 0) return 1;
4501         return 0;
4502 } /* }}} */
4503 
4504 /* {{{ proto array timezone_identifiers_list([long what[, string country]])
4505    Returns numerically index array with all timezone identifiers.
4506 */
4507 PHP_FUNCTION(timezone_identifiers_list)
4508 {
4509         const timelib_tzdb             *tzdb;
4510         const timelib_tzdb_index_entry *table;
4511         int                             i, item_count;
4512         zend_long                            what = PHP_DATE_TIMEZONE_GROUP_ALL;
4513         char                           *option = NULL;
4514         size_t                             option_len = 0;
4515 
4516         if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &what, &option, &option_len) == FAILURE) {
4517                 RETURN_FALSE;
4518         }
4519 
4520         /* Extra validation */
4521         if (what == PHP_DATE_TIMEZONE_PER_COUNTRY && option_len != 2) {
4522                 php_error_docref(NULL, E_NOTICE, "A two-letter ISO 3166-1 compatible country code is expected");
4523                 RETURN_FALSE;
4524         }
4525 
4526         tzdb = DATE_TIMEZONEDB;
4527         item_count = tzdb->index_size;
4528         table = tzdb->index;
4529 
4530         array_init(return_value);
4531 
4532         for (i = 0; i < item_count; ++i) {
4533                 if (what == PHP_DATE_TIMEZONE_PER_COUNTRY) {
4534                         if (tzdb->data[table[i].pos + 5] == option[0] && tzdb->data[table[i].pos + 6] == option[1]) {
4535                                 add_next_index_string(return_value, table[i].id);
4536                         }
4537                 } else if (what == PHP_DATE_TIMEZONE_GROUP_ALL_W_BC || (check_id_allowed(table[i].id, what) && (tzdb->data[table[i].pos + 4] == '\1'))) {
4538                         add_next_index_string(return_value, table[i].id);
4539                 }
4540         };
4541 }
4542 /* }}} */
4543 
4544 /* {{{ proto array timezone_version_get()
4545    Returns the Olson database version number.
4546 */
4547 PHP_FUNCTION(timezone_version_get)
4548 {
4549         const timelib_tzdb *tzdb;
4550 
4551         tzdb = DATE_TIMEZONEDB;
4552         RETURN_STRING(tzdb->version);
4553 }
4554 /* }}} */
4555 
4556 /* {{{ proto array timezone_abbreviations_list()
4557    Returns associative array containing dst, offset and the timezone name
4558 */
4559 PHP_FUNCTION(timezone_abbreviations_list)
4560 {
4561         const timelib_tz_lookup_table *table, *entry;
4562         zval                          element, *abbr_array_p, abbr_array;
4563 
4564         table = timelib_timezone_abbreviations_list();
4565         array_init(return_value);
4566         entry = table;
4567 
4568         do {
4569                 array_init(&element);
4570                 add_assoc_bool_ex(&element, "dst", sizeof("dst") -1, entry->type);
4571                 add_assoc_long_ex(&element, "offset", sizeof("offset") - 1, entry->gmtoffset);
4572                 if (entry->full_tz_name) {
4573                         add_assoc_string_ex(&element, "timezone_id", sizeof("timezone_id") - 1, entry->full_tz_name);
4574                 } else {
4575                         add_assoc_null_ex(&element, "timezone_id", sizeof("timezone_id") - 1);
4576                 }
4577 
4578                 abbr_array_p = zend_hash_str_find(Z_ARRVAL_P(return_value), entry->name, strlen(entry->name));
4579                 if (!abbr_array_p) {
4580                         array_init(&abbr_array);
4581                         add_assoc_zval(return_value, entry->name, &abbr_array);
4582                 } else {
4583                         ZVAL_COPY_VALUE(&abbr_array, abbr_array_p);
4584                 }
4585                 add_next_index_zval(&abbr_array, &element);
4586                 entry++;
4587         } while (entry->name);
4588 }
4589 /* }}} */
4590 
4591 /* {{{ proto bool date_default_timezone_set(string timezone_identifier)
4592    Sets the default timezone used by all date/time functions in a script */
4593 PHP_FUNCTION(date_default_timezone_set)
4594 {
4595         char *zone;
4596         size_t   zone_len;
4597 
4598         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &zone, &zone_len) == FAILURE) {
4599                 RETURN_FALSE;
4600         }
4601         if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
4602                 php_error_docref(NULL, E_NOTICE, "Timezone ID '%s' is invalid", zone);
4603                 RETURN_FALSE;
4604         }
4605         if (DATEG(timezone)) {
4606                 efree(DATEG(timezone));
4607                 DATEG(timezone) = NULL;
4608         }
4609         DATEG(timezone) = estrndup(zone, zone_len);
4610         RETURN_TRUE;
4611 }
4612 /* }}} */
4613 
4614 /* {{{ proto string date_default_timezone_get()
4615    Gets the default timezone used by all date/time functions in a script */
4616 PHP_FUNCTION(date_default_timezone_get)
4617 {
4618         timelib_tzinfo *default_tz;
4619 
4620         default_tz = get_timezone_info();
4621         RETVAL_STRING(default_tz->name);
4622 }
4623 /* }}} */
4624 
4625 /* {{{ php_do_date_sunrise_sunset
4626  *  Common for date_sunrise() and date_sunset() functions
4627  */
4628 static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, int calc_sunset)
4629 {
4630         double latitude = 0.0, longitude = 0.0, zenith = 0.0, gmt_offset = 0, altitude;
4631         double h_rise, h_set, N;
4632         timelib_sll rise, set, transit;
4633         zend_long time, retformat = 0;
4634         int             rs;
4635         timelib_time   *t;
4636         timelib_tzinfo *tzi;
4637         zend_string    *retstr;
4638 
4639         if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ldddd", &time, &retformat, &latitude, &longitude, &zenith, &gmt_offset) == FAILURE) {
4640                 RETURN_FALSE;
4641         }
4642 
4643         switch (ZEND_NUM_ARGS()) {
4644                 case 1:
4645                         retformat = SUNFUNCS_RET_STRING;
4646                 case 2:
4647                         latitude = INI_FLT("date.default_latitude");
4648                 case 3:
4649                         longitude = INI_FLT("date.default_longitude");
4650                 case 4:
4651                         if (calc_sunset) {
4652                                 zenith = INI_FLT("date.sunset_zenith");
4653                         } else {
4654                                 zenith = INI_FLT("date.sunrise_zenith");
4655                         }
4656                 case 5:
4657                 case 6:
4658                         break;
4659                 default:
4660                         php_error_docref(NULL, E_WARNING, "invalid format");
4661                         RETURN_FALSE;
4662                         break;
4663         }
4664         if (retformat != SUNFUNCS_RET_TIMESTAMP &&
4665                 retformat != SUNFUNCS_RET_STRING &&
4666                 retformat != SUNFUNCS_RET_DOUBLE)
4667         {
4668                 php_error_docref(NULL, E_WARNING, "Wrong return format given, pick one of SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING or SUNFUNCS_RET_DOUBLE");
4669                 RETURN_FALSE;
4670         }
4671         altitude = 90 - zenith;
4672 
4673         /* Initialize time struct */
4674         t = timelib_time_ctor();
4675         tzi = get_timezone_info();
4676         t->tz_info = tzi;
4677         t->zone_type = TIMELIB_ZONETYPE_ID;
4678 
4679         if (ZEND_NUM_ARGS() <= 5) {
4680                 gmt_offset = timelib_get_current_offset(t) / 3600;
4681         }
4682 
4683         timelib_unixtime2local(t, time);
4684         rs = timelib_astro_rise_set_altitude(t, longitude, latitude, altitude, 1, &h_rise, &h_set, &rise, &set, &transit);
4685         timelib_time_dtor(t);
4686 
4687         if (rs != 0) {
4688                 RETURN_FALSE;
4689         }
4690 
4691         if (retformat == SUNFUNCS_RET_TIMESTAMP) {
4692                 RETURN_LONG(calc_sunset ? set : rise);
4693         }
4694         N = (calc_sunset ? h_set : h_rise) + gmt_offset;
4695 
4696         if (N > 24 || N < 0) {
4697                 N -= floor(N / 24) * 24;
4698         }
4699 
4700         switch (retformat) {
4701                 case SUNFUNCS_RET_STRING:
4702                         retstr = strpprintf(0, "%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
4703                         RETURN_NEW_STR(retstr);
4704                         break;
4705                 case SUNFUNCS_RET_DOUBLE:
4706                         RETURN_DOUBLE(N);
4707                         break;
4708         }
4709 }
4710 /* }}} */
4711 
4712 /* {{{ proto mixed date_sunrise(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])
4713    Returns time of sunrise for a given day and location */
4714 PHP_FUNCTION(date_sunrise)
4715 {
4716         php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
4717 }
4718 /* }}} */
4719 
4720 /* {{{ proto mixed date_sunset(mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]])
4721    Returns time of sunset for a given day and location */
4722 PHP_FUNCTION(date_sunset)
4723 {
4724         php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
4725 }
4726 /* }}} */
4727 
4728 /* {{{ proto array date_sun_info(long time, float latitude, float longitude)
4729    Returns an array with information about sun set/rise and twilight begin/end */
4730 PHP_FUNCTION(date_sun_info)
4731 {
4732         zend_long       time;
4733         double          latitude, longitude;
4734         timelib_time   *t, *t2;
4735         timelib_tzinfo *tzi;
4736         int             rs;
4737         timelib_sll     rise, set, transit;
4738         int             dummy;
4739         double          ddummy;
4740 
4741         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ldd", &time, &latitude, &longitude) == FAILURE) {
4742                 RETURN_FALSE;
4743         }
4744         /* Initialize time struct */
4745         t = timelib_time_ctor();
4746         tzi = get_timezone_info();
4747         t->tz_info = tzi;
4748         t->zone_type = TIMELIB_ZONETYPE_ID;
4749         timelib_unixtime2local(t, time);
4750 
4751         /* Setup */
4752         t2 = timelib_time_ctor();
4753         array_init(return_value);
4754 
4755         /* Get sun up/down and transit */
4756         rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -35.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
4757         switch (rs) {
4758                 case -1: /* always below */
4759                         add_assoc_bool(return_value, "sunrise", 0);
4760                         add_assoc_bool(return_value, "sunset", 0);
4761                         break;
4762                 case 1: /* always above */
4763                         add_assoc_bool(return_value, "sunrise", 1);
4764                         add_assoc_bool(return_value, "sunset", 1);
4765                         break;
4766                 default:
4767                         t2->sse = rise;
4768                         add_assoc_long(return_value, "sunrise", timelib_date_to_int(t2, &dummy));
4769                         t2->sse = set;
4770                         add_assoc_long(return_value, "sunset", timelib_date_to_int(t2, &dummy));
4771         }
4772         t2->sse = transit;
4773         add_assoc_long(return_value, "transit", timelib_date_to_int(t2, &dummy));
4774 
4775         /* Get civil twilight */
4776         rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -6.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4777         switch (rs) {
4778                 case -1: /* always below */
4779                         add_assoc_bool(return_value, "civil_twilight_begin", 0);
4780                         add_assoc_bool(return_value, "civil_twilight_end", 0);
4781                         break;
4782                 case 1: /* always above */
4783                         add_assoc_bool(return_value, "civil_twilight_begin", 1);
4784                         add_assoc_bool(return_value, "civil_twilight_end", 1);
4785                         break;
4786                 default:
4787                         t2->sse = rise;
4788                         add_assoc_long(return_value, "civil_twilight_begin", timelib_date_to_int(t2, &dummy));
4789                         t2->sse = set;
4790                         add_assoc_long(return_value, "civil_twilight_end", timelib_date_to_int(t2, &dummy));
4791         }
4792 
4793         /* Get nautical twilight */
4794         rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -12.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4795         switch (rs) {
4796                 case -1: /* always below */
4797                         add_assoc_bool(return_value, "nautical_twilight_begin", 0);
4798                         add_assoc_bool(return_value, "nautical_twilight_end", 0);
4799                         break;
4800                 case 1: /* always above */
4801                         add_assoc_bool(return_value, "nautical_twilight_begin", 1);
4802                         add_assoc_bool(return_value, "nautical_twilight_end", 1);
4803                         break;
4804                 default:
4805                         t2->sse = rise;
4806                         add_assoc_long(return_value, "nautical_twilight_begin", timelib_date_to_int(t2, &dummy));
4807                         t2->sse = set;
4808                         add_assoc_long(return_value, "nautical_twilight_end", timelib_date_to_int(t2, &dummy));
4809         }
4810 
4811         /* Get astronomical twilight */
4812         rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -18.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
4813         switch (rs) {
4814                 case -1: /* always below */
4815                         add_assoc_bool(return_value, "astronomical_twilight_begin", 0);
4816                         add_assoc_bool(return_value, "astronomical_twilight_end", 0);
4817                         break;
4818                 case 1: /* always above */
4819                         add_assoc_bool(return_value, "astronomical_twilight_begin", 1);
4820                         add_assoc_bool(return_value, "astronomical_twilight_end", 1);
4821                         break;
4822                 default:
4823                         t2->sse = rise;
4824                         add_assoc_long(return_value, "astronomical_twilight_begin", timelib_date_to_int(t2, &dummy));
4825                         t2->sse = set;
4826                         add_assoc_long(return_value, "astronomical_twilight_end", timelib_date_to_int(t2, &dummy));
4827         }
4828         timelib_time_dtor(t);
4829         timelib_time_dtor(t2);
4830 }
4831 /* }}} */
4832 
4833 static HashTable *date_object_get_gc_period(zval *object, zval **table, int *n) /* {{{ */
4834 {
4835         *table = NULL;
4836         *n = 0;
4837         return zend_std_get_properties(object);
4838 } /* }}} */
4839 
4840 static HashTable *date_object_get_properties_period(zval *object) /* {{{ */
4841 {
4842         HashTable               *props;
4843         zval                     zv;
4844         php_period_obj  *period_obj;
4845 
4846         period_obj = Z_PHPPERIOD_P(object);
4847 
4848         props = zend_std_get_properties(object);
4849 
4850         if (!period_obj->start || GC_G(gc_active)) {
4851                 return props;
4852         }
4853 
4854         if (period_obj->start) {
4855                 php_date_obj *date_obj;
4856                 object_init_ex(&zv, date_ce_date);
4857                 date_obj = Z_PHPDATE_P(&zv);
4858                 date_obj->time = timelib_time_clone(period_obj->start);
4859         } else {
4860                 ZVAL_NULL(&zv);
4861         }
4862         zend_hash_str_update(props, "start", sizeof("start")-1, &zv);
4863 
4864         if (period_obj->current) {
4865                 php_date_obj *date_obj;
4866                 object_init_ex(&zv, date_ce_date);
4867                 date_obj = Z_PHPDATE_P(&zv);
4868                 date_obj->time = timelib_time_clone(period_obj->current);
4869         } else {
4870                 ZVAL_NULL(&zv);
4871         }
4872         zend_hash_str_update(props, "current", sizeof("current")-1, &zv);
4873 
4874         if (period_obj->end) {
4875                 php_date_obj *date_obj;
4876                 object_init_ex(&zv, date_ce_date);
4877                 date_obj = Z_PHPDATE_P(&zv);
4878                 date_obj->time = timelib_time_clone(period_obj->end);
4879         } else {
4880                 ZVAL_NULL(&zv);
4881         }
4882         zend_hash_str_update(props, "end", sizeof("end")-1, &zv);
4883 
4884         if (period_obj->interval) {
4885                 php_interval_obj *interval_obj;
4886                 object_init_ex(&zv, date_ce_interval);
4887                 interval_obj = Z_PHPINTERVAL_P(&zv);
4888                 interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
4889                 interval_obj->initialized = 1;
4890         } else {
4891                 ZVAL_NULL(&zv);
4892         }
4893         zend_hash_str_update(props, "interval", sizeof("interval")-1, &zv);
4894 
4895         /* converted to larger type (int->long); must check when unserializing */
4896         ZVAL_LONG(&zv, (zend_long) period_obj->recurrences);
4897         zend_hash_str_update(props, "recurrences", sizeof("recurrences")-1, &zv);
4898 
4899         ZVAL_BOOL(&zv, period_obj->include_start_date);
4900         zend_hash_str_update(props, "include_start_date", sizeof("include_start_date")-1, &zv);
4901 
4902         return props;
4903 } /* }}} */
4904 
4905 static int php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht) /* {{{ */
4906 {
4907         zval *ht_entry;
4908 
4909         /* this function does no rollback on error */
4910 
4911         ht_entry = zend_hash_str_find(myht, "start", sizeof("start")-1);
4912         if (ht_entry) {
4913                 if (Z_TYPE_P(ht_entry) == IS_OBJECT && Z_OBJCE_P(ht_entry) == date_ce_date) {
4914                         php_date_obj *date_obj;
4915                         date_obj = Z_PHPDATE_P(ht_entry);
4916                         period_obj->start = timelib_time_clone(date_obj->time);
4917                         period_obj->start_ce = Z_OBJCE_P(ht_entry);
4918                 } else if (Z_TYPE_P(ht_entry) != IS_NULL) {
4919                         return 0;
4920                 }
4921         } else {
4922                 return 0;
4923         }
4924 
4925         ht_entry = zend_hash_str_find(myht, "end", sizeof("end")-1);
4926         if (ht_entry) {
4927                 if (Z_TYPE_P(ht_entry) == IS_OBJECT && Z_OBJCE_P(ht_entry) == date_ce_date) {
4928                         php_date_obj *date_obj;
4929                         date_obj = Z_PHPDATE_P(ht_entry);
4930                         period_obj->end = timelib_time_clone(date_obj->time);
4931                 } else if (Z_TYPE_P(ht_entry) != IS_NULL) {
4932                         return 0;
4933                 }
4934         } else {
4935                 return 0;
4936         }
4937 
4938         ht_entry = zend_hash_str_find(myht, "current", sizeof("current")-1);
4939         if (ht_entry) {
4940                 if (Z_TYPE_P(ht_entry) == IS_OBJECT && Z_OBJCE_P(ht_entry) == date_ce_date) {
4941                         php_date_obj *date_obj;
4942                         date_obj = Z_PHPDATE_P(ht_entry);
4943                         period_obj->current = timelib_time_clone(date_obj->time);
4944                 } else if (Z_TYPE_P(ht_entry) != IS_NULL)  {
4945                         return 0;
4946                 }
4947         } else {
4948                 return 0;
4949         }
4950 
4951         ht_entry = zend_hash_str_find(myht, "interval", sizeof("interval")-1);
4952         if (ht_entry) {
4953                 if (Z_TYPE_P(ht_entry) == IS_OBJECT && Z_OBJCE_P(ht_entry) == date_ce_interval) {
4954                         php_interval_obj *interval_obj;
4955                         interval_obj = Z_PHPINTERVAL_P(ht_entry);
4956                         period_obj->interval = timelib_rel_time_clone(interval_obj->diff);
4957                 } else { /* interval is required */
4958                         return 0;
4959                 }
4960         } else {
4961                 return 0;
4962         }
4963 
4964         ht_entry = zend_hash_str_find(myht, "recurrences", sizeof("recurrences")-1);
4965         if (ht_entry &&
4966                         Z_TYPE_P(ht_entry) == IS_LONG && Z_LVAL_P(ht_entry) >= 0 && Z_LVAL_P(ht_entry) <= INT_MAX) {
4967                 period_obj->recurrences = Z_LVAL_P(ht_entry);
4968         } else {
4969                 return 0;
4970         }
4971 
4972         ht_entry = zend_hash_str_find(myht, "include_start_date", sizeof("include_start_date")-1);
4973         if (ht_entry &&
4974                         (Z_TYPE_P(ht_entry) == IS_FALSE || Z_TYPE_P(ht_entry) == IS_TRUE)) {
4975                 period_obj->include_start_date = (Z_TYPE_P(ht_entry) == IS_TRUE);
4976         } else {
4977                 return 0;
4978         }
4979 
4980         period_obj->initialized = 1;
4981 
4982         return 1;
4983 } /* }}} */
4984 
4985 /* {{{ proto DatePeriod::__set_state()
4986 */
4987 PHP_METHOD(DatePeriod, __set_state)
4988 {
4989         php_period_obj   *period_obj;
4990         zval             *array;
4991         HashTable        *myht;
4992 
4993         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) {
4994                 RETURN_FALSE;
4995         }
4996 
4997         myht = Z_ARRVAL_P(array);
4998 
4999         object_init_ex(return_value, date_ce_period);
5000         period_obj = Z_PHPPERIOD_P(return_value);
5001         if (!php_date_period_initialize_from_hash(period_obj, myht)) {
5002                 php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
5003         }
5004 }
5005 /* }}} */
5006 
5007 /* {{{ proto DatePeriod::__wakeup()
5008 */
5009 PHP_METHOD(DatePeriod, __wakeup)
5010 {
5011         zval             *object = getThis();
5012         php_period_obj   *period_obj;
5013         HashTable        *myht;
5014 
5015         period_obj = Z_PHPPERIOD_P(object);
5016 
5017         myht = Z_OBJPROP_P(object);
5018 
5019         if (!php_date_period_initialize_from_hash(period_obj, myht)) {
5020                 php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
5021         }
5022 }
5023 /* }}} */
5024 
5025 /* {{{ date_period_read_property */
5026 static zval *date_period_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv)
5027 {
5028         zval *zv;
5029         if (type != BP_VAR_IS && type != BP_VAR_R) {
5030                 php_error_docref(NULL, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported");
5031         }
5032 
5033         Z_OBJPROP_P(object); /* build properties hash table */
5034 
5035         zv = std_object_handlers.read_property(object, member, type, cache_slot, rv);
5036         if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJ_HANDLER_P(zv, clone_obj)) {
5037                 /* defensive copy */
5038                 ZVAL_OBJ(zv, Z_OBJ_HANDLER_P(zv, clone_obj)(zv));
5039         }
5040 
5041         return zv;
5042 }
5043 /* }}} */
5044 
5045 /* {{{ date_period_write_property */
5046 static void date_period_write_property(zval *object, zval *member, zval *value, void **cache_slot)
5047 {
5048         php_error_docref(NULL, E_ERROR, "Writing to DatePeriod properties is unsupported");
5049 }
5050 /* }}} */
5051 
5052 /*
5053  * Local variables:
5054  * tab-width: 4
5055  * c-basic-offset: 4
5056  * End:
5057  * vim600: fdm=marker
5058  * vim: noet sw=4 ts=4
5059  */

/* [<][>][^][v][top][bottom][index][help] */