2 +----------------------------------------------------------------------+
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"
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)
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;
49 static String
zone_type_to_string(int zoneType
, req::ptr
<DateTime
> dt
) {
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");
63 double default_latitude
;
64 double default_longitude
;
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()); \
79 ///////////////////////////////////////////////////////////////////////////////
82 static const StaticString
84 s_getTimestamp("getTimestamp"),
85 s_DateTimeInterface("DateTimeInterface"),
86 s_DateTimeZone("DateTimeZone"),
87 s_DateInterval("DateInterval"),
88 s_DateTime("DateTime");
90 ///////////////////////////////////////////////////////////////////////////////
94 void raise_argument_warning(const char* func
,
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 ///////////////////////////////////////////////////////////////////////////////
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
);
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
,
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
,
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)) {
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
,
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();
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();
235 return DateTimeZoneData::wrap(tz
);
240 Variant
HHVM_METHOD(DateTime
, modify
,
241 const String
& modify
) {
242 auto const data
= getDateTimeData(this_
);
243 if (!data
->m_dt
->modify(modify
)) {
246 return Object(this_
);
249 Object
HHVM_METHOD(DateTime
, setDate
,
253 auto const data
= getDateTimeData(this_
);
254 data
->m_dt
->setDate(year
, month
, day
);
255 return Object(this_
);
258 Object
HHVM_METHOD(DateTime
, setISODate
,
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
,
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
,
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
);
302 auto di
= DateIntervalData::unwrap(interval
);
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
);
314 auto di
= DateIntervalData::unwrap(interval
);
316 return Object(this_
);
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(),
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 {
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 ///////////////////////////////////////////////////////////////////////////////
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
);
388 auto leftTime
= getTimestamp(left
);
389 auto rightTime
= getTimestamp(right
);
390 if (leftTime
< rightTime
) {
392 } else if (leftTime
> rightTime
) {
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
);
412 req::ptr
<DateTime
> DateTimeData::unwrap(const Object
& datetime
) {
413 if (LIKELY(datetime
.instanceof(getClass()))) {
414 DateTimeData
* data
= Native::data
<DateTimeData
>(datetime
);
417 if (datetime
->instanceof(SystemLib::s_DateTimeImmutableClass
)) {
418 auto rval
= datetime
->getProp(
419 SystemLib::s_DateTimeImmutableClass
,
422 assertx(rval
.is_set() && type(rval
) == KindOfObject
);
423 Object
impl(rval
.val().pobj
);
426 return req::ptr
<DateTime
>();
429 IMPLEMENT_GET_CLASS(DateTimeData
)
431 ///////////////////////////////////////////////////////////////////////////////
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();
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_
);
463 if (!datetime
.instanceof(s_DateTime
)) {
464 raise_argument_warning("DateTimeZone::getOffset", 1, s_DateTime
, datetime
);
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
,
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");
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
));
520 ///////////////////////////////////////////////////////////////////////////////
521 // DateTimeZone helpers
523 Object
DateTimeZoneData::wrap(req::ptr
<TimeZone
> tz
) {
524 Object obj
{getClass()};
525 DateTimeZoneData
* data
= Native::data
<DateTimeZoneData
>(obj
);
530 req::ptr
<TimeZone
> DateTimeZoneData::unwrap(const Object
& timezone
) {
531 if (timezone
.instanceof(getClass())) {
532 DateTimeZoneData
* data
= Native::data
<DateTimeZoneData
>(timezone
);
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 {
547 return make_dict_array(
548 s_timezone_type
, m_tz
->type(),
549 s_timezone
, m_tz
->name()
553 ///////////////////////////////////////////////////////////////////////////////
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();
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
> },
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
);
637 req::ptr
<DateInterval
> DateIntervalData::unwrap(const Object
& obj
) {
638 if (obj
.instanceof(getClass())) {
639 DateIntervalData
* data
= Native::data
<DateIntervalData
>(obj
);
643 return req::ptr
<DateInterval
>();
646 IMPLEMENT_GET_CLASS(DateIntervalData
)
648 ///////////////////////////////////////////////////////////////////////////////
651 Variant
HHVM_FUNCTION(gettimeofday
,
652 bool return_float
/* = false */) {
654 return TimeStamp::CurrentSecond();
656 return TimeStamp::CurrentTime();
659 Variant
HHVM_FUNCTION(microtime
,
660 bool get_as_float
/* = false */) {
662 return TimeStamp::CurrentSecond();
664 return TimeStamp::CurrentMicroTime();
667 int64_t HHVM_FUNCTION(time
) {
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");
690 int64_t ts
= TimeStamp::Get(error
, hour
, minute
, second
, month
, day
, year
,
692 if (error
) return false;
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
;
710 int64_t ts
= TimeStamp::Get(error
, hour
, minute
, second
, month
, day
, year
,
712 if (error
) return false;
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
),
725 )->toInteger(*fmt
.data());
726 if (ret
== -1) return make_tv
<KindOfBoolean
>(false);
727 return make_tv
<KindOfInt64
>(ret
);
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
);
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
) {
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);
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
),
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
),
785 Variant
HHVM_FUNCTION(strptime
,
787 const String
& format
) {
788 Array ret
= DateTime::ParseAsStrptime(format
, date
);
795 ///////////////////////////////////////////////////////////////////////////////
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
,
809 int64_t gmtoffset
/* = -1 */,
810 int64_t isdst
/* = 1 */) {
811 String ret
= TimeZone::AbbreviationToName(abbr
, gmtoffset
, isdst
);
818 String
HHVM_FUNCTION(timezone_version_get
) {
819 return TimeZone::getVersion();
822 ///////////////////////////////////////////////////////////////////////////////
825 bool HHVM_FUNCTION(checkdate
,
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
);
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
);
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
862 auto dt
= DateTimeData::unwrap(ret
);
863 if (!dt
->fromString(str_time
, tz
, nullptr, false)) {
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
);
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
);
889 ///////////////////////////////////////////////////////////////////////////////
892 Array
HHVM_FUNCTION(date_sun_info
,
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
),
907 ? s_date_globals
->default_latitude
908 : tvAssertDouble(latitude
),
910 ? s_date_globals
->default_longitude
911 : tvAssertDouble(longitude
),
914 ? s_date_globals
->sunset_zenith
915 : s_date_globals
->sunrise_zenith
)
916 : tvAssertDouble(zenith
),
918 ? TimeZone::Current()->offset(0) / 3600
919 : tvAssertDouble(offset
),
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
);
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>);
1035 HHVM_FE(gettimeofday
);
1037 HHVM_NAMED_FE(strftime
, strftime_impl
<false>);
1038 HHVM_NAMED_FE(gmstrftime
, strftime_impl
<true>);
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
{
1061 this, IniSetting::PHP_INI_ALL
,
1064 IniSetting::SetAndGet
<std::string
>(
1065 dateTimezoneIniUpdate
, dateTimezoneIniGet
1069 this, IniSetting::PHP_INI_ALL
,
1070 "date.default_latitude", "31.7667",
1071 &s_date_globals
->default_latitude
1074 this, IniSetting::PHP_INI_ALL
,
1075 "date.default_longitude", "35.2333",
1076 &s_date_globals
->default_longitude
1079 this, IniSetting::PHP_INI_ALL
,
1080 "date.sunset_zenith", "90.583333",
1081 &s_date_globals
->sunset_zenith
1084 this, IniSetting::PHP_INI_ALL
,
1085 "date.sunrise_zenith", "90.583333",
1086 &s_date_globals
->sunrise_zenith
1091 static bool dateTimezoneIniUpdate(const std::string
& value
) {
1092 if (value
.empty()) {
1095 return TimeZone::SetCurrent(value
.c_str());
1098 static std::string
dateTimezoneIniGet() {
1099 return RID().getTimezone();
1103 ///////////////////////////////////////////////////////////////////////////////