[security][CVE-2022-27809] Builtins should always take int64_t, not int
[hiphop-php.git] / hphp / runtime / ext / datetime / ext_datetime.cpp
blobd38c4e81c16bb21c91cc3b17096b21d49a343b1b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/datetime/ext_datetime.h"
20 #include "hphp/runtime/base/comparisons.h"
21 #include "hphp/runtime/base/ini-setting.h"
22 #include "hphp/runtime/vm/native-data.h"
23 #include "hphp/runtime/vm/native-prop-handler.h"
24 #include "hphp/system/systemlib.h"
26 namespace HPHP {
28 static int check_id_allowed(const String& id, long bf) {
29 if (bf & DateTimeZoneData::AFRICA && id.find("Africa/") == 0) return 1;
30 if (bf & DateTimeZoneData::AMERICA && id.find("America/") == 0) return 1;
31 if (bf & DateTimeZoneData::ANTARCTICA && id.find("Antarctica/") == 0)
32 return 1;
33 if (bf & DateTimeZoneData::ARCTIC && id.find("Arctic/") == 0) return 1;
34 if (bf & DateTimeZoneData::ASIA && id.find("Asia/") == 0) return 1;
35 if (bf & DateTimeZoneData::ATLANTIC && id.find("Atlantic/") == 0) return 1;
36 if (bf & DateTimeZoneData::AUSTRALIA && id.find("Australia/") == 0) return 1;
37 if (bf & DateTimeZoneData::EUROPE && id.find("Europe/") == 0) return 1;
38 if (bf & DateTimeZoneData::INDIAN && id.find("Indian/") == 0) return 1;
39 if (bf & DateTimeZoneData::PACIFIC && id.find("Pacific/") == 0) return 1;
40 if (bf & DateTimeZoneData::UTC && id.find("UTC") == 0) return 1;
41 return 0;
44 const StaticString
45 s_formatOffset("P"),
46 s_formatID("e"),
47 s_formatAbbr("T");
49 static String zone_type_to_string(int zoneType, req::ptr<DateTime> dt) {
50 switch (zoneType) {
51 case TIMELIB_ZONETYPE_OFFSET:
52 return dt->toString(s_formatOffset);
53 case TIMELIB_ZONETYPE_ID:
54 return dt->toString(s_formatID);
55 case TIMELIB_ZONETYPE_ABBR:
56 return dt->toString(s_formatAbbr);
59 always_assert(!"Bad zone type");
62 struct DateGlobals {
63 double default_latitude;
64 double default_longitude;
65 double sunset_zenith;
66 double sunrise_zenith;
68 RDS_LOCAL(DateGlobals, s_date_globals);
70 #define IMPLEMENT_GET_CLASS(cls) \
71 Class* cls::getClass() { \
72 if (s_class == nullptr) { \
73 s_class = Class::lookup(s_className.get()); \
74 assertx(s_class); \
75 } \
76 return s_class; \
77 } \
79 ///////////////////////////////////////////////////////////////////////////////
80 // constants
82 static const StaticString
83 s_data("data"),
84 s_getTimestamp("getTimestamp"),
85 s_DateTimeInterface("DateTimeInterface"),
86 s_DateTimeZone("DateTimeZone"),
87 s_DateInterval("DateInterval"),
88 s_DateTime("DateTime");
90 ///////////////////////////////////////////////////////////////////////////////
92 namespace {
94 void raise_argument_warning(const char* func,
95 int param,
96 const StaticString& expected,
97 const Object& given) {
98 raise_warning("%s() expects parameter %d to be %s, %s given",
99 func, param, expected.c_str(), given.get()->classname_cstr());
102 DateTimeData* getDateTimeData(ObjectData* this_) {
103 auto const data = Native::data<DateTimeData>(this_);
104 if (data->m_dt) return data;
105 SystemLib::throwInvalidArgumentExceptionObject(
106 "Use before DateTime::__construct() called"
110 DateTimeData* getDateTimeData(const Object& this_) {
111 return getDateTimeData(this_.get());
114 DateTimeZoneData* getDateTimeZoneData(ObjectData* this_) {
115 auto const data = Native::data<DateTimeZoneData>(this_);
116 if (data->m_tz) return data;
117 SystemLib::throwInvalidArgumentExceptionObject(
118 "Use before DateTimeZone::__construct() called"
122 DateIntervalData* getDateIntervalData(ObjectData* this_) {
123 auto const data = Native::data<DateIntervalData>(this_);
124 if (data->m_di) return data;
125 SystemLib::throwInvalidArgumentExceptionObject(
126 "Use before DateInterval::__construct() called"
132 ///////////////////////////////////////////////////////////////////////////////
133 // DateTime
135 Class* DateTimeData::s_class = nullptr;
136 const StaticString DateTimeData::s_className("DateTime");
138 void HHVM_METHOD(DateTime, __construct,
139 const String& time /*= "now"*/,
140 const Variant& timezone /*= uninit_variant*/) {
141 DateTimeData* data = Native::data<DateTimeData>(this_);
142 auto tz = TimeZone::Current();
143 if (!timezone.isNull()) {
144 const Object& obj_timezone = timezone.toObject();
145 tz = DateTimeZoneData::unwrap(obj_timezone);
146 if (!tz) throw_invalid_object_type(obj_timezone);
148 data->m_dt = req::make<DateTime>(TimeStamp::Current(), tz);
149 if (!time.empty()) {
150 data->m_dt->fromString(time, tz);
151 } else if (!timezone.isNull()) {
152 // We still have to tell the underlying DateTime the timezone in case they
153 // call setTimestamp or something else later
154 data->m_dt->setTimezone(tz);
158 Variant HHVM_STATIC_METHOD(DateTime, createFromFormat,
159 const String& format,
160 const String& time,
161 const Variant& timezone /*= uninit_variant */) {
162 auto tz = TimeZone::Current();
163 if (!timezone.isNull()) {
164 const Object& obj_timezone = timezone.toObject();
165 if (!obj_timezone.instanceof(s_DateTimeZone)) {
166 raise_argument_warning("DateTime::createFromFormat", 3, s_DateTimeZone,
167 obj_timezone);
168 return false;
170 tz = DateTimeZoneData::unwrap(obj_timezone);
172 Object obj{DateTimeData::getClass()};
173 DateTimeData* data = Native::data<DateTimeData>(obj);
174 const auto curr = (format.find("!") != String::npos) ? 0 : f_time() ;
175 data->m_dt = req::make<DateTime>(curr, false);
176 if (!data->m_dt->fromString(time, tz, format.data(), false)) {
177 return false;
180 return obj;
183 Variant HHVM_METHOD(DateTime, diff,
184 const Variant& datetime2,
185 const Variant& absolute) {
186 auto const data = getDateTimeData(this_);
187 const Object obj_datetime2 = datetime2.toObject();
188 if (!obj_datetime2.instanceof(s_DateTimeInterface)) {
189 raise_argument_warning("DateTime::diff", 1, s_DateTimeInterface,
190 obj_datetime2);
191 return false;
193 auto dt = DateTimeData::unwrap(obj_datetime2);
194 return DateIntervalData::wrap(data->m_dt->diff(dt, absolute.toBoolean()));
197 String HHVM_METHOD(DateTime, format,
198 const Variant& format) {
199 auto const data = getDateTimeData(this_);
200 return data->format(format.toString());
203 static const StaticString s_warning_count("warning_count");
204 static const StaticString s_warnings("warnings");
205 static const StaticString s_error_count("error_count");
206 static const StaticString s_errors("errors");
208 Array HHVM_STATIC_METHOD(DateTime, getLastErrors) {
209 Array errors = DateTime::getLastErrors();
210 Array warnings = DateTime::getLastWarnings();
211 DictInit ret(4);
213 ret.set(s_warning_count, warnings.size());
214 ret.set(s_warnings, warnings);
215 ret.set(s_error_count, errors.size());
216 ret.set(s_errors, errors);
218 return ret.toArray();
221 int64_t HHVM_METHOD(DateTime, getOffset) {
222 auto const data = getDateTimeData(this_);
223 return data->m_dt->offset();
226 int64_t HHVM_METHOD(DateTime, getTimestamp) {
227 auto const data = getDateTimeData(this_);
228 return data->getTimestamp();
231 Variant HHVM_METHOD(DateTime, getTimezone) {
232 auto const data = getDateTimeData(this_);
233 req::ptr<TimeZone> tz = data->m_dt->timezone();
234 if (tz->isValid()) {
235 return DateTimeZoneData::wrap(tz);
237 return false;
240 Variant HHVM_METHOD(DateTime, modify,
241 const String& modify) {
242 auto const data = getDateTimeData(this_);
243 if (!data->m_dt->modify(modify)) {
244 return false;
246 return Object(this_);
249 Object HHVM_METHOD(DateTime, setDate,
250 int64_t year,
251 int64_t month,
252 int64_t day) {
253 auto const data = getDateTimeData(this_);
254 data->m_dt->setDate(year, month, day);
255 return Object(this_);
258 Object HHVM_METHOD(DateTime, setISODate,
259 int64_t year,
260 int64_t week,
261 int64_t day /*= 1*/) {
262 auto const data = getDateTimeData(this_);
263 data->m_dt->setISODate(year, week, day);
264 return Object(this_);
267 Object HHVM_METHOD(DateTime, setTime,
268 int64_t hour,
269 int64_t minute,
270 int64_t second /*= 0*/) {
271 auto const data = getDateTimeData(this_);
272 data->m_dt->setTime(hour, minute, second);
273 return Object(this_);
276 Object HHVM_METHOD(DateTime, setTimestamp,
277 int64_t unixtimestamp) {
278 auto const data = getDateTimeData(this_);
279 data->m_dt->fromTimeStamp(unixtimestamp, false);
280 return Object(this_);
283 Variant HHVM_METHOD(DateTime, setTimezone,
284 const Object& timezone) {
285 auto const data = getDateTimeData(this_);
286 if (!timezone.instanceof(s_DateTimeZone)) {
287 raise_argument_warning("DateTime::setTimezone", 1, s_DateTimeZone,
288 timezone);
289 return false;
291 data->m_dt->setTimezone(DateTimeZoneData::unwrap(timezone));
292 return Object(this_);
295 Variant HHVM_METHOD(DateTime, add,
296 const Object& interval) {
297 auto const data = getDateTimeData(this_);
298 if (!interval.instanceof(s_DateInterval)) {
299 raise_argument_warning("DateTime::add", 1, s_DateInterval, interval);
300 return false;
302 auto di = DateIntervalData::unwrap(interval);
303 data->m_dt->add(di);
304 return Object(this_);
307 Variant HHVM_METHOD(DateTime, sub,
308 const Object& interval) {
309 auto const data = getDateTimeData(this_);
310 if (!interval.instanceof(s_DateInterval)) {
311 raise_argument_warning("DateTime::sub", 1, s_DateInterval, interval);
312 return false;
314 auto di = DateIntervalData::unwrap(interval);
315 data->m_dt->sub(di);
316 return Object(this_);
319 const StaticString
320 s_date("date"),
321 s_timezone_type("timezone_type"),
322 s_timezone("timezone"),
323 s_ISOformat("Y-m-d H:i:s.u");
325 Array HHVM_METHOD(DateTime, __sleep) {
326 auto const data = getDateTimeData(this_);
328 auto const formatted = data->format(s_ISOformat);
329 this_->setProp(nullptr, s_date.get(), formatted.asTypedValue());
330 int zoneType = data->m_dt->zoneType();
331 this_->setProp(nullptr, s_timezone_type.get(),
332 make_tv<KindOfInt64>(zoneType));
333 auto const timezone = zone_type_to_string(zoneType, data->m_dt);
334 this_->setProp(nullptr, s_timezone.get(), timezone.asTypedValue());
335 return make_vec_array(s_date, s_timezone_type, s_timezone);
338 void HHVM_METHOD(DateTime, __wakeup) {
339 Object dtz_obj{DateTimeZoneData::getClass()};
340 HHVM_MN(DateTimeZone, __construct)(dtz_obj.get(),
341 this_->o_get(s_timezone).toString());
342 HHVM_MN(DateTime, __construct)(this_, this_->o_get(s_date).toString(),
343 std::move(dtz_obj));
345 // cleanup
346 Class* cls = this_->getVMClass();
347 this_->unsetProp(cls, s_date.get());
348 this_->unsetProp(cls, s_timezone_type.get());
349 this_->unsetProp(cls, s_timezone.get());
352 Array HHVM_METHOD(DateTime, __debugInfo) {
353 auto const data = getDateTimeData(this_);
354 return data->getDebugInfo();
357 Array DateTimeData::getDebugInfo() const {
358 assertx(m_dt);
359 return make_dict_array(
360 s_date, format(s_ISOformat),
361 s_timezone_type, m_dt->zoneType(),
362 s_timezone, zone_type_to_string(m_dt->zoneType(), m_dt)
366 ///////////////////////////////////////////////////////////////////////////////
367 // DateTime helpers
369 int64_t DateTimeData::getTimestamp(const Object& obj) {
370 if (LIKELY(obj.instanceof(getClass()))) {
371 return getDateTimeData(obj)->getTimestamp();
373 assertx(obj->instanceof(SystemLib::s_DateTimeInterfaceClass));
374 Variant result = obj->o_invoke(s_getTimestamp, Array::CreateVec());
375 return result.toInt64();
378 int64_t DateTimeData::getTimestamp(const ObjectData* od) {
379 return getTimestamp(Object(const_cast<ObjectData*>(od)));
382 int DateTimeData::compare(const Object& left, const Object &right) {
383 if (LIKELY(left.instanceof(getClass()) && right.instanceof(getClass()))) {
384 auto const leftData = getDateTimeData(left);
385 auto const rightData = getDateTimeData(right);
386 return leftData->m_dt->compare(rightData->m_dt);
387 } else {
388 auto leftTime = getTimestamp(left);
389 auto rightTime = getTimestamp(right);
390 if (leftTime < rightTime) {
391 return -1;
392 } else if (leftTime > rightTime) {
393 return 1;
394 } else {
395 return 0;
400 int DateTimeData::compare(const ObjectData* left, const ObjectData* right) {
401 return compare(Object(const_cast<ObjectData*>(left)),
402 Object(const_cast<ObjectData*>(right)));
405 Object DateTimeData::wrap(req::ptr<DateTime> dt) {
406 Object obj{getClass()};
407 DateTimeData* data = Native::data<DateTimeData>(obj);
408 data->m_dt = dt;
409 return obj;
412 req::ptr<DateTime> DateTimeData::unwrap(const Object& datetime) {
413 if (LIKELY(datetime.instanceof(getClass()))) {
414 DateTimeData* data = Native::data<DateTimeData>(datetime);
415 return data->m_dt;
417 if (datetime->instanceof(SystemLib::s_DateTimeImmutableClass)) {
418 auto rval = datetime->getProp(
419 SystemLib::s_DateTimeImmutableClass,
420 s_data.get()
422 assertx(rval.is_set() && type(rval) == KindOfObject);
423 Object impl(rval.val().pobj);
424 return unwrap(impl);
426 return req::ptr<DateTime>();
429 IMPLEMENT_GET_CLASS(DateTimeData)
431 ///////////////////////////////////////////////////////////////////////////////
432 // DateTimeZone
434 Class* DateTimeZoneData::s_class = nullptr;
435 const StaticString DateTimeZoneData::s_className("DateTimeZone");
437 void HHVM_METHOD(DateTimeZone, __construct,
438 const String& timezone) {
439 DateTimeZoneData* data = Native::data<DateTimeZoneData>(this_);
440 data->m_tz = req::make<TimeZone>(timezone);
441 if (!data->m_tz->isValid()) {
442 std::string msg = "DateTimeZone::__construct(): Unknown or bad timezone (";
443 msg += timezone.data();
444 msg += ")";
445 SystemLib::throwExceptionObject(msg);
449 Array HHVM_METHOD(DateTimeZone, getLocation) {
450 auto const data = getDateTimeZoneData(this_);
451 return data->m_tz->getLocation();
454 String HHVM_METHOD(DateTimeZone, getName) {
455 auto const data = getDateTimeZoneData(this_);
456 return data->getName();
459 Variant HHVM_METHOD(DateTimeZone, getOffset,
460 const Object& datetime) {
461 auto const data = getDateTimeZoneData(this_);
462 bool error;
463 if (!datetime.instanceof(s_DateTime)) {
464 raise_argument_warning("DateTimeZone::getOffset", 1, s_DateTime, datetime);
465 return false;
467 auto dt = DateTimeData::unwrap(datetime);
468 int64_t ts = dt->toTimeStamp(error);
469 return data->m_tz->offset(ts);
472 TypedValue HHVM_METHOD(DateTimeZone, getTransitions,
473 int64_t timestamp_begin, /*=k_PHP_INT_MIN*/
474 int64_t timestamp_end /*=k_PHP_INT_MAX*/) {
475 auto const data = getDateTimeZoneData(this_);
476 auto result = data->m_tz->transitions(timestamp_begin, timestamp_end);
477 if (result.isNull()) {
478 return make_tv<KindOfBoolean>(false);
480 return tvReturn(std::move(result));
483 Array HHVM_STATIC_METHOD(DateTimeZone, listAbbreviations) {
484 return TimeZone::GetAbbreviations();
487 Variant HHVM_STATIC_METHOD(DateTimeZone, listIdentifiers,
488 int64_t what,
489 const String& country) {
490 // This is the same check that PHP5 performs, no validation needed.
491 // See ext/date/php_date.c lines 4496-4499
492 if (what == DateTimeZoneData::PER_COUNTRY && country.length() != 2) {
493 raise_notice("A two-letter ISO 3166-1 compatible country code is expected");
494 return false;
497 const timelib_tzdb *tzdb = timezone_get_tzdb();
498 int item_count = tzdb->index_size;
499 const timelib_tzdb_index_entry *table = tzdb->index;
501 Array ret = Array::CreateVec();
502 for (int i = 0; i < item_count; ++i) {
503 // This string is what PHP considers as "data" or "info" which is basically
504 // the string of "PHP1xx" where xx is country code that uses this timezone.
505 // When country code is unknown or not in use anymore, ?? is used instead.
506 // There is no known better way to extract this information out.
507 const char* infoString = (const char*)&tzdb->data[table[i].pos];
508 String countryCode = String(&infoString[5], 2, CopyString);
509 if ((what == DateTimeZoneData::PER_COUNTRY && equal(country.get(), countryCode.get()))
510 || what == DateTimeZoneData::ALL_WITH_BC
511 || (check_id_allowed(table[i].id, what)
512 && tzdb->data[table[i].pos + 4] == '\1')) {
514 ret.append(String(table[i].id, CopyString));
517 return ret;
520 ///////////////////////////////////////////////////////////////////////////////
521 // DateTimeZone helpers
523 Object DateTimeZoneData::wrap(req::ptr<TimeZone> tz) {
524 Object obj{getClass()};
525 DateTimeZoneData* data = Native::data<DateTimeZoneData>(obj);
526 data->m_tz = tz;
527 return obj;
530 req::ptr<TimeZone> DateTimeZoneData::unwrap(const Object& timezone) {
531 if (timezone.instanceof(getClass())) {
532 DateTimeZoneData* data = Native::data<DateTimeZoneData>(timezone);
533 return data->m_tz;
535 return req::ptr<TimeZone>();
538 IMPLEMENT_GET_CLASS(DateTimeZoneData)
540 Array HHVM_METHOD(DateTimeZone, __debugInfo) {
541 auto const data = getDateTimeZoneData(this_);
542 return data->getDebugInfo();
545 Array DateTimeZoneData::getDebugInfo() const {
546 assertx(m_tz);
547 return make_dict_array(
548 s_timezone_type, m_tz->type(),
549 s_timezone, m_tz->name()
553 ///////////////////////////////////////////////////////////////////////////////
554 // DateInterval
556 Class* DateIntervalData::s_class = nullptr;
557 const StaticString DateIntervalData::s_className("DateInterval");
559 void HHVM_METHOD(DateInterval, __construct,
560 const String& interval_spec) {
561 DateIntervalData* data = Native::data<DateIntervalData>(this_);
562 data->m_di = req::make<DateInterval>(interval_spec);
563 if (!data->m_di->isValid()) {
564 std::string msg = "DateInterval::__construct: Invalid interval (";
565 msg += interval_spec.data();
566 msg += ")";
567 SystemLib::throwExceptionObject(msg);
571 template <int64_t (DateInterval::*get)() const>
572 static Variant get_prop(const Object& this_) {
573 return (getDateIntervalData(this_.get())->m_di.get()->*get)();
575 static Variant is_inverted(const Object& this_) {
576 return (int)getDateIntervalData(this_.get())->m_di->isInverted();
578 static Variant get_total_days(const Object& this_) {
579 auto di = getDateIntervalData(this_.get())->m_di;
580 if (!di->haveTotalDays()) return false;
581 return di->getTotalDays();
584 template <typename T> using setter_t = void (DateInterval::*)(T);
585 template <typename T> using cast_t = T (Variant::*)() const;
586 template <typename T> static void set_prop_impl(
587 const Object& this_, const Variant& value, setter_t<T> set, cast_t<T> cast) {
588 (getDateIntervalData(this_.get())->m_di.get()->*set)((value.*cast)());
590 template <setter_t<bool> set>
591 static void set_prop(const Object& this_, const Variant& value) {
592 set_prop_impl(this_, value, set, &Variant::toBoolean);
594 template <setter_t<int64_t> set>
595 static void set_prop(const Object& this_, const Variant& value) {
596 set_prop_impl(this_, value, set, &Variant::toInt64);
599 static Native::PropAccessor date_interval_properties[] = {
600 { "y", get_prop<&DateInterval::getYears>, set_prop<&DateInterval::setYears> },
601 { "m", get_prop<&DateInterval::getMonths>, set_prop<&DateInterval::setMonths> },
602 { "d", get_prop<&DateInterval::getDays>, set_prop<&DateInterval::setDays> },
603 { "h", get_prop<&DateInterval::getHours>, set_prop<&DateInterval::setHours> },
604 { "i", get_prop<&DateInterval::getMinutes>, set_prop<&DateInterval::setMinutes> },
605 { "s", get_prop<&DateInterval::getSeconds>, set_prop<&DateInterval::setSeconds> },
606 { "invert", is_inverted, set_prop<&DateInterval::setInverted> },
607 { "days", get_total_days, set_prop<&DateInterval::setTotalDays> },
608 { nullptr }
611 Native::PropAccessorMap date_interval_properties_map{date_interval_properties};
612 struct DateIntervalPropHandler : Native::MapPropHandler<DateIntervalPropHandler> {
613 static constexpr Native::PropAccessorMap& map = date_interval_properties_map;
616 Object HHVM_STATIC_METHOD(DateInterval, createFromDateString,
617 const String& time) {
618 return DateIntervalData::wrap(req::make<DateInterval>(time, true));
621 String HHVM_METHOD(DateInterval, format,
622 const String& format) {
623 auto const data = getDateIntervalData(this_);
624 return data->m_di->format(format);
627 ///////////////////////////////////////////////////////////////////////////////
628 // DateInterval helpers
630 Object DateIntervalData::wrap(req::ptr<DateInterval> di) {
631 Object obj{getClass()};
632 DateIntervalData* data = Native::data<DateIntervalData>(obj);
633 data->m_di = di;
634 return obj;
637 req::ptr<DateInterval> DateIntervalData::unwrap(const Object& obj) {
638 if (obj.instanceof(getClass())) {
639 DateIntervalData* data = Native::data<DateIntervalData>(obj);
640 return data->m_di;
643 return req::ptr<DateInterval>();
646 IMPLEMENT_GET_CLASS(DateIntervalData)
648 ///////////////////////////////////////////////////////////////////////////////
649 // timestamp
651 Variant HHVM_FUNCTION(gettimeofday,
652 bool return_float /* = false */) {
653 if (return_float) {
654 return TimeStamp::CurrentSecond();
656 return TimeStamp::CurrentTime();
659 Variant HHVM_FUNCTION(microtime,
660 bool get_as_float /* = false */) {
661 if (get_as_float) {
662 return TimeStamp::CurrentSecond();
664 return TimeStamp::CurrentMicroTime();
667 int64_t HHVM_FUNCTION(time) {
668 return time(0);
671 Variant HHVM_FUNCTION(mktime,
672 int64_t hour /* = PHP_INT_MAX */,
673 int64_t minute /* = PHP_INT_MAX */,
674 int64_t second /* = PHP_INT_MAX */,
675 int64_t month /* = PHP_INT_MAX */,
676 int64_t day /* = PHP_INT_MAX */,
677 int64_t year /* = PHP_INT_MAX */) {
678 hour = hour < INT_MAX ? hour : INT_MAX;
679 minute = minute < INT_MAX ? minute : INT_MAX;
680 second = second < INT_MAX ? second : INT_MAX;
681 month = month < INT_MAX ? month : INT_MAX;
682 day = day < INT_MAX ? day : INT_MAX;
683 year = year < INT_MAX ? year : INT_MAX;
684 if (hour == INT_MAX && minute == INT_MAX && second == INT_MAX &&
685 month == INT_MAX && day == INT_MAX && year == INT_MAX) {
686 raise_strict_warning("mktime(): You should be using "
687 "the time() function instead");
689 bool error;
690 int64_t ts = TimeStamp::Get(error, hour, minute, second, month, day, year,
691 false);
692 if (error) return false;
693 return ts;
696 Variant HHVM_FUNCTION(gmmktime,
697 int64_t hour /* = PHP_INT_MAX */,
698 int64_t minute /* = PHP_INT_MAX */,
699 int64_t second /* = PHP_INT_MAX */,
700 int64_t month /* = PHP_INT_MAX */,
701 int64_t day /* = PHP_INT_MAX */,
702 int64_t year /* = PHP_INT_MAX */) {
703 hour = hour < INT_MAX ? hour : INT_MAX;
704 minute = minute < INT_MAX ? minute : INT_MAX;
705 second = second < INT_MAX ? second : INT_MAX;
706 month = month < INT_MAX ? month : INT_MAX;
707 day = day < INT_MAX ? day : INT_MAX;
708 year = year < INT_MAX ? year : INT_MAX;
709 bool error;
710 int64_t ts = TimeStamp::Get(error, hour, minute, second, month, day, year,
711 true);
712 if (error) return false;
713 return ts;
716 static TypedValue HHVM_FUNCTION(idate,
717 const String& fmt, TypedValue timestamp) {
718 if (fmt.size() != 1) {
719 raise_invalid_argument_warning("format: %s", fmt.data());
720 return make_tv<KindOfBoolean>(false);
722 int64_t ret = req::make<DateTime>(
723 tvIsNull(timestamp) ? TimeStamp::Current() : tvAssertInt(timestamp),
724 false
725 )->toInteger(*fmt.data());
726 if (ret == -1) return make_tv<KindOfBoolean>(false);
727 return make_tv<KindOfInt64>(ret);
730 template<bool gmt>
731 static TypedValue date_impl(const String& format, TypedValue timestamp) {
732 if (!gmt && format.empty()) {
733 return tvReturn(empty_string());
736 String ret = req::make<DateTime>(
737 tvIsNull(timestamp) ? TimeStamp::Current() : tvAssertInt(timestamp),
739 )->toString(format, false);
740 if (ret.isNull()) return make_tv<KindOfBoolean>(false);
741 return tvReturn(ret);
744 template<bool gmt>
745 static TypedValue strftime_impl(const String& format, TypedValue timestamp) {
746 String ret = req::make<DateTime>(
747 tvIsNull(timestamp) ? TimeStamp::Current() : tvAssertInt(timestamp),
749 )->toString(format, true);
750 if (ret.isNull()) return make_tv<KindOfBoolean>(false);
751 return tvReturn(ret);
754 TypedValue HHVM_FUNCTION(strtotime,
755 const String& input, TypedValue timestamp) {
756 if (input.empty()) {
757 return make_tv<KindOfBoolean>(false);
759 auto dt = req::make<DateTime>(
760 tvIsNull(timestamp) ? TimeStamp::Current() : tvAssertInt(timestamp));
761 if (!dt->fromString(input, req::ptr<TimeZone>(), nullptr, false)) {
762 return make_tv<KindOfBoolean>(false);
764 bool error;
765 return make_tv<KindOfInt64>(dt->toTimeStamp(error));
768 static Array HHVM_FUNCTION(getdate, TypedValue timestamp) {
769 return req::make<DateTime>(
770 tvIsNull(timestamp) ? TimeStamp::Current() : tvAssertInt(timestamp),
771 false
772 )->toArray(DateTime::ArrayFormat::TimeMap);
775 static Variant HHVM_FUNCTION(localtime,
776 TypedValue timestamp, bool is_assoc) {
777 auto format = is_assoc ? DateTime::ArrayFormat::TmMap
778 : DateTime::ArrayFormat::TmVector;
779 return req::make<DateTime>(
780 tvIsNull(timestamp) ? TimeStamp::Current() : tvAssertInt(timestamp),
781 false
782 )->toArray(format);
785 Variant HHVM_FUNCTION(strptime,
786 const String& date,
787 const String& format) {
788 Array ret = DateTime::ParseAsStrptime(format, date);
789 if (ret.empty()) {
790 return false;
792 return ret;
795 ///////////////////////////////////////////////////////////////////////////////
796 // timezone
798 String HHVM_FUNCTION(date_default_timezone_get) {
799 return TimeZone::Current()->name();
802 bool HHVM_FUNCTION(date_default_timezone_set,
803 const String& name) {
804 return TimeZone::SetCurrent(name.c_str());
807 Variant HHVM_FUNCTION(timezone_name_from_abbr,
808 const String& abbr,
809 int64_t gmtoffset /* = -1 */,
810 int64_t isdst /* = 1 */) {
811 String ret = TimeZone::AbbreviationToName(abbr, gmtoffset, isdst);
812 if (ret.isNull()) {
813 return false;
815 return ret;
818 String HHVM_FUNCTION(timezone_version_get) {
819 return TimeZone::getVersion();
822 ///////////////////////////////////////////////////////////////////////////////
823 // datetime
825 bool HHVM_FUNCTION(checkdate,
826 int64_t month,
827 int64_t day,
828 int64_t year) {
829 return DateTime::IsValid(year, month, day);
832 Variant HHVM_FUNCTION(date_parse_from_format,
833 const String& format,
834 const String& date) {
835 Array ret = DateTime::Parse(format, date);
836 if (ret.empty()) {
837 return false;
839 return ret;
842 Variant HHVM_FUNCTION(date_create,
843 const Variant& time /* = null_string */,
844 const Variant& timezone /* = uninit_variant */) {
845 const String& str_time = time.isNull() ? null_string : time.toString();
846 auto tz = TimeZone::Current();
847 if (!timezone.isNull()) {
848 const Object& obj_timezone = timezone.toObject();
849 if (!obj_timezone.instanceof(s_DateTimeZone)) {
850 raise_argument_warning("date_create", 2, s_DateTimeZone, obj_timezone);
851 return false;
853 tz = DateTimeZoneData::unwrap(obj_timezone);
855 Object ret{DateTimeData::getClass()};
856 // Don't set the time here because it will throw if it is bad
857 HHVM_MN(DateTime, __construct)(ret.get());
858 if (str_time.empty()) {
859 // zend does this, so so do we
860 return ret;
862 auto dt = DateTimeData::unwrap(ret);
863 if (!dt->fromString(str_time, tz, nullptr, false)) {
864 return false;
866 return ret;
869 Variant HHVM_FUNCTION(date_format,
870 const Object& datetime,
871 const String& format) {
872 if (!datetime.instanceof(s_DateTimeInterface)) {
873 raise_argument_warning("date_format", 1, s_DateTimeInterface, datetime);
874 return false;
876 auto dt = DateTimeData::unwrap(datetime);
877 return dt->toString(format, false);
880 Variant HHVM_FUNCTION(date_parse,
881 const String& date) {
882 Array ret = DateTime::Parse(date);
883 if (ret.empty()) {
884 return false;
886 return ret;
889 ///////////////////////////////////////////////////////////////////////////////
890 // sun
892 Array HHVM_FUNCTION(date_sun_info,
893 int64_t timestamp,
894 double latitude,
895 double longitude) {
896 auto dt = req::make<DateTime>(timestamp, false);
897 return dt->getSunInfo(latitude, longitude);
900 template<bool sunset>
901 TypedValue date_sunrise_sunset(int64_t timestamp, int64_t format,
902 TypedValue latitude, TypedValue longitude,
903 TypedValue zenith, TypedValue offset) {
904 return tvReturn(req::make<DateTime>(timestamp, false)->getSunInfo(
905 static_cast<DateTime::SunInfoFormat>(format),
906 tvIsNull(latitude)
907 ? s_date_globals->default_latitude
908 : tvAssertDouble(latitude),
909 tvIsNull(longitude)
910 ? s_date_globals->default_longitude
911 : tvAssertDouble(longitude),
912 tvIsNull(zenith)
913 ? (sunset
914 ? s_date_globals->sunset_zenith
915 : s_date_globals->sunrise_zenith)
916 : tvAssertDouble(zenith),
917 tvIsNull(offset)
918 ? TimeZone::Current()->offset(0) / 3600
919 : tvAssertDouble(offset),
920 sunset));
923 ///////////////////////////////////////////////////////////////////////////////
925 #define DATE_ATOM "Y-m-d\\TH:i:sP"
926 #define DATE_COOKIE "D, d-M-Y H:i:s T"
927 #define DATE_ISO8601 "Y-m-d\\TH:i:sO"
928 #define DATE_RFC822 "D, d M y H:i:s O"
929 #define DATE_RFC850 "l, d-M-y H:i:s T"
930 #define DATE_RFC1036 "D, d M y H:i:s O"
931 #define DATE_RFC1123 "D, d M Y H:i:s O"
932 #define DATE_RFC2822 "D, d M Y H:i:s O"
933 #define DATE_RFC3339 "Y-m-d\\TH:i:sP"
934 #define DATE_RSS "D, d M Y H:i:s O"
935 #define DATE_W3C "Y-m-d\\TH:i:sP"
937 static struct DateTimeExtension final : Extension {
938 DateTimeExtension() : Extension("date") { }
940 void moduleInit() override {
941 HHVM_ME(DateTime, __construct);
942 HHVM_ME(DateTime, add);
943 HHVM_ME(DateTime, diff);
944 HHVM_ME(DateTime, format);
945 HHVM_ME(DateTime, getOffset);
946 HHVM_ME(DateTime, getTimestamp);
947 HHVM_ME(DateTime, getTimezone);
948 HHVM_ME(DateTime, modify);
949 HHVM_ME(DateTime, setDate);
950 HHVM_ME(DateTime, setISODate);
951 HHVM_ME(DateTime, setTime);
952 HHVM_ME(DateTime, setTimestamp);
953 HHVM_ME(DateTime, setTimezone);
954 HHVM_ME(DateTime, sub);
955 HHVM_ME(DateTime, __sleep);
956 HHVM_ME(DateTime, __wakeup);
957 HHVM_ME(DateTime, __debugInfo);
958 HHVM_STATIC_ME(DateTime, createFromFormat);
959 HHVM_STATIC_ME(DateTime, getLastErrors);
961 Native::registerNativeDataInfo<DateTimeData>(
962 DateTimeData::s_className.get(), Native::NDIFlags::NO_SWEEP);
964 HHVM_RC_STR_SAME(DATE_ATOM);
965 HHVM_RCC_STR(DateTime, ATOM, DATE_ATOM);
966 HHVM_RC_STR_SAME(DATE_COOKIE);
967 HHVM_RCC_STR(DateTime, COOKIE, DATE_COOKIE);
968 HHVM_RC_STR_SAME(DATE_ISO8601);
969 HHVM_RCC_STR(DateTime, ISO8601, DATE_ISO8601);
970 HHVM_RC_STR_SAME(DATE_RFC822);
971 HHVM_RCC_STR(DateTime, RFC822, DATE_RFC822);
972 HHVM_RC_STR_SAME(DATE_RFC850);
973 HHVM_RCC_STR(DateTime, RFC850, DATE_RFC850);
974 HHVM_RC_STR_SAME(DATE_RFC1036);
975 HHVM_RCC_STR(DateTime, RFC1036, DATE_RFC1036);
976 HHVM_RC_STR_SAME(DATE_RFC1123);
977 HHVM_RCC_STR(DateTime, RFC1123, DATE_RFC1123);
978 HHVM_RC_STR_SAME(DATE_RFC2822);
979 HHVM_RCC_STR(DateTime, RFC2822, DATE_RFC2822);
980 HHVM_RC_STR_SAME(DATE_RFC3339);
981 HHVM_RCC_STR(DateTime, RFC3339, DATE_RFC3339);
982 HHVM_RC_STR_SAME(DATE_RSS);
983 HHVM_RCC_STR(DateTime, RSS, DATE_RSS);
984 HHVM_RC_STR_SAME(DATE_W3C);
985 HHVM_RCC_STR(DateTime, W3C, DATE_W3C);
987 HHVM_RCC_INT(DateTimeZone, AFRICA, DateTimeZoneData::AFRICA);
988 HHVM_RCC_INT(DateTimeZone, AMERICA, DateTimeZoneData::AMERICA);
989 HHVM_RCC_INT(DateTimeZone, ANTARCTICA, DateTimeZoneData::ANTARCTICA);
990 HHVM_RCC_INT(DateTimeZone, ARCTIC, DateTimeZoneData::ARCTIC);
991 HHVM_RCC_INT(DateTimeZone, ASIA, DateTimeZoneData::ASIA);
992 HHVM_RCC_INT(DateTimeZone, ATLANTIC, DateTimeZoneData::ATLANTIC);
993 HHVM_RCC_INT(DateTimeZone, AUSTRALIA, DateTimeZoneData::AUSTRALIA);
994 HHVM_RCC_INT(DateTimeZone, EUROPE, DateTimeZoneData::EUROPE);
995 HHVM_RCC_INT(DateTimeZone, INDIAN, DateTimeZoneData::INDIAN);
996 HHVM_RCC_INT(DateTimeZone, PACIFIC, DateTimeZoneData::PACIFIC);
997 HHVM_RCC_INT(DateTimeZone, UTC, DateTimeZoneData::UTC);
998 HHVM_RCC_INT(DateTimeZone, ALL, DateTimeZoneData::ALL);
999 HHVM_RCC_INT(DateTimeZone, ALL_WITH_BC, DateTimeZoneData::ALL_WITH_BC);
1000 HHVM_RCC_INT(DateTimeZone, PER_COUNTRY, DateTimeZoneData::PER_COUNTRY);
1002 HHVM_ME(DateTimeZone, __construct);
1003 HHVM_ME(DateTimeZone, __debugInfo);
1004 HHVM_ME(DateTimeZone, getLocation);
1005 HHVM_ME(DateTimeZone, getName);
1006 HHVM_ME(DateTimeZone, getOffset);
1007 HHVM_ME(DateTimeZone, getTransitions);
1008 HHVM_STATIC_ME(DateTimeZone, listAbbreviations);
1009 HHVM_STATIC_ME(DateTimeZone, listIdentifiers);
1011 Native::registerNativeDataInfo<DateTimeZoneData>(
1012 DateTimeZoneData::s_className.get(), Native::NDIFlags::NO_SWEEP);
1014 HHVM_ME(DateInterval, __construct);
1015 HHVM_ME(DateInterval, format);
1016 HHVM_STATIC_ME(DateInterval, createFromDateString);
1017 Native::registerNativePropHandler<DateIntervalPropHandler>(s_DateInterval);
1019 Native::registerNativeDataInfo<DateIntervalData>(
1020 DateIntervalData::s_className.get(), Native::NDIFlags::NO_SWEEP);
1022 HHVM_FE(checkdate);
1023 HHVM_FE(date_parse_from_format);
1024 HHVM_FE(date_create);
1025 HHVM_FE(date_default_timezone_get);
1026 HHVM_FE(date_default_timezone_set);
1027 HHVM_FE(date_format);
1028 HHVM_FE(date_parse);
1029 HHVM_FE(date_sun_info);
1030 HHVM_NAMED_FE(date_sunrise, date_sunrise_sunset<false>);
1031 HHVM_NAMED_FE(date_sunset, date_sunrise_sunset<true>);
1032 HHVM_NAMED_FE(date, date_impl<false>);
1033 HHVM_NAMED_FE(gmdate, date_impl<true>);
1034 HHVM_FE(getdate);
1035 HHVM_FE(gettimeofday);
1036 HHVM_FE(gmmktime);
1037 HHVM_NAMED_FE(strftime, strftime_impl<false>);
1038 HHVM_NAMED_FE(gmstrftime, strftime_impl<true>);
1039 HHVM_FE(idate);
1040 HHVM_FE(localtime);
1041 HHVM_FE(microtime);
1042 HHVM_FE(mktime);
1043 HHVM_FE(strptime);
1044 HHVM_FE(strtotime);
1045 HHVM_FE(time);
1046 HHVM_FE(timezone_name_from_abbr);
1047 HHVM_FE(timezone_version_get);
1049 HHVM_RC_INT(SUNFUNCS_RET_DOUBLE,
1050 static_cast<int64_t>(DateTime::SunInfoFormat::ReturnDouble));
1051 HHVM_RC_INT(SUNFUNCS_RET_STRING,
1052 static_cast<int64_t>(DateTime::SunInfoFormat::ReturnString));
1053 HHVM_RC_INT(SUNFUNCS_RET_TIMESTAMP,
1054 static_cast<int64_t>(DateTime::SunInfoFormat::ReturnTimeStamp));
1056 loadSystemlib("datetime");
1059 void threadInit() override {
1060 IniSetting::Bind(
1061 this, IniSetting::PHP_INI_ALL,
1062 "date.timezone",
1064 IniSetting::SetAndGet<std::string>(
1065 dateTimezoneIniUpdate, dateTimezoneIniGet
1068 IniSetting::Bind(
1069 this, IniSetting::PHP_INI_ALL,
1070 "date.default_latitude", "31.7667",
1071 &s_date_globals->default_latitude
1073 IniSetting::Bind(
1074 this, IniSetting::PHP_INI_ALL,
1075 "date.default_longitude", "35.2333",
1076 &s_date_globals->default_longitude
1078 IniSetting::Bind(
1079 this, IniSetting::PHP_INI_ALL,
1080 "date.sunset_zenith", "90.583333",
1081 &s_date_globals->sunset_zenith
1083 IniSetting::Bind(
1084 this, IniSetting::PHP_INI_ALL,
1085 "date.sunrise_zenith", "90.583333",
1086 &s_date_globals->sunrise_zenith
1090 private:
1091 static bool dateTimezoneIniUpdate(const std::string& value) {
1092 if (value.empty()) {
1093 return false;
1095 return TimeZone::SetCurrent(value.c_str());
1098 static std::string dateTimezoneIniGet() {
1099 return RID().getTimezone();
1101 } s_date_extension;
1103 ///////////////////////////////////////////////////////////////////////////////