2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/builtin-functions.h"
22 #include "hphp/runtime/base/array-init.h"
26 ///////////////////////////////////////////////////////////////////////////////
29 IMPLEMENT_REQUEST_LOCAL(DateTime::LastErrors
, DateTime::s_lastErrors
);
31 const char *DateTime::DateFormatRFC822
= "D, d M y H:i:s O";
32 const char *DateTime::DateFormatRFC850
= "l, d-M-y H:i:s T";
33 const char *DateTime::DateFormatRFC1036
= "D, d M y H:i:s O";
34 const char *DateTime::DateFormatRFC1123
= "D, d M Y H:i:s O";
35 const char *DateTime::DateFormatRFC2822
= "D, d M Y H:i:s O";
36 const char *DateTime::DateFormatRFC3339
= "Y-m-d\\TH:i:sP";
37 const char *DateTime::DateFormatISO8601
= "Y-m-d\\TH:i:sO";
38 const char *DateTime::DateFormatCookie
= "D, d-M-Y H:i:s T";
39 const char *DateTime::DateFormatHttpHeader
= "D, d M Y H:i:s T";
41 const char *DateTime::MonthNames
[] = {
42 "January", "February", "March", "April", "May", "June",
43 "July", "August", "September", "October", "November", "December"
46 const char *DateTime::ShortMonthNames
[] = {
47 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
48 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
51 const char *DateTime::WeekdayNames
[] = {
52 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
55 const char *DateTime::ShortWeekdayNames
[] = {
56 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
59 const char *DateTime::GetWeekdayName(int y
, int m
, int d
) {
60 int day_of_week
= timelib_day_of_week(y
, m
, d
);
61 if (day_of_week
< 0) {
64 return WeekdayNames
[day_of_week
];
67 const char *DateTime::GetShortWeekdayName(int y
, int m
, int d
) {
68 int day_of_week
= timelib_day_of_week(y
, m
, d
);
69 if (day_of_week
< 0) {
72 return ShortWeekdayNames
[day_of_week
];
75 const char *DateTime::OrdinalSuffix(int number
) {
76 if (number
>= 10 && number
<= 19) {
79 switch (number
% 10) {
87 bool DateTime::IsLeap(int year
) {
88 return timelib_is_leap(year
);
91 int DateTime::DaysInMonth(int y
, int m
) {
92 return timelib_days_in_month(y
, m
);
95 bool DateTime::IsValid(int y
, int m
, int d
) {
96 return y
>= 1 && y
<= 32767 && m
>= 1 && m
<= 12 && d
>= 1 &&
97 d
<= timelib_days_in_month(y
, m
);
100 req::ptr
<DateTime
> DateTime::Current(bool utc
/* = false */) {
101 return req::make
<DateTime
>(time(0), utc
);
112 s_zone_type("zone_type"),
113 s_fraction("fraction"),
114 s_warning_count("warning_count"),
115 s_warnings("warnings"),
116 s_error_count("error_count"),
118 s_is_localtime("is_localtime"),
120 s_tz_abbr("tz_abbr"),
122 s_weekday("weekday"),
123 s_relative("relative"),
126 s_tm_hour("tm_hour"),
127 s_tm_mday("tm_mday"),
129 s_tm_year("tm_year"),
130 s_tm_wday("tm_wday"),
131 s_tm_yday("tm_yday"),
132 s_tm_isdst("tm_isdst"),
133 s_unparsed("unparsed"),
134 s_seconds("seconds"),
135 s_minutes("minutes"),
142 #define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
143 if ((int)parsed_time->elem == -99999) { \
144 ret.set(name, false); \
146 ret.set(name, (int)parsed_time->elem); \
149 Array
DateTime::Parse(const String
& datetime
) {
150 struct timelib_error_container
* error
;
151 timelib_time
* parsed_time
=
152 timelib_strtotime((char *)datetime
.data(), datetime
.size(), &error
,
153 TimeZone::GetDatabase(), TimeZone::GetTimeZoneInfoRaw
);
154 return DateTime::ParseTime(parsed_time
, error
);
157 Array
DateTime::Parse(const String
& format
, const String
& date
) {
158 struct timelib_error_container
* error
;
159 timelib_time
* parsed_time
=
160 timelib_parse_from_format((char *)format
.data(), (char *)date
.data(),
161 date
.size(), &error
, TimeZone::GetDatabase(),
162 TimeZone::GetTimeZoneInfoRaw
);
163 return DateTime::ParseTime(parsed_time
, error
);
166 Array
DateTime::ParseAsStrptime(const String
& format
, const String
& date
) {
167 struct tm parsed_time
;
168 memset(&parsed_time
, 0, sizeof(parsed_time
));
169 char* unparsed_part
= strptime(date
.data(), format
.data(), &parsed_time
);
170 if (unparsed_part
== nullptr) {
174 return make_map_array(
175 s_tm_sec
, parsed_time
.tm_sec
,
176 s_tm_min
, parsed_time
.tm_min
,
177 s_tm_hour
, parsed_time
.tm_hour
,
178 s_tm_mday
, parsed_time
.tm_mday
,
179 s_tm_mon
, parsed_time
.tm_mon
,
180 s_tm_year
, parsed_time
.tm_year
,
181 s_tm_wday
, parsed_time
.tm_wday
,
182 s_tm_yday
, parsed_time
.tm_yday
,
183 s_unparsed
, String(unparsed_part
, CopyString
)
187 Array
DateTime::ParseTime(timelib_time
* parsed_time
,
188 struct timelib_error_container
* error
) {
190 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_year
, y
);
191 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_month
, m
);
192 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_day
, d
);
193 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_hour
, h
);
194 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_minute
, i
);
195 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_second
, s
);
197 if (parsed_time
->f
== -99999) {
198 ret
.set(s_fraction
, false);
200 ret
.set(s_fraction
, parsed_time
->f
);
203 setLastErrors(error
);
205 Array warnings
= DateTime::getLastWarnings();
206 ret
.set(s_warning_count
, warnings
.size());
207 ret
.set(s_warnings
, warnings
);
210 Array errors
= DateTime::getLastErrors();
211 ret
.set(s_error_count
, errors
.size());
212 ret
.set(s_errors
, errors
);
215 ret
.set(s_is_localtime
, (bool)parsed_time
->is_localtime
);
216 if (parsed_time
->is_localtime
) {
217 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone_type
, zone_type
);
218 switch (parsed_time
->zone_type
) {
219 case TIMELIB_ZONETYPE_OFFSET
:
220 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone
, z
);
221 ret
.set(s_is_dst
, (bool)parsed_time
->dst
);
223 case TIMELIB_ZONETYPE_ID
:
224 if (parsed_time
->tz_abbr
) {
225 ret
.set(s_tz_abbr
, String(parsed_time
->tz_abbr
, CopyString
));
227 if (parsed_time
->tz_info
) {
228 ret
.set(s_tz_id
, String(parsed_time
->tz_info
->name
, CopyString
));
231 case TIMELIB_ZONETYPE_ABBR
:
232 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone
, z
);
233 ret
.set(s_is_dst
, (bool)parsed_time
->dst
);
234 ret
.set(s_tz_abbr
, String(parsed_time
->tz_abbr
, CopyString
));
241 if (parsed_time
->have_relative
) {
242 element
.set(s_year
, parsed_time
->relative
.y
);
243 element
.set(s_month
, parsed_time
->relative
.m
);
244 element
.set(s_day
, parsed_time
->relative
.d
);
245 element
.set(s_hour
, parsed_time
->relative
.h
);
246 element
.set(s_minute
, parsed_time
->relative
.i
);
247 element
.set(s_second
, parsed_time
->relative
.s
);
248 #if defined(TIMELIB_VERSION)
249 if (parsed_time
->relative
.have_weekday_relative
) {
251 if (parsed_time
->have_weekday_relative
) {
253 element
.set(s_weekday
, parsed_time
->relative
.weekday
);
255 ret
.set(s_relative
, element
);
259 timelib_time_dtor(parsed_time
);
263 ///////////////////////////////////////////////////////////////////////////////
266 DateTime::DateTime() : m_timestamp(-1), m_timestampSet(false) {
267 m_time
= TimePtr(timelib_time_ctor(), time_deleter());
268 setTimezone(TimeZone::Current());
271 DateTime::DateTime(int64_t timestamp
, bool utc
/* = false */) {
272 fromTimeStamp(timestamp
, utc
);
275 DateTime::DateTime(int64_t timestamp
, req::ptr
<TimeZone
> tz
) : m_tz(tz
) {
276 fromTimeStamp(timestamp
);
279 DateTime::DateTime(const DateTime
& dt
) :
281 m_timestamp(dt
.m_timestamp
),
282 m_timestampSet(dt
.m_timestampSet
) {
284 auto t
= timelib_time_clone(dt
.m_time
.get());
285 m_time
= TimePtr(t
, time_deleter());
288 void DateTime::fromTimeStamp(int64_t timestamp
, bool utc
/* = false */) {
289 m_timestamp
= timestamp
;
290 m_timestampSet
= true;
292 timelib_time
*t
= timelib_time_ctor();
294 t
->zone_type
= TIMELIB_ZONETYPE_OFFSET
;
296 timelib_unixtime2gmt(t
, (timelib_sll
)m_timestamp
);
299 m_tz
= TimeZone::Current();
301 t
->tz_info
= m_tz
->get();
303 raise_error("No tz info found for timezone check for tzdata package.");
305 t
->zone_type
= TIMELIB_ZONETYPE_ID
;
306 timelib_unixtime2local(t
, (timelib_sll
)m_timestamp
);
308 m_time
= TimePtr(t
, time_deleter());
311 void DateTime::sweep() {
315 ///////////////////////////////////////////////////////////////////////////////
318 int DateTime::beat() const {
319 int retval
= ((((long)m_time
->sse
)-(((long)m_time
->sse
) -
320 ((((long)m_time
->sse
) % 86400) +
325 retval
= (retval
/ 864) % 1000;
329 int DateTime::dow() const {
330 return timelib_day_of_week(year(), month(), day());
333 int DateTime::doy() const {
334 return timelib_day_of_year(year(), month(), day());
337 int DateTime::isoWeek() const {
339 timelib_isoweek_from_date(year(), month(), day(), &iw
, &iy
);
343 int DateTime::isoYear() const {
345 timelib_isoweek_from_date(year(), month(), day(), &iw
, &iy
);
349 int DateTime::isoDow() const {
350 return timelib_iso_day_of_week(year(), month(), day());
353 int DateTime::offset() const {
355 switch (m_time
->zone_type
) {
356 case TIMELIB_ZONETYPE_ABBR
:
357 case TIMELIB_ZONETYPE_OFFSET
:
358 return (m_time
->z
- (m_time
->dst
* 60)) * -60;
362 timelib_time_offset
*offset
=
363 timelib_get_time_zone_info(toTimeStamp(error
), m_tz
->get());
364 int ret
= offset
->offset
;
365 timelib_time_offset_dtor(offset
);
373 const char *DateTime::weekdayName() const {
374 return GetWeekdayName(year(), month(), day());
377 const char *DateTime::shortWeekdayName() const {
378 return GetShortWeekdayName(year(), month(), day());
381 const char *DateTime::monthName() const {
382 return MonthNames
[month() - 1];
385 const char *DateTime::shortMonthName() const {
386 return ShortMonthNames
[month() - 1];
389 ///////////////////////////////////////////////////////////////////////////////
392 void DateTime::update() {
394 timelib_update_ts(m_time
.get(), nullptr);
396 timelib_update_ts(m_time
.get(), m_tz
->get());
399 m_timestampSet
= false;
402 void DateTime::set(int hou
, int min
, int sec
, int mon
, int day
, int yea
) {
403 /* Fill in the new data */
404 if (yea
!= INT_MAX
) {
407 } else if (yea
>= 70 && yea
<= 100) {
412 if (day
!= INT_MAX
) m_time
->d
= day
;
413 if (mon
!= INT_MAX
) m_time
->m
= mon
;
414 if (sec
!= INT_MAX
) m_time
->s
= sec
;
415 if (min
!= INT_MAX
) m_time
->i
= min
;
416 if (hou
!= INT_MAX
) m_time
->h
= hou
;
420 void DateTime::setDate(int y
, int m
, int d
) {
427 void DateTime::setISODate(int y
, int w
, int d
/* = 1 */) {
431 m_time
->relative
.d
= timelib_daynr_from_weeknr(y
, w
, d
);
432 m_time
->have_relative
= 1;
436 void DateTime::setTime(int hour
, int minute
, int second
) {
443 void DateTime::setTimezone(req::ptr
<TimeZone
> timezone
) {
445 m_tz
= timezone
->cloneTimeZone();
447 timelib_set_timezone(m_time
.get(), m_tz
->get());
448 timelib_unixtime2local(m_time
.get(), m_time
->sse
);
453 bool DateTime::modify(const String
& diff
) {
454 timelib_error_container
* error
= nullptr;
455 timelib_time
*tmp_time
= timelib_strtotime((char*)diff
.data(), diff
.size(),
456 &error
, TimeZone::GetDatabase(),
457 TimeZone::GetTimeZoneInfoRaw
);
459 timelib_time_dtor(tmp_time
);
460 if (error
) timelib_error_container_dtor(error
);
463 if (error
&& error
->error_count
> 0) {
464 raise_warning("DateTime::modify(): Failed to parse time string (%s)"
465 " at position %d (%c): %s",
466 diff
.c_str(), error
->error_messages
[0].position
,
467 error
->error_messages
[0].character
, error
->error_messages
[0].message
471 internalModify(tmp_time
);
475 void DateTime::internalModify(timelib_time
*t
) {
476 // TIMELIB_UNSET (and other constants) defined in timelib.h
477 // (see hhvm-third-party)
478 if (t
->y
!= TIMELIB_UNSET
) {
481 if (t
->m
!= TIMELIB_UNSET
) {
484 if (t
->d
!= TIMELIB_UNSET
) {
487 if (t
->h
!= TIMELIB_UNSET
) {
491 if (t
->i
!= TIMELIB_UNSET
) {
493 if (t
->s
!= TIMELIB_UNSET
) {
498 internalModifyRelative(&(t
->relative
), t
->have_relative
, 1);
501 void DateTime::internalModifyRelative(timelib_rel_time
*rel
,
502 bool have_relative
, int8_t bias
) {
503 m_time
->relative
.y
= rel
->y
* bias
;
504 m_time
->relative
.m
= rel
->m
* bias
;
505 m_time
->relative
.d
= rel
->d
* bias
;
506 m_time
->relative
.h
= rel
->h
* bias
;
507 m_time
->relative
.i
= rel
->i
* bias
;
508 m_time
->relative
.s
= rel
->s
* bias
;
509 m_time
->relative
.weekday
= rel
->weekday
;
510 m_time
->have_relative
= have_relative
;
511 m_time
->relative
.special
= rel
->special
;
512 m_time
->relative
.have_special_relative
= rel
->have_special_relative
;
513 m_time
->relative
.have_weekday_relative
= rel
->have_weekday_relative
;
514 m_time
->relative
.weekday_behavior
= rel
->weekday_behavior
;
515 m_time
->relative
.first_last_day_of
= rel
->first_last_day_of
;
516 m_time
->sse_uptodate
= 0;
518 timelib_update_from_sse(m_time
.get());
521 void DateTime::add(const req::ptr
<DateInterval
>& interval
) {
522 timelib_rel_time
*rel
= interval
->get();
523 internalModifyRelative(rel
, true, rel
->invert
? -1 : 1);
526 void DateTime::sub(const req::ptr
<DateInterval
>& interval
) {
527 timelib_rel_time
*rel
= interval
->get();
528 internalModifyRelative(rel
, true, rel
->invert
? 1 : -1);
531 ///////////////////////////////////////////////////////////////////////////////
534 void DateTime::toTm(struct tm
&ta
) const {
535 // TODO: Fixme under MSVC!
536 ta
.tm_sec
= second();
537 ta
.tm_min
= minute();
540 ta
.tm_mon
= month() - 1;
541 ta
.tm_year
= year() - 1900;
551 timelib_time_offset
*offset
=
552 timelib_get_time_zone_info(m_time
->sse
, m_time
->tz_info
);
553 ta
.tm_isdst
= offset
->is_dst
;
555 ta
.tm_gmtoff
= offset
->offset
;
556 ta
.tm_zone
= offset
->abbr
;
558 timelib_time_offset_dtor(offset
);
562 int64_t DateTime::toTimeStamp(bool &err
) const {
564 if (!m_timestampSet
) {
566 m_timestamp
= timelib_date_to_int(m_time
.get(), &error
);
570 m_timestampSet
= true;
576 int64_t DateTime::toInteger(char format
) const {
580 case 'j': return day();
581 case 'w': return dow();
582 case 'z': return doy();
583 case 'W': return isoWeek();
585 case 'n': return month();
586 case 't': return DaysInMonth(year(), month());
587 case 'L': return DateTime::IsLeap(year());
588 case 'y': return (year() % 100);
589 case 'Y': return year();
590 case 'B': return beat();
592 case 'h': return hour12();
594 case 'G': return hour();
595 case 'i': return minute();
596 case 's': return second();
597 case 'I': return (!utc() && m_tz
->dst(toTimeStamp(error
))) ? 1 : 0;
598 case 'Z': return utc() ? 0 : m_tz
->offset(toTimeStamp(error
));
599 case 'U': return toTimeStamp(error
);
601 throw_invalid_argument("unknown format char: %d", (int)format
);
605 String
DateTime::toString(const String
& format
, bool stdc
/* = false */) const {
606 if (format
.empty()) return String();
607 return stdc
? stdcFormat(format
) : rfcFormat(format
);
610 String
DateTime::toString(DateFormat format
) const {
612 case DateFormat::RFC822
: return rfcFormat(DateFormatRFC822
);
613 case DateFormat::RFC850
: return rfcFormat(DateFormatRFC850
);
614 case DateFormat::RFC1036
: return rfcFormat(DateFormatRFC1036
);
615 case DateFormat::RFC1123
: return rfcFormat(DateFormatRFC1123
);
616 case DateFormat::RFC2822
: return rfcFormat(DateFormatRFC2822
);
617 case DateFormat::RFC3339
: return rfcFormat(DateFormatRFC3339
);
618 case DateFormat::ISO8601
: return rfcFormat(DateFormatISO8601
);
619 case DateFormat::Cookie
: return rfcFormat(DateFormatCookie
);
620 case DateFormat::HttpHeader
: return rfcFormat(DateFormatHttpHeader
);
624 throw_invalid_argument("format: %d", static_cast<int>(format
));
628 String
DateTime::rfcFormat(const String
& format
) const {
630 bool rfc_colon
= false;
632 for (int i
= 0; i
< format
.size(); i
++) {
633 switch (format
.charAt(i
)) {
634 case 'd': s
.printf("%02d", day()); break;
635 case 'D': s
.append(shortWeekdayName()); break;
636 case 'j': s
.append(day()); break;
637 case 'l': s
.append(weekdayName()); break;
638 case 'S': s
.append(OrdinalSuffix(day())); break;
639 case 'w': s
.append(dow()); break;
640 case 'N': s
.append(isoDow()); break;
641 case 'z': s
.append(doy()); break;
642 case 'W': s
.printf("%02d", isoWeek()); break;
643 case 'o': s
.append(isoYear()); break;
644 case 'F': s
.append(monthName()); break;
645 case 'm': s
.printf("%02d", month()); break;
646 case 'M': s
.append(shortMonthName()); break;
647 case 'n': s
.append(month()); break;
648 case 't': s
.append(DaysInMonth(year(), month())); break;
649 case 'L': s
.append(IsLeap(year())); break;
650 case 'y': s
.printf("%02d", year() % 100); break;
651 case 'Y': s
.printf("%s%04d", year() < 0 ? "-" : "", abs(year()));
653 case 'a': s
.append(hour() >= 12 ? "pm" : "am"); break;
654 case 'A': s
.append(hour() >= 12 ? "PM" : "AM"); break;
655 case 'B': s
.printf("%03d", beat()); break;
656 case 'g': s
.append((hour() % 12) ? (int)hour() % 12 : 12); break;
657 case 'G': s
.append(hour()); break;
658 case 'h': s
.printf("%02d", (hour() % 12) ? (int)hour() % 12 : 12); break;
659 case 'H': s
.printf("%02d", (int)hour()); break;
660 case 'i': s
.printf("%02d", (int)minute()); break;
661 case 's': s
.printf("%02d", (int)second()); break;
662 case 'u': s
.printf("%06d", (int)floor(fraction() * 1000000)); break;
663 case 'v': s
.printf("%03d", (int)floor(fraction() * 1000)); break;
664 case 'I': s
.append(!utc() && m_tz
->dst(toTimeStamp(error
)) ? 1 : 0);
666 case 'P': rfc_colon
= true; /* break intentionally missing */
669 s
.printf("+00%s00", rfc_colon
? ":" : "");
671 int offset
= this->offset();
672 s
.printf("%c%02d%s%02d",
673 (offset
< 0 ? '-' : '+'), abs(offset
/ 3600),
674 rfc_colon
? ":" : "", abs((offset
% 3600) / 60));
681 if (m_time
->zone_type
!= TIMELIB_ZONETYPE_OFFSET
) {
682 s
.append(m_time
->tz_abbr
);
684 auto offset
= m_time
->z
* -60;
686 snprintf(abbr
, 9, "GMT%c%02d%02d",
687 ((offset
< 0) ? '-' : '+'),
689 abs((offset
% 3600) / 60));
698 if (m_time
->zone_type
!= TIMELIB_ZONETYPE_OFFSET
) {
699 s
.append(m_tz
->name());
701 auto offset
= m_time
->z
* -60;
703 snprintf(abbr
, 7, "%c%02d:%02d",
704 ((offset
< 0) ? '-' : '+'),
706 abs((offset
% 3600) / 60));
711 case 'Z': s
.append(utc() ? 0 : this->offset()); break;
714 s
.printf("%04d-%02d-%02dT%02d:%02d:%02d+00:00",
715 year(), month(), day(), hour(), minute(), second());
717 int offset
= this->offset();
718 s
.printf("%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
719 year(), month(), day(), hour(), minute(), second(),
720 (offset
< 0 ? '-' : '+'),
721 abs(offset
/ 3600), abs((offset
% 3600) / 60));
726 s
.printf("%3s, %02d %3s %04d %02d:%02d:%02d +0000",
727 shortWeekdayName(), day(), shortMonthName(), year(),
728 hour(), minute(), second());
730 int offset
= this->offset();
731 s
.printf("%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d",
732 shortWeekdayName(), day(), shortMonthName(), year(),
733 hour(), minute(), second(),
734 (offset
< 0 ? '-' : '+'),
735 abs(offset
/ 3600), abs((offset
% 3600) / 60));
738 case 'U': s
.printf("%" PRId64
, toTimeStamp(error
)); break;
740 if (i
< format
.size()) i
++; /* break intentionally missing */
749 String
DateTime::stdcFormat(const String
& format
) const {
750 // TODO: Fixme under MSVC!
752 timelib_time_offset
*offset
= nullptr;
753 ta
.tm_sec
= second();
754 ta
.tm_min
= minute();
757 ta
.tm_mon
= month() - 1;
758 ta
.tm_year
= year() - 1900;
768 offset
= timelib_get_time_zone_info(m_time
->sse
, m_time
->tz_info
);
769 ta
.tm_isdst
= offset
->is_dst
;
771 ta
.tm_gmtoff
= offset
->offset
;
772 ta
.tm_zone
= offset
->abbr
;
776 if ((ta
.tm_sec
< 0 || ta
.tm_sec
> 60) ||
777 (ta
.tm_min
< 0 || ta
.tm_min
> 59) ||
778 (ta
.tm_hour
< 0 || ta
.tm_hour
> 23) ||
779 (ta
.tm_mday
< 1 || ta
.tm_mday
> 31) ||
780 (ta
.tm_mon
< 0 || ta
.tm_mon
> 11) ||
781 (ta
.tm_wday
< 0 || ta
.tm_wday
> 6) ||
782 (ta
.tm_yday
< 0 || ta
.tm_yday
> 365)) {
783 throw_invalid_argument("argument: invalid time");
787 int max_reallocs
= 5;
788 size_t buf_len
= 256, real_len
;
789 char *buf
= (char *)malloc(buf_len
);
790 while ((real_len
= strftime(buf
, buf_len
, format
.data(), &ta
)) == buf_len
||
794 buf
= (char *)malloc(buf_len
);
795 if (!--max_reallocs
) {
800 timelib_time_offset_dtor(offset
);
802 if (real_len
&& real_len
!= buf_len
) {
803 return String(buf
, real_len
, AttachString
);
806 throw_invalid_argument("format: (over internal buffer)");
810 Array
DateTime::toArray(ArrayFormat format
) const {
813 case ArrayFormat::TimeMap
:
814 return make_map_array(
823 s_weekday
, weekdayName(),
824 s_month
, monthName(),
825 0, toTimeStamp(error
)
827 case ArrayFormat::TmMap
:
831 return make_map_array(
834 s_tm_hour
, tm
.tm_hour
,
835 s_tm_mday
, tm
.tm_mday
,
837 s_tm_year
, tm
.tm_year
,
838 s_tm_wday
, tm
.tm_wday
,
839 s_tm_yday
, tm
.tm_yday
,
840 s_tm_isdst
, tm
.tm_isdst
843 case ArrayFormat::TmVector
:
847 return make_packed_array(
860 return empty_array();
863 bool DateTime::fromString(const String
& input
, req::ptr
<TimeZone
> tz
,
864 const char* format
/*=NUL*/,
865 bool throw_on_error
/*= true*/) {
866 struct timelib_error_container
*error
;
869 t
= timelib_parse_from_format((char*)format
, (char*)input
.data(),
870 input
.size(), &error
, TimeZone::GetDatabase(),
871 TimeZone::GetTimeZoneInfoRaw
);
873 t
= timelib_strtotime((char*)input
.data(), input
.size(),
874 &error
, TimeZone::GetDatabase(),
875 TimeZone::GetTimeZoneInfoRaw
);
877 int error1
= error
->error_count
;
878 setLastErrors(error
);
880 timelib_time_dtor(t
);
881 if (!throw_on_error
) {
884 auto msg
= folly::format(
885 "DateTime::__construct(): Failed to parse time string "
886 "({}) at position {} ({}): {}",
888 error
->error_messages
[0].position
,
889 error
->error_messages
[0].character
,
890 error
->error_messages
[0].message
892 SystemLib::throwExceptionObject(msg
);
895 if (m_timestamp
== -1) {
898 if (tz
.get() && (input
.size() <= 0 || input
[0] != '@')) {
901 setTimezone(TimeZone::Current());
904 // needed if any date part is missing
905 timelib_fill_holes(t
, m_time
.get(), TIMELIB_NO_CLONE
);
906 timelib_update_ts(t
, m_tz
->get());
907 timelib_update_from_sse(t
);
910 m_timestamp
= timelib_date_to_int(t
, &error2
);
911 if (error1
|| error2
) {
912 // Don't free t->tz_info, it belongs to GetTimeZoneInfo
913 timelib_time_dtor(t
);
917 m_time
= TimePtr(t
, time_deleter());
918 if (t
->tz_info
!= m_tz
->get()) {
919 m_tz
= req::make
<TimeZone
>(t
->tz_info
);
924 req::ptr
<DateTime
> DateTime::cloneDateTime() const {
925 return req::make
<DateTime
>(*this);
928 ///////////////////////////////////////////////////////////////////////////////
931 req::ptr
<DateInterval
>
932 DateTime::diff(req::ptr
<DateTime
> datetime2
, bool absolute
) {
933 timelib_rel_time
*rel
= timelib_diff(m_time
.get(), datetime2
.get()->m_time
.get());
937 return req::make
<DateInterval
>(rel
);
940 int DateTime::compare(req::ptr
<DateTime
> datetime2
) {
941 return timelib_time_compare(m_time
.get(), datetime2
.get()->m_time
.get());
944 ///////////////////////////////////////////////////////////////////////////////
948 s_sunrise("sunrise"),
950 s_transit("transit"),
951 s_civil_twilight_begin("civil_twilight_begin"),
952 s_civil_twilight_end("civil_twilight_end"),
953 s_nautical_twilight_begin("nautical_twilight_begin"),
954 s_nautical_twilight_end("nautical_twilight_end"),
955 s_astronomical_twilight_begin("astronomical_twilight_begin"),
956 s_astronomical_twilight_end("astronomical_twilight_end");
958 Array
DateTime::getSunInfo(double latitude
, double longitude
) const {
960 timelib_sll sunrise
, sunset
, transit
;
963 /* Get sun up/down and transit */
964 int rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
965 -35.0/60, 1, &ddummy
, &ddummy
,
966 &sunrise
, &sunset
, &transit
);
968 case -1: /* always below */
969 ret
.set(s_sunrise
, false);
970 ret
.set(s_sunset
, false);
972 case 1: /* always above */
973 ret
.set(s_sunrise
, true);
974 ret
.set(s_sunset
, true);
977 ret
.set(s_sunrise
, sunrise
);
978 ret
.set(s_sunset
, sunset
);
980 ret
.set(s_transit
, transit
);
982 /* Get civil twilight */
983 rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
985 &ddummy
, &ddummy
, &sunrise
, &sunset
,
988 case -1: /* always below */
989 ret
.set(s_civil_twilight_begin
, false);
990 ret
.set(s_civil_twilight_end
, false);
992 case 1: /* always above */
993 ret
.set(s_civil_twilight_begin
, true);
994 ret
.set(s_civil_twilight_end
, true);
997 ret
.set(s_civil_twilight_begin
, sunrise
);
998 ret
.set(s_civil_twilight_end
, sunset
);
1001 /* Get nautical twilight */
1002 rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
1004 &ddummy
, &ddummy
, &sunrise
, &sunset
,
1007 case -1: /* always below */
1008 ret
.set(s_nautical_twilight_begin
, false);
1009 ret
.set(s_nautical_twilight_end
, false);
1011 case 1: /* always above */
1012 ret
.set(s_nautical_twilight_begin
, true);
1013 ret
.set(s_nautical_twilight_end
, true);
1016 ret
.set(s_nautical_twilight_begin
, sunrise
);
1017 ret
.set(s_nautical_twilight_end
, sunset
);
1020 /* Get astronomical twilight */
1021 rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
1023 &ddummy
, &ddummy
, &sunrise
, &sunset
,
1026 case -1: /* always below */
1027 ret
.set(s_astronomical_twilight_begin
, false);
1028 ret
.set(s_astronomical_twilight_end
, false);
1030 case 1: /* always above */
1031 ret
.set(s_astronomical_twilight_begin
, true);
1032 ret
.set(s_astronomical_twilight_end
, true);
1035 ret
.set(s_astronomical_twilight_begin
, sunrise
);
1036 ret
.set(s_astronomical_twilight_end
, sunset
);
1041 Variant
DateTime::getSunInfo(SunInfoFormat retformat
,
1042 double latitude
, double longitude
,
1043 double zenith
, double utc_offset
,
1044 bool calc_sunset
) const {
1045 if (retformat
!= SunInfoFormat::ReturnTimeStamp
&&
1046 retformat
!= SunInfoFormat::ReturnString
&&
1047 retformat
!= SunInfoFormat::ReturnDouble
) {
1048 raise_warning("Wrong return format given, pick one of "
1049 "SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING or "
1050 "SUNFUNCS_RET_DOUBLE");
1053 double altitude
= 90 - zenith
;
1054 double h_rise
, h_set
;
1055 timelib_sll sunrise
, sunset
, transit
;
1056 int rs
= timelib_astro_rise_set_altitude(m_time
.get(), longitude
, latitude
,
1058 &h_rise
, &h_set
, &sunrise
, &sunset
,
1064 if (retformat
== SunInfoFormat::ReturnTimeStamp
) {
1065 return calc_sunset
? sunset
: sunrise
;
1068 double N
= (calc_sunset
? h_set
: h_rise
) + utc_offset
;
1069 if (N
> 24 || N
< 0) {
1070 N
-= floor(N
/ 24) * 24;
1073 if (retformat
== SunInfoFormat::ReturnString
) {
1075 snprintf(retstr
, sizeof(retstr
),
1076 "%02d:%02d", (int) N
, (int) (60 * (N
- (int) N
)));
1077 return String(retstr
, CopyString
);
1080 assert(retformat
== SunInfoFormat::ReturnDouble
);
1084 ///////////////////////////////////////////////////////////////////////////////