2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/datetime.h"
18 #include "hphp/runtime/base/dateinterval.h"
19 #include "hphp/runtime/base/string-buffer.h"
20 #include "hphp/runtime/base/runtime-error.h"
21 #include "hphp/runtime/base/type-conversions.h"
22 #include "hphp/runtime/base/builtin-functions.h"
23 #include "hphp/runtime/base/array-init.h"
27 ///////////////////////////////////////////////////////////////////////////////
30 IMPLEMENT_REQUEST_LOCAL(DateTime::LastErrors
, DateTime::s_lastErrors
);
32 const char *DateTime::DateFormatRFC822
= "D, d M y H:i:s O";
33 const char *DateTime::DateFormatRFC850
= "l, d-M-y H:i:s T";
34 const char *DateTime::DateFormatRFC1036
= "D, d M y H:i:s O";
35 const char *DateTime::DateFormatRFC1123
= "D, d M Y H:i:s O";
36 const char *DateTime::DateFormatRFC2822
= "D, d M Y H:i:s O";
37 const char *DateTime::DateFormatRFC3339
= "Y-m-d\\TH:i:sP";
38 const char *DateTime::DateFormatISO8601
= "Y-m-d\\TH:i:sO";
39 const char *DateTime::DateFormatCookie
= "D, d-M-Y H:i:s T";
40 const char *DateTime::DateFormatHttpHeader
= "D, d M Y H:i:s T";
42 const char *DateTime::MonthNames
[] = {
43 "January", "February", "March", "April", "May", "June",
44 "July", "August", "September", "October", "November", "December"
47 const char *DateTime::ShortMonthNames
[] = {
48 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
49 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
52 const char *DateTime::WeekdayNames
[] = {
53 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
56 const char *DateTime::ShortWeekdayNames
[] = {
57 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
60 const char *DateTime::GetWeekdayName(int y
, int m
, int d
) {
61 int day_of_week
= timelib_day_of_week(y
, m
, d
);
62 if (day_of_week
< 0) {
65 return WeekdayNames
[day_of_week
];
68 const char *DateTime::GetShortWeekdayName(int y
, int m
, int d
) {
69 int day_of_week
= timelib_day_of_week(y
, m
, d
);
70 if (day_of_week
< 0) {
73 return ShortWeekdayNames
[day_of_week
];
76 const char *DateTime::OrdinalSuffix(int number
) {
77 if (number
>= 10 && number
<= 19) {
80 switch (number
% 10) {
88 bool DateTime::IsLeap(int year
) {
89 return timelib_is_leap(year
);
92 int DateTime::DaysInMonth(int y
, int m
) {
93 return timelib_days_in_month(y
, m
);
96 bool DateTime::IsValid(int y
, int m
, int d
) {
97 return y
>= 1 && y
<= 32767 && m
>= 1 && m
<= 12 && d
>= 1 &&
98 d
<= timelib_days_in_month(y
, m
);
101 req::ptr
<DateTime
> DateTime::Current(bool utc
/* = false */) {
102 return req::make
<DateTime
>(time(0), utc
);
113 s_zone_type("zone_type"),
114 s_fraction("fraction"),
115 s_warning_count("warning_count"),
116 s_warnings("warnings"),
117 s_error_count("error_count"),
119 s_is_localtime("is_localtime"),
121 s_tz_abbr("tz_abbr"),
123 s_weekday("weekday"),
124 s_relative("relative"),
127 s_tm_hour("tm_hour"),
128 s_tm_mday("tm_mday"),
130 s_tm_year("tm_year"),
131 s_tm_wday("tm_wday"),
132 s_tm_yday("tm_yday"),
133 s_tm_isdst("tm_isdst"),
134 s_unparsed("unparsed"),
135 s_seconds("seconds"),
136 s_minutes("minutes"),
143 #define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
144 if ((int)parsed_time->elem == -99999) { \
145 ret.set(name, false); \
147 ret.set(name, (int)parsed_time->elem); \
150 Array
DateTime::Parse(const String
& datetime
) {
151 struct timelib_error_container
* error
;
152 timelib_time
* parsed_time
=
153 timelib_strtotime((char *)datetime
.data(), datetime
.size(), &error
,
154 TimeZone::GetDatabase(), TimeZone::GetTimeZoneInfoRaw
);
155 return DateTime::ParseTime(parsed_time
, error
);
158 Array
DateTime::Parse(const String
& format
, const String
& date
) {
159 struct timelib_error_container
* error
;
160 timelib_time
* parsed_time
=
161 timelib_parse_from_format((char *)format
.data(), (char *)date
.data(),
162 date
.size(), &error
, TimeZone::GetDatabase(),
163 TimeZone::GetTimeZoneInfoRaw
);
164 return DateTime::ParseTime(parsed_time
, error
);
167 Array
DateTime::ParseAsStrptime(const String
& format
, const String
& date
) {
168 struct tm parsed_time
;
169 memset(&parsed_time
, 0, sizeof(parsed_time
));
170 char* unparsed_part
= strptime(date
.data(), format
.data(), &parsed_time
);
171 if (unparsed_part
== nullptr) {
175 return make_map_array(
176 s_tm_sec
, parsed_time
.tm_sec
,
177 s_tm_min
, parsed_time
.tm_min
,
178 s_tm_hour
, parsed_time
.tm_hour
,
179 s_tm_mday
, parsed_time
.tm_mday
,
180 s_tm_mon
, parsed_time
.tm_mon
,
181 s_tm_year
, parsed_time
.tm_year
,
182 s_tm_wday
, parsed_time
.tm_wday
,
183 s_tm_yday
, parsed_time
.tm_yday
,
184 s_unparsed
, String(unparsed_part
, CopyString
)
188 Array
DateTime::ParseTime(timelib_time
* parsed_time
,
189 struct timelib_error_container
* error
) {
191 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_year
, y
);
192 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_month
, m
);
193 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_day
, d
);
194 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_hour
, h
);
195 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_minute
, i
);
196 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_second
, s
);
198 if (parsed_time
->f
== -99999) {
199 ret
.set(s_fraction
, false);
201 ret
.set(s_fraction
, parsed_time
->f
);
204 setLastErrors(error
);
206 Array warnings
= DateTime::getLastWarnings();
207 ret
.set(s_warning_count
, warnings
.size());
208 ret
.set(s_warnings
, warnings
);
211 Array errors
= DateTime::getLastErrors();
212 ret
.set(s_error_count
, errors
.size());
213 ret
.set(s_errors
, errors
);
216 ret
.set(s_is_localtime
, (bool)parsed_time
->is_localtime
);
217 if (parsed_time
->is_localtime
) {
218 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone_type
, zone_type
);
219 switch (parsed_time
->zone_type
) {
220 case TIMELIB_ZONETYPE_OFFSET
:
221 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone
, z
);
222 ret
.set(s_is_dst
, (bool)parsed_time
->dst
);
224 case TIMELIB_ZONETYPE_ID
:
225 if (parsed_time
->tz_abbr
) {
226 ret
.set(s_tz_abbr
, String(parsed_time
->tz_abbr
, CopyString
));
228 if (parsed_time
->tz_info
) {
229 ret
.set(s_tz_id
, String(parsed_time
->tz_info
->name
, CopyString
));
232 case TIMELIB_ZONETYPE_ABBR
:
233 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone
, z
);
234 ret
.set(s_is_dst
, (bool)parsed_time
->dst
);
235 ret
.set(s_tz_abbr
, String(parsed_time
->tz_abbr
, CopyString
));
242 if (parsed_time
->have_relative
) {
243 element
.set(s_year
, parsed_time
->relative
.y
);
244 element
.set(s_month
, parsed_time
->relative
.m
);
245 element
.set(s_day
, parsed_time
->relative
.d
);
246 element
.set(s_hour
, parsed_time
->relative
.h
);
247 element
.set(s_minute
, parsed_time
->relative
.i
);
248 element
.set(s_second
, parsed_time
->relative
.s
);
249 #if defined(TIMELIB_VERSION)
250 if (parsed_time
->relative
.have_weekday_relative
) {
252 if (parsed_time
->have_weekday_relative
) {
254 element
.set(s_weekday
, parsed_time
->relative
.weekday
);
256 ret
.set(s_relative
, element
);
260 timelib_time_dtor(parsed_time
);
264 ///////////////////////////////////////////////////////////////////////////////
267 DateTime::DateTime() : m_timestamp(-1), m_timestampSet(false) {
268 m_time
= TimePtr(timelib_time_ctor(), time_deleter());
269 setTimezone(TimeZone::Current());
272 DateTime::DateTime(int64_t timestamp
, bool utc
/* = false */) {
273 fromTimeStamp(timestamp
, utc
);
276 DateTime::DateTime(int64_t timestamp
, req::ptr
<TimeZone
> tz
) : m_tz(tz
) {
277 fromTimeStamp(timestamp
);
280 DateTime::DateTime(const DateTime
& dt
) :
282 m_timestamp(dt
.m_timestamp
),
283 m_timestampSet(dt
.m_timestampSet
) {
285 auto t
= timelib_time_clone(dt
.m_time
.get());
286 m_time
= TimePtr(t
, time_deleter());
289 void DateTime::fromTimeStamp(int64_t timestamp
, bool utc
/* = false */) {
290 m_timestamp
= timestamp
;
291 m_timestampSet
= true;
293 timelib_time
*t
= timelib_time_ctor();
295 t
->zone_type
= TIMELIB_ZONETYPE_OFFSET
;
297 timelib_unixtime2gmt(t
, (timelib_sll
)m_timestamp
);
300 m_tz
= TimeZone::Current();
302 t
->tz_info
= m_tz
->get();
303 t
->zone_type
= TIMELIB_ZONETYPE_ID
;
304 timelib_unixtime2local(t
, (timelib_sll
)m_timestamp
);
306 m_time
= TimePtr(t
, time_deleter());
309 void DateTime::sweep() {
313 ///////////////////////////////////////////////////////////////////////////////
316 int DateTime::beat() const {
317 int retval
= (((((long)m_time
->sse
)-(((long)m_time
->sse
) -
318 ((((long)m_time
->sse
) % 86400) +
319 3600))) * 10) / 864);
323 retval
= retval
% 1000;
327 int DateTime::dow() const {
328 return timelib_day_of_week(year(), month(), day());
331 int DateTime::doy() const {
332 return timelib_day_of_year(year(), month(), day());
335 int DateTime::isoWeek() const {
337 timelib_isoweek_from_date(year(), month(), day(), &iw
, &iy
);
341 int DateTime::isoYear() const {
343 timelib_isoweek_from_date(year(), month(), day(), &iw
, &iy
);
347 int DateTime::isoDow() const {
348 return timelib_iso_day_of_week(year(), month(), day());
351 int DateTime::offset() const {
353 switch (m_time
->zone_type
) {
354 case TIMELIB_ZONETYPE_ABBR
:
355 case TIMELIB_ZONETYPE_OFFSET
:
356 return (m_time
->z
- (m_time
->dst
* 60)) * -60;
360 timelib_time_offset
*offset
=
361 timelib_get_time_zone_info(toTimeStamp(error
), m_tz
->get());
362 int ret
= offset
->offset
;
363 timelib_time_offset_dtor(offset
);
371 const char *DateTime::weekdayName() const {
372 return GetWeekdayName(year(), month(), day());
375 const char *DateTime::shortWeekdayName() const {
376 return GetShortWeekdayName(year(), month(), day());
379 const char *DateTime::monthName() const {
380 return MonthNames
[month() - 1];
383 const char *DateTime::shortMonthName() const {
384 return ShortMonthNames
[month() - 1];
387 ///////////////////////////////////////////////////////////////////////////////
390 void DateTime::update() {
392 timelib_update_ts(m_time
.get(), nullptr);
394 timelib_update_ts(m_time
.get(), m_tz
->get());
397 m_timestampSet
= false;
400 void DateTime::set(int hou
, int min
, int sec
, int mon
, int day
, int yea
) {
401 /* Fill in the new data */
402 if (yea
!= INT_MAX
) {
405 } else if (yea
>= 70 && yea
<= 100) {
410 if (day
!= INT_MAX
) m_time
->d
= day
;
411 if (mon
!= INT_MAX
) m_time
->m
= mon
;
412 if (sec
!= INT_MAX
) m_time
->s
= sec
;
413 if (min
!= INT_MAX
) m_time
->i
= min
;
414 if (hou
!= INT_MAX
) m_time
->h
= hou
;
418 void DateTime::setDate(int y
, int m
, int d
) {
425 void DateTime::setISODate(int y
, int w
, int d
/* = 1 */) {
429 m_time
->relative
.d
= timelib_daynr_from_weeknr(y
, w
, d
);
430 m_time
->have_relative
= 1;
434 void DateTime::setTime(int hour
, int minute
, int second
) {
441 void DateTime::setTimezone(req::ptr
<TimeZone
> timezone
) {
443 m_tz
= timezone
->cloneTimeZone();
444 if (m_tz
.get() && m_tz
->get()) {
445 timelib_set_timezone(m_time
.get(), m_tz
->get());
446 timelib_unixtime2local(m_time
.get(), m_time
->sse
);
451 void DateTime::modify(const String
& diff
) {
452 timelib_time
*tmp_time
= timelib_strtotime((char*)diff
.data(), diff
.size(),
453 nullptr, TimeZone::GetDatabase(),
454 TimeZone::GetTimeZoneInfoRaw
);
455 internalModify(tmp_time
);
456 timelib_time_dtor(tmp_time
);
459 void DateTime::internalModify(timelib_time
*t
) {
460 // TIMELIB_UNSET (and other constants) defined in timelib.h
461 // (see hhvm-third-party)
462 if (t
->y
!= TIMELIB_UNSET
) {
465 if (t
->m
!= TIMELIB_UNSET
) {
468 if (t
->d
!= TIMELIB_UNSET
) {
471 if (t
->h
!= TIMELIB_UNSET
) {
475 if (t
->i
!= TIMELIB_UNSET
) {
477 if (t
->s
!= TIMELIB_UNSET
) {
482 internalModifyRelative(&(t
->relative
), t
->have_relative
, 1);
485 void DateTime::internalModifyRelative(timelib_rel_time
*rel
,
486 bool have_relative
, int8_t bias
) {
487 m_time
->relative
.y
= rel
->y
* bias
;
488 m_time
->relative
.m
= rel
->m
* bias
;
489 m_time
->relative
.d
= rel
->d
* bias
;
490 m_time
->relative
.h
= rel
->h
* bias
;
491 m_time
->relative
.i
= rel
->i
* bias
;
492 m_time
->relative
.s
= rel
->s
* bias
;
493 m_time
->relative
.weekday
= rel
->weekday
;
494 m_time
->have_relative
= have_relative
;
495 m_time
->relative
.special
= rel
->special
;
496 m_time
->relative
.have_special_relative
= rel
->have_special_relative
;
497 m_time
->relative
.have_weekday_relative
= rel
->have_weekday_relative
;
498 m_time
->relative
.weekday_behavior
= rel
->weekday_behavior
;
499 m_time
->relative
.first_last_day_of
= rel
->first_last_day_of
;
500 m_time
->sse_uptodate
= 0;
502 timelib_update_from_sse(m_time
.get());
505 void DateTime::add(const req::ptr
<DateInterval
>& interval
) {
506 timelib_rel_time
*rel
= interval
->get();
507 internalModifyRelative(rel
, true, rel
->invert
? -1 : 1);
510 void DateTime::sub(const req::ptr
<DateInterval
>& interval
) {
511 timelib_rel_time
*rel
= interval
->get();
512 internalModifyRelative(rel
, true, rel
->invert
? 1 : -1);
515 ///////////////////////////////////////////////////////////////////////////////
518 void DateTime::toTm(struct tm
&ta
) const {
519 // TODO: Fixme under MSVC!
520 ta
.tm_sec
= second();
521 ta
.tm_min
= minute();
524 ta
.tm_mon
= month() - 1;
525 ta
.tm_year
= year() - 1900;
535 timelib_time_offset
*offset
=
536 timelib_get_time_zone_info(m_time
->sse
, m_time
->tz_info
);
537 ta
.tm_isdst
= offset
->is_dst
;
539 ta
.tm_gmtoff
= offset
->offset
;
540 ta
.tm_zone
= offset
->abbr
;
542 timelib_time_offset_dtor(offset
);
546 int64_t DateTime::toTimeStamp(bool &err
) const {
548 if (!m_timestampSet
) {
550 m_timestamp
= timelib_date_to_int(m_time
.get(), &error
);
554 m_timestampSet
= true;
560 int64_t DateTime::toInteger(char format
) const {
564 case 'j': return day();
565 case 'w': return dow();
566 case 'z': return doy();
567 case 'W': return isoWeek();
569 case 'n': return month();
570 case 't': return DaysInMonth(year(), month());
571 case 'L': return DateTime::IsLeap(year());
572 case 'y': return (year() % 100);
573 case 'Y': return year();
574 case 'B': return beat();
576 case 'h': return hour12();
578 case 'G': return hour();
579 case 'i': return minute();
580 case 's': return second();
581 case 'I': return (!utc() && m_tz
->dst(toTimeStamp(error
))) ? 1 : 0;
582 case 'Z': return utc() ? 0 : m_tz
->offset(toTimeStamp(error
));
583 case 'U': return toTimeStamp(error
);
585 throw_invalid_argument("unknown format char: %d", (int)format
);
589 String
DateTime::toString(const String
& format
, bool stdc
/* = false */) const {
590 if (format
.empty()) return String();
591 return stdc
? stdcFormat(format
) : rfcFormat(format
);
594 String
DateTime::toString(DateFormat format
) const {
596 case DateFormat::RFC822
: return rfcFormat(DateFormatRFC822
);
597 case DateFormat::RFC850
: return rfcFormat(DateFormatRFC850
);
598 case DateFormat::RFC1036
: return rfcFormat(DateFormatRFC1036
);
599 case DateFormat::RFC1123
: return rfcFormat(DateFormatRFC1123
);
600 case DateFormat::RFC2822
: return rfcFormat(DateFormatRFC2822
);
601 case DateFormat::RFC3339
: return rfcFormat(DateFormatRFC3339
);
602 case DateFormat::ISO8601
: return rfcFormat(DateFormatISO8601
);
603 case DateFormat::Cookie
: return rfcFormat(DateFormatCookie
);
604 case DateFormat::HttpHeader
: return rfcFormat(DateFormatHttpHeader
);
608 throw_invalid_argument("format: %d", static_cast<int>(format
));
612 String
DateTime::rfcFormat(const String
& format
) const {
614 bool rfc_colon
= false;
616 for (int i
= 0; i
< format
.size(); i
++) {
617 switch (format
.charAt(i
)) {
618 case 'd': s
.printf("%02d", day()); break;
619 case 'D': s
.append(shortWeekdayName()); break;
620 case 'j': s
.append(day()); break;
621 case 'l': s
.append(weekdayName()); break;
622 case 'S': s
.append(OrdinalSuffix(day())); break;
623 case 'w': s
.append(dow()); break;
624 case 'N': s
.append(isoDow()); break;
625 case 'z': s
.append(doy()); break;
626 case 'W': s
.printf("%02d", isoWeek()); break;
627 case 'o': s
.append(isoYear()); break;
628 case 'F': s
.append(monthName()); break;
629 case 'm': s
.printf("%02d", month()); break;
630 case 'M': s
.append(shortMonthName()); break;
631 case 'n': s
.append(month()); break;
632 case 't': s
.append(DaysInMonth(year(), month())); break;
633 case 'L': s
.append(IsLeap(year())); break;
634 case 'y': s
.printf("%02d", year() % 100); break;
635 case 'Y': s
.printf("%s%04d", year() < 0 ? "-" : "", abs(year()));
637 case 'a': s
.append(hour() >= 12 ? "pm" : "am"); break;
638 case 'A': s
.append(hour() >= 12 ? "PM" : "AM"); break;
639 case 'B': s
.printf("%03d", beat()); break;
640 case 'g': s
.append((hour() % 12) ? (int)hour() % 12 : 12); break;
641 case 'G': s
.append(hour()); break;
642 case 'h': s
.printf("%02d", (hour() % 12) ? (int)hour() % 12 : 12); break;
643 case 'H': s
.printf("%02d", (int)hour()); break;
644 case 'i': s
.printf("%02d", (int)minute()); break;
645 case 's': s
.printf("%02d", (int)second()); break;
646 case 'u': s
.printf("%06d", (int)floor(fraction() * 1000000)); break;
647 case 'I': s
.append(!utc() && m_tz
->dst(toTimeStamp(error
)) ? 1 : 0);
649 case 'P': rfc_colon
= true; /* break intentionally missing */
652 s
.printf("+00%s00", rfc_colon
? ":" : "");
654 int offset
= this->offset();
655 s
.printf("%c%02d%s%02d",
656 (offset
< 0 ? '-' : '+'), abs(offset
/ 3600),
657 rfc_colon
? ":" : "", abs((offset
% 3600) / 60));
664 if (m_time
->zone_type
!= TIMELIB_ZONETYPE_OFFSET
) {
665 s
.append(m_time
->tz_abbr
);
667 auto offset
= m_time
->z
* -60;
669 snprintf(abbr
, 9, "GMT%c%02d%02d",
670 ((offset
< 0) ? '-' : '+'),
672 abs((offset
% 3600) / 60));
681 if (m_time
->zone_type
!= TIMELIB_ZONETYPE_OFFSET
) {
682 s
.append(m_tz
->name());
684 auto offset
= m_time
->z
* -60;
686 snprintf(abbr
, 7, "%c%02d:%02d",
687 ((offset
< 0) ? '-' : '+'),
689 abs((offset
% 3600) / 60));
694 case 'Z': s
.append(utc() ? 0 : this->offset()); break;
697 s
.printf("%04d-%02d-%02dT%02d:%02d:%02d+00:00",
698 year(), month(), day(), hour(), minute(), second());
700 int offset
= this->offset();
701 s
.printf("%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
702 year(), month(), day(), hour(), minute(), second(),
703 (offset
< 0 ? '-' : '+'),
704 abs(offset
/ 3600), abs((offset
% 3600) / 60));
709 s
.printf("%3s, %02d %3s %04d %02d:%02d:%02d +0000",
710 shortWeekdayName(), day(), shortMonthName(), year(),
711 hour(), minute(), second());
713 int offset
= this->offset();
714 s
.printf("%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d",
715 shortWeekdayName(), day(), shortMonthName(), year(),
716 hour(), minute(), second(),
717 (offset
< 0 ? '-' : '+'),
718 abs(offset
/ 3600), abs((offset
% 3600) / 60));
721 case 'U': s
.printf("%" PRId64
, toTimeStamp(error
)); break;
723 if (i
< format
.size()) i
++; /* break intentionally missing */
732 String
DateTime::stdcFormat(const String
& format
) const {
733 // TODO: Fixme under MSVC!
735 timelib_time_offset
*offset
= nullptr;
736 ta
.tm_sec
= second();
737 ta
.tm_min
= minute();
740 ta
.tm_mon
= month() - 1;
741 ta
.tm_year
= year() - 1900;
751 offset
= timelib_get_time_zone_info(m_time
->sse
, m_time
->tz_info
);
752 ta
.tm_isdst
= offset
->is_dst
;
754 ta
.tm_gmtoff
= offset
->offset
;
755 ta
.tm_zone
= offset
->abbr
;
759 if ((ta
.tm_sec
< 0 || ta
.tm_sec
> 60) ||
760 (ta
.tm_min
< 0 || ta
.tm_min
> 59) ||
761 (ta
.tm_hour
< 0 || ta
.tm_hour
> 23) ||
762 (ta
.tm_mday
< 1 || ta
.tm_mday
> 31) ||
763 (ta
.tm_mon
< 0 || ta
.tm_mon
> 11) ||
764 (ta
.tm_wday
< 0 || ta
.tm_wday
> 6) ||
765 (ta
.tm_yday
< 0 || ta
.tm_yday
> 365)) {
766 throw_invalid_argument("argument: invalid time");
770 int max_reallocs
= 5;
771 size_t buf_len
= 256, real_len
;
772 char *buf
= (char *)malloc(buf_len
);
773 while ((real_len
= strftime(buf
, buf_len
, format
.data(), &ta
)) == buf_len
||
777 buf
= (char *)malloc(buf_len
);
778 if (!--max_reallocs
) {
783 timelib_time_offset_dtor(offset
);
785 if (real_len
&& real_len
!= buf_len
) {
786 return String(buf
, real_len
, AttachString
);
789 throw_invalid_argument("format: (over internal buffer)");
793 Array
DateTime::toArray(ArrayFormat format
) const {
796 case ArrayFormat::TimeMap
:
797 return make_map_array(
806 s_weekday
, weekdayName(),
807 s_month
, monthName(),
808 0, toTimeStamp(error
)
810 case ArrayFormat::TmMap
:
814 return make_map_array(
817 s_tm_hour
, tm
.tm_hour
,
818 s_tm_mday
, tm
.tm_mday
,
820 s_tm_year
, tm
.tm_year
,
821 s_tm_wday
, tm
.tm_wday
,
822 s_tm_yday
, tm
.tm_yday
,
823 s_tm_isdst
, tm
.tm_isdst
826 case ArrayFormat::TmVector
:
830 return make_packed_array(
843 return empty_array();
846 bool DateTime::fromString(const String
& input
, req::ptr
<TimeZone
> tz
,
847 const char* format
/*=NUL*/,
848 bool throw_on_error
/*= true*/) {
849 struct timelib_error_container
*error
;
852 t
= timelib_parse_from_format((char*)format
, (char*)input
.data(),
853 input
.size(), &error
, TimeZone::GetDatabase(),
854 TimeZone::GetTimeZoneInfoRaw
);
856 t
= timelib_strtotime((char*)input
.data(), input
.size(),
857 &error
, TimeZone::GetDatabase(),
858 TimeZone::GetTimeZoneInfoRaw
);
860 int error1
= error
->error_count
;
861 setLastErrors(error
);
863 timelib_time_dtor(t
);
864 if (!throw_on_error
) {
867 auto msg
= folly::format(
868 "DateTime::__construct(): Failed to parse time string "
869 "({}) at position {} ({}): {}",
871 error
->error_messages
[0].position
,
872 error
->error_messages
[0].character
,
873 error
->error_messages
[0].message
875 SystemLib::throwExceptionObject(msg
);
878 if (m_timestamp
== -1) {
881 if (tz
.get() && (input
.size() <= 0 || input
[0] != '@')) {
884 setTimezone(TimeZone::Current());
887 // needed if any date part is missing
888 timelib_fill_holes(t
, m_time
.get(), TIMELIB_NO_CLONE
);
889 timelib_update_ts(t
, m_tz
->get());
892 m_timestamp
= timelib_date_to_int(t
, &error2
);
893 if (error1
|| error2
) {
894 // Don't free t->tz_info, it belongs to GetTimeZoneInfo
895 timelib_time_dtor(t
);
899 m_time
= TimePtr(t
, time_deleter());
900 if (t
->tz_info
!= m_tz
->get()) {
901 m_tz
= req::make
<TimeZone
>(t
->tz_info
);
906 req::ptr
<DateTime
> DateTime::cloneDateTime() const {
907 return req::make
<DateTime
>(*this);
910 ///////////////////////////////////////////////////////////////////////////////
913 req::ptr
<DateInterval
>
914 DateTime::diff(req::ptr
<DateTime
> datetime2
, bool absolute
) {
915 timelib_rel_time
*rel
= timelib_diff(m_time
.get(), datetime2
.get()->m_time
.get());
919 return req::make
<DateInterval
>(rel
);
922 ///////////////////////////////////////////////////////////////////////////////
926 s_sunrise("sunrise"),
928 s_transit("transit"),
929 s_civil_twilight_begin("civil_twilight_begin"),
930 s_civil_twilight_end("civil_twilight_end"),
931 s_nautical_twilight_begin("nautical_twilight_begin"),
932 s_nautical_twilight_end("nautical_twilight_end"),
933 s_astronomical_twilight_begin("astronomical_twilight_begin"),
934 s_astronomical_twilight_end("astronomical_twilight_end");
936 Array
DateTime::getSunInfo(double latitude
, double longitude
) const {
938 timelib_sll sunrise
, sunset
, transit
;
941 /* Get sun up/down and transit */
942 int rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
943 -35.0/60, 1, &ddummy
, &ddummy
,
944 &sunrise
, &sunset
, &transit
);
946 case -1: /* always below */
947 ret
.set(s_sunrise
, false);
948 ret
.set(s_sunset
, false);
950 case 1: /* always above */
951 ret
.set(s_sunrise
, true);
952 ret
.set(s_sunset
, true);
955 ret
.set(s_sunrise
, sunrise
);
956 ret
.set(s_sunset
, sunset
);
958 ret
.set(s_transit
, transit
);
960 /* Get civil twilight */
961 rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
963 &ddummy
, &ddummy
, &sunrise
, &sunset
,
966 case -1: /* always below */
967 ret
.set(s_civil_twilight_begin
, false);
968 ret
.set(s_civil_twilight_end
, false);
970 case 1: /* always above */
971 ret
.set(s_civil_twilight_begin
, true);
972 ret
.set(s_civil_twilight_end
, true);
975 ret
.set(s_civil_twilight_begin
, sunrise
);
976 ret
.set(s_civil_twilight_end
, sunset
);
979 /* Get nautical twilight */
980 rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
982 &ddummy
, &ddummy
, &sunrise
, &sunset
,
985 case -1: /* always below */
986 ret
.set(s_nautical_twilight_begin
, false);
987 ret
.set(s_nautical_twilight_end
, false);
989 case 1: /* always above */
990 ret
.set(s_nautical_twilight_begin
, true);
991 ret
.set(s_nautical_twilight_end
, true);
994 ret
.set(s_nautical_twilight_begin
, sunrise
);
995 ret
.set(s_nautical_twilight_end
, sunset
);
998 /* Get astronomical twilight */
999 rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
1001 &ddummy
, &ddummy
, &sunrise
, &sunset
,
1004 case -1: /* always below */
1005 ret
.set(s_astronomical_twilight_begin
, false);
1006 ret
.set(s_astronomical_twilight_end
, false);
1008 case 1: /* always above */
1009 ret
.set(s_astronomical_twilight_begin
, true);
1010 ret
.set(s_astronomical_twilight_end
, true);
1013 ret
.set(s_astronomical_twilight_begin
, sunrise
);
1014 ret
.set(s_astronomical_twilight_end
, sunset
);
1019 Variant
DateTime::getSunInfo(SunInfoFormat retformat
,
1020 double latitude
, double longitude
,
1021 double zenith
, double utc_offset
,
1022 bool calc_sunset
) const {
1023 if (retformat
!= SunInfoFormat::ReturnTimeStamp
&&
1024 retformat
!= SunInfoFormat::ReturnString
&&
1025 retformat
!= SunInfoFormat::ReturnDouble
) {
1026 raise_warning("Wrong return format given, pick one of "
1027 "SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING or "
1028 "SUNFUNCS_RET_DOUBLE");
1031 double altitude
= 90 - zenith
;
1032 double h_rise
, h_set
;
1033 timelib_sll sunrise
, sunset
, transit
;
1034 int rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
1036 &h_rise
, &h_set
, &sunrise
, &sunset
,
1042 if (retformat
== SunInfoFormat::ReturnTimeStamp
) {
1043 return calc_sunset
? sunset
: sunrise
;
1046 double N
= (calc_sunset
? h_set
: h_rise
) + utc_offset
;
1047 if (N
> 24 || N
< 0) {
1048 N
-= floor(N
/ 24) * 24;
1051 if (retformat
== SunInfoFormat::ReturnString
) {
1053 snprintf(retstr
, sizeof(retstr
),
1054 "%02d:%02d", (int) N
, (int) (60 * (N
- (int) N
)));
1055 return String(retstr
, CopyString
);
1058 assert(retformat
== SunInfoFormat::ReturnDouble
);
1062 ///////////////////////////////////////////////////////////////////////////////