c646a30002
Many callsites of Array.set(litstr, ...)
944 linhas
29 KiB
C++
944 linhas
29 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/base/time/datetime.h>
|
|
#include <runtime/base/time/dateinterval.h>
|
|
#include <runtime/base/complex_types.h>
|
|
#include <runtime/base/util/string_buffer.h>
|
|
#include <runtime/base/runtime_error.h>
|
|
#include <runtime/base/type_conversions.h>
|
|
#include <runtime/base/builtin_functions.h>
|
|
|
|
namespace HPHP {
|
|
|
|
IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(DateTime);
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// statics
|
|
|
|
StaticString DateTime::s_class_name("DateTime");
|
|
IMPLEMENT_REQUEST_LOCAL(DateTime::LastErrors, DateTime::s_lastErrors);
|
|
|
|
const char *DateTime::DateFormatRFC822 = "D, d M y H:i:s O";
|
|
const char *DateTime::DateFormatRFC850 = "l, d-M-y H:i:s T";
|
|
const char *DateTime::DateFormatRFC1036 = "D, d M y H:i:s O";
|
|
const char *DateTime::DateFormatRFC1123 = "D, d M Y H:i:s O";
|
|
const char *DateTime::DateFormatRFC2822 = "D, d M Y H:i:s O";
|
|
const char *DateTime::DateFormatRFC3339 = "Y-m-d\\TH:i:sP";
|
|
const char *DateTime::DateFormatISO8601 = "Y-m-d\\TH:i:sO";
|
|
const char *DateTime::DateFormatCookie = "D, d-M-Y H:i:s T";
|
|
const char *DateTime::DateFormatHttpHeader = "D, d M Y H:i:s T";
|
|
|
|
const char *DateTime::MonthNames[] = {
|
|
"January", "February", "March", "April", "May", "June",
|
|
"July", "August", "September", "October", "November", "December"
|
|
};
|
|
|
|
const char *DateTime::ShortMonthNames[] = {
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
};
|
|
|
|
const char *DateTime::WeekdayNames[] = {
|
|
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
|
};
|
|
|
|
const char *DateTime::ShortWeekdayNames[] = {
|
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
|
};
|
|
|
|
const char *DateTime::GetWeekdayName(int y, int m, int d) {
|
|
int day_of_week = timelib_day_of_week(y, m, d);
|
|
if (day_of_week < 0) {
|
|
return "Unknown";
|
|
}
|
|
return WeekdayNames[day_of_week];
|
|
}
|
|
|
|
const char *DateTime::GetShortWeekdayName(int y, int m, int d) {
|
|
int day_of_week = timelib_day_of_week(y, m, d);
|
|
if (day_of_week < 0) {
|
|
return "Unknown";
|
|
}
|
|
return ShortWeekdayNames[day_of_week];
|
|
}
|
|
|
|
const char *DateTime::OrdinalSuffix(int number) {
|
|
if (number >= 10 && number <= 19) {
|
|
return "th";
|
|
}
|
|
switch (number % 10) {
|
|
case 1: return "st";
|
|
case 2: return "nd";
|
|
case 3: return "rd";
|
|
}
|
|
return "th";
|
|
}
|
|
|
|
bool DateTime::IsLeap(int year) {
|
|
return timelib_is_leap(year);
|
|
}
|
|
|
|
int DateTime::DaysInMonth(int y, int m) {
|
|
return timelib_days_in_month(y, m);
|
|
}
|
|
|
|
bool DateTime::IsValid(int y, int m, int d) {
|
|
return y >= 1 && y <= 32767 && m >= 1 && m <= 12 && d >= 1 &&
|
|
d <= timelib_days_in_month(y, m);
|
|
}
|
|
|
|
SmartObject<DateTime> DateTime::Current(bool utc /* = false */) {
|
|
return NEWOBJ(DateTime)(time(0), utc);
|
|
}
|
|
|
|
static const StaticString s_year("year");
|
|
static const StaticString s_month("month");
|
|
static const StaticString s_day("day");
|
|
static const StaticString s_hour("hour");
|
|
static const StaticString s_minute("minute");
|
|
static const StaticString s_second("second");
|
|
static const StaticString s_zone("zone");
|
|
static const StaticString s_zone_type("zone_type");
|
|
static const StaticString s_fraction("fraction");
|
|
static const StaticString s_warning_count("warning_count");
|
|
static const StaticString s_warnings("warnings");
|
|
static const StaticString s_error_count("error_count");
|
|
static const StaticString s_errors("errors");
|
|
static const StaticString s_is_localtime("is_localtime");
|
|
static const StaticString s_is_dst("is_dst");
|
|
static const StaticString s_tz_abbr("tz_abbr");
|
|
static const StaticString s_tz_id("tz_id");
|
|
static const StaticString s_weekday("weekday");
|
|
static const StaticString s_relative("relative");
|
|
static const StaticString s_tm_sec("tm_sec");
|
|
static const StaticString s_tm_min("tm_min");
|
|
static const StaticString s_tm_hour("tm_hour");
|
|
static const StaticString s_tm_mday("tm_mday");
|
|
static const StaticString s_tm_mon("tm_mon");
|
|
static const StaticString s_tm_year("tm_year");
|
|
static const StaticString s_tm_wday("tm_wday");
|
|
static const StaticString s_tm_yday("tm_yday");
|
|
static const StaticString s_unparsed("unparsed");
|
|
|
|
#define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
|
|
if ((int)parsed_time->elem == -99999) { \
|
|
ret.set(name, false); \
|
|
} else { \
|
|
ret.set(name, (int)parsed_time->elem); \
|
|
}
|
|
|
|
Array DateTime::Parse(CStrRef datetime) {
|
|
struct timelib_error_container *error;
|
|
timelib_time *parsed_time =
|
|
timelib_strtotime((char*)datetime.data(), datetime.size(), &error,
|
|
TimeZone::GetDatabase());
|
|
|
|
Array ret;
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_year, y);
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_month, m);
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_day, d);
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_hour, h);
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_minute, i);
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_second, s);
|
|
|
|
if (parsed_time->f == -99999) {
|
|
ret.set(s_fraction, false);
|
|
} else {
|
|
ret.set(s_fraction, parsed_time->f);
|
|
}
|
|
|
|
setLastErrors(error);
|
|
{
|
|
Array warnings = DateTime::getLastWarnings();
|
|
ret.set(s_warning_count, warnings.size());
|
|
ret.set(s_warnings, warnings);
|
|
}
|
|
{
|
|
Array errors = DateTime::getLastErrors();
|
|
ret.set(s_error_count, errors.size());
|
|
ret.set(s_errors, errors);
|
|
}
|
|
|
|
ret.set(s_is_localtime, (bool)parsed_time->is_localtime);
|
|
if (parsed_time->is_localtime) {
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone_type, zone_type);
|
|
switch (parsed_time->zone_type) {
|
|
case TIMELIB_ZONETYPE_OFFSET:
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone, z);
|
|
ret.set(s_is_dst, (bool)parsed_time->dst);
|
|
break;
|
|
case TIMELIB_ZONETYPE_ID:
|
|
if (parsed_time->tz_abbr) {
|
|
ret.set(s_tz_abbr, String(parsed_time->tz_abbr, CopyString));
|
|
}
|
|
if (parsed_time->tz_info) {
|
|
ret.set(s_tz_id, String(parsed_time->tz_info->name, CopyString));
|
|
}
|
|
break;
|
|
case TIMELIB_ZONETYPE_ABBR:
|
|
PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(s_zone, z);
|
|
ret.set(s_is_dst, (bool)parsed_time->dst);
|
|
ret.set(s_tz_abbr, String(parsed_time->tz_abbr, CopyString));
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
Array element;
|
|
if (parsed_time->have_relative) {
|
|
element.set(s_year, parsed_time->relative.y);
|
|
element.set(s_month, parsed_time->relative.m);
|
|
element.set(s_day, parsed_time->relative.d);
|
|
element.set(s_hour, parsed_time->relative.h);
|
|
element.set(s_minute, parsed_time->relative.i);
|
|
element.set(s_second, parsed_time->relative.s);
|
|
#if defined(TIMELIB_VERSION)
|
|
if (parsed_time->relative.have_weekday_relative) {
|
|
#else
|
|
if (parsed_time->have_weekday_relative) {
|
|
#endif
|
|
element.set(s_weekday, parsed_time->relative.weekday);
|
|
}
|
|
ret.set(s_relative, element);
|
|
}
|
|
}
|
|
|
|
timelib_time_dtor(parsed_time);
|
|
return ret;
|
|
}
|
|
|
|
Array DateTime::Parse(CStrRef ts, CStrRef format) {
|
|
struct tm parsed_time;
|
|
memset(&parsed_time, 0, sizeof(parsed_time));
|
|
char *unparsed_part = strptime(ts.data(), format.data(), &parsed_time);
|
|
if (unparsed_part == nullptr) {
|
|
return Array();
|
|
}
|
|
|
|
ArrayInit ret(9);
|
|
ret.set(s_tm_sec, parsed_time.tm_sec);
|
|
ret.set(s_tm_min, parsed_time.tm_min);
|
|
ret.set(s_tm_hour, parsed_time.tm_hour);
|
|
ret.set(s_tm_mday, parsed_time.tm_mday);
|
|
ret.set(s_tm_mon, parsed_time.tm_mon);
|
|
ret.set(s_tm_year, parsed_time.tm_year);
|
|
ret.set(s_tm_wday, parsed_time.tm_wday);
|
|
ret.set(s_tm_yday, parsed_time.tm_yday);
|
|
ret.set(s_unparsed, String(unparsed_part, CopyString));
|
|
return ret.create();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors
|
|
|
|
DateTime::DateTime() : m_timestamp(-1), m_timestampSet(false) {
|
|
m_time = TimePtr(timelib_time_ctor(), time_deleter());
|
|
setTimezone(TimeZone::Current());
|
|
}
|
|
|
|
DateTime::DateTime(int64_t timestamp, bool utc /* = false */) {
|
|
fromTimeStamp(timestamp, utc);
|
|
}
|
|
|
|
void DateTime::fromTimeStamp(int64_t timestamp, bool utc /* = false */) {
|
|
m_timestamp = timestamp;
|
|
m_timestampSet = true;
|
|
|
|
timelib_time *t = timelib_time_ctor();
|
|
if (utc) {
|
|
timelib_unixtime2gmt(t, (timelib_sll)m_timestamp);
|
|
} else {
|
|
m_tz = TimeZone::Current();
|
|
t->tz_info = m_tz->get();
|
|
t->zone_type = TIMELIB_ZONETYPE_ID;
|
|
timelib_unixtime2local(t, (timelib_sll)m_timestamp);
|
|
}
|
|
m_time = TimePtr(t, time_deleter());
|
|
}
|
|
|
|
void DateTime::sweep() {
|
|
m_time.reset();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// informational
|
|
|
|
int DateTime::beat() const {
|
|
int retval = (((((long)m_time->sse)-(((long)m_time->sse) -
|
|
((((long)m_time->sse) % 86400) +
|
|
3600))) * 10) / 864);
|
|
while (retval < 0) {
|
|
retval += 1000;
|
|
}
|
|
retval = retval % 1000;
|
|
return retval;
|
|
}
|
|
|
|
int DateTime::dow() const {
|
|
return timelib_day_of_week(year(), month(), day());
|
|
}
|
|
|
|
int DateTime::doy() const {
|
|
return timelib_day_of_year(year(), month(), day());
|
|
}
|
|
|
|
int DateTime::isoWeek() const {
|
|
timelib_sll iw, iy;
|
|
timelib_isoweek_from_date(year(), month(), day(), &iw, &iy);
|
|
return iw;
|
|
}
|
|
|
|
int DateTime::isoYear() const {
|
|
timelib_sll iw, iy;
|
|
timelib_isoweek_from_date(year(), month(), day(), &iw, &iy);
|
|
return iy;
|
|
}
|
|
|
|
int DateTime::isoDow() const {
|
|
return timelib_iso_day_of_week(year(), month(), day());
|
|
}
|
|
|
|
int DateTime::offset() const {
|
|
if (local()) {
|
|
switch (m_time->zone_type) {
|
|
case TIMELIB_ZONETYPE_ABBR:
|
|
case TIMELIB_ZONETYPE_OFFSET:
|
|
return (m_time->z - (m_time->dst * 60)) * -60;
|
|
default:
|
|
{
|
|
bool error;
|
|
timelib_time_offset *offset =
|
|
timelib_get_time_zone_info(toTimeStamp(error), m_tz->get());
|
|
int ret = offset->offset;
|
|
timelib_time_offset_dtor(offset);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *DateTime::weekdayName() const {
|
|
return GetWeekdayName(year(), month(), day());
|
|
}
|
|
|
|
const char *DateTime::shortWeekdayName() const {
|
|
return GetShortWeekdayName(year(), month(), day());
|
|
}
|
|
|
|
const char *DateTime::monthName() const {
|
|
return MonthNames[month() - 1];
|
|
}
|
|
|
|
const char *DateTime::shortMonthName() const {
|
|
return ShortMonthNames[month() - 1];
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// modifications
|
|
|
|
void DateTime::update() {
|
|
if (utc()) {
|
|
timelib_update_ts(m_time.get(), nullptr);
|
|
} else {
|
|
timelib_update_ts(m_time.get(), m_tz->get());
|
|
}
|
|
m_timestamp = 0;
|
|
m_timestampSet = false;
|
|
}
|
|
|
|
void DateTime::set(int hou, int min, int sec, int mon, int day, int yea) {
|
|
/* Fill in the new data */
|
|
if (yea != INT_MAX) {
|
|
if (yea < 70) {
|
|
yea += 2000;
|
|
} else if (yea >= 70 && yea <= 100) {
|
|
yea += 1900;
|
|
}
|
|
m_time->y = yea;
|
|
}
|
|
if (day != INT_MAX) m_time->d = day;
|
|
if (mon != INT_MAX) m_time->m = mon;
|
|
if (sec != INT_MAX) m_time->s = sec;
|
|
if (min != INT_MAX) m_time->i = min;
|
|
if (hou != INT_MAX) m_time->h = hou;
|
|
update();
|
|
}
|
|
|
|
void DateTime::setDate(int y, int m, int d) {
|
|
m_time->y = y;
|
|
m_time->m = m;
|
|
m_time->d = d;
|
|
update();
|
|
}
|
|
|
|
void DateTime::setISODate(int y, int w, int d /* = 1 */) {
|
|
m_time->y = y;
|
|
m_time->m = 1;
|
|
m_time->d = 1;
|
|
m_time->relative.d = timelib_daynr_from_weeknr(y, w, d);
|
|
m_time->have_relative = 1;
|
|
update();
|
|
}
|
|
|
|
void DateTime::setTime(int hour, int minute, int second) {
|
|
m_time->h = hour;
|
|
m_time->i = minute;
|
|
m_time->s = second;
|
|
update();
|
|
}
|
|
|
|
void DateTime::setTimezone(SmartObject<TimeZone> timezone) {
|
|
if (!timezone.isNull()) {
|
|
m_tz = timezone->cloneTimeZone();
|
|
if (m_tz.get() && m_tz->get()) {
|
|
timelib_set_timezone(m_time.get(), m_tz->get());
|
|
timelib_unixtime2local(m_time.get(), m_time->sse);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DateTime::modify(CStrRef diff) {
|
|
timelib_time *tmp_time = timelib_strtotime((char*)diff.data(), diff.size(),
|
|
nullptr, TimeZone::GetDatabase());
|
|
internalModify(&(tmp_time->relative), tmp_time->have_relative, 1);
|
|
timelib_time_dtor(tmp_time);
|
|
}
|
|
|
|
void DateTime::internalModify(timelib_rel_time *rel,
|
|
bool have_relative, char bias) {
|
|
m_time->relative.y = rel->y * bias;
|
|
m_time->relative.m = rel->m * bias;
|
|
m_time->relative.d = rel->d * bias;
|
|
m_time->relative.h = rel->h * bias;
|
|
m_time->relative.i = rel->i * bias;
|
|
m_time->relative.s = rel->s * bias;
|
|
m_time->relative.weekday = rel->weekday;
|
|
m_time->have_relative = have_relative;
|
|
#ifdef TIMELIB_HAVE_INTERVAL
|
|
m_time->relative.have_weekday_relative = rel->have_weekday_relative;
|
|
#endif
|
|
m_time->sse_uptodate = 0;
|
|
update();
|
|
timelib_update_from_sse(m_time.get());
|
|
}
|
|
|
|
void DateTime::add(const SmartObject<DateInterval> &interval) {
|
|
timelib_rel_time *rel = interval->get();
|
|
internalModify(rel, true, TIMELIB_REL_INVERT(rel) ? -1 : 1);
|
|
}
|
|
|
|
void DateTime::sub(const SmartObject<DateInterval> &interval) {
|
|
timelib_rel_time *rel = interval->get();
|
|
internalModify(rel, true, TIMELIB_REL_INVERT(rel) ? 1 : -1);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// conversions
|
|
|
|
void DateTime::toTm(struct tm &ta) const {
|
|
ta.tm_sec = second();
|
|
ta.tm_min = minute();
|
|
ta.tm_hour = hour();
|
|
ta.tm_mday = day();
|
|
ta.tm_mon = month() - 1;
|
|
ta.tm_year = year() - 1900;
|
|
ta.tm_wday = dow();
|
|
ta.tm_yday = doy();
|
|
if (utc()) {
|
|
ta.tm_isdst = 0;
|
|
ta.tm_gmtoff = 0;
|
|
ta.tm_zone = "GMT";
|
|
} else {
|
|
timelib_time_offset *offset =
|
|
timelib_get_time_zone_info(m_time->sse, m_time->tz_info);
|
|
ta.tm_isdst = offset->is_dst;
|
|
ta.tm_gmtoff = offset->offset;
|
|
ta.tm_zone = offset->abbr;
|
|
timelib_time_offset_dtor(offset);
|
|
}
|
|
}
|
|
|
|
int64_t DateTime::toTimeStamp(bool &err) const {
|
|
err = false;
|
|
if (!m_timestampSet) {
|
|
int error;
|
|
m_timestamp = timelib_date_to_int(m_time.get(), &error);
|
|
if (error) {
|
|
err = true;
|
|
} else {
|
|
m_timestampSet = true;
|
|
}
|
|
}
|
|
return m_timestamp;
|
|
}
|
|
|
|
int64_t DateTime::toInteger(char format) const {
|
|
bool error;
|
|
switch (format) {
|
|
case 'd':
|
|
case 'j': return day();
|
|
case 'w': return dow();
|
|
case 'z': return doy();
|
|
case 'W': return isoWeek();
|
|
case 'm':
|
|
case 'n': return month();
|
|
case 't': return DaysInMonth(year(), month());
|
|
case 'L': return DateTime::IsLeap(year());
|
|
case 'y': return (year() % 100);
|
|
case 'Y': return year();
|
|
case 'B': return beat();
|
|
case 'g':
|
|
case 'h': return hour12();
|
|
case 'H':
|
|
case 'G': return hour();
|
|
case 'i': return minute();
|
|
case 's': return second();
|
|
case 'I': return (!utc() && m_tz->dst(toTimeStamp(error))) ? 1 : 0;
|
|
case 'Z': return utc() ? 0 : m_tz->offset(toTimeStamp(error));
|
|
case 'U': return toTimeStamp(error);
|
|
}
|
|
throw_invalid_argument("unknown format char: %d", (int)format);
|
|
return -1;
|
|
}
|
|
|
|
String DateTime::toString(CStrRef format, bool stdc /* = false */) const {
|
|
if (format.empty()) return String();
|
|
return stdc ? stdcFormat(format) : rfcFormat(format);
|
|
}
|
|
|
|
String DateTime::toString(DateFormat format) const {
|
|
switch (format) {
|
|
case RFC822: return rfcFormat(DateFormatRFC822);
|
|
case RFC850: return rfcFormat(DateFormatRFC850);
|
|
case RFC1036: return rfcFormat(DateFormatRFC1036);
|
|
case RFC1123: return rfcFormat(DateFormatRFC1123);
|
|
case RFC2822: return rfcFormat(DateFormatRFC2822);
|
|
case RFC3339: return rfcFormat(DateFormatRFC3339);
|
|
case ISO8601: return rfcFormat(DateFormatISO8601);
|
|
case Cookie: return rfcFormat(DateFormatCookie);
|
|
case HttpHeader: return rfcFormat(DateFormatHttpHeader);
|
|
default:
|
|
assert(false);
|
|
}
|
|
throw_invalid_argument("format: %d", format);
|
|
return String();
|
|
}
|
|
|
|
String DateTime::rfcFormat(CStrRef format) const {
|
|
StringBuffer s;
|
|
bool rfc_colon = false;
|
|
bool error;
|
|
for (int i = 0; i < format.size(); i++) {
|
|
switch (format.charAt(i)) {
|
|
case 'd': s.printf("%02d", day()); break;
|
|
case 'D': s.append(shortWeekdayName()); break;
|
|
case 'j': s.append(day()); break;
|
|
case 'l': s.append(weekdayName()); break;
|
|
case 'S': s.append(OrdinalSuffix(day())); break;
|
|
case 'w': s.append(dow()); break;
|
|
case 'N': s.append(isoDow()); break;
|
|
case 'z': s.append(doy()); break;
|
|
case 'W': s.printf("%02d", isoWeek()); break;
|
|
case 'o': s.append(isoYear()); break;
|
|
case 'F': s.append(monthName()); break;
|
|
case 'm': s.printf("%02d", month()); break;
|
|
case 'M': s.append(shortMonthName()); break;
|
|
case 'n': s.append(month()); break;
|
|
case 't': s.append(DaysInMonth(year(), month())); break;
|
|
case 'L': s.append(IsLeap(year())); break;
|
|
case 'y': s.printf("%02d", year() % 100); break;
|
|
case 'Y': s.printf("%s%04d", year() < 0 ? "-" : "", abs(year()));
|
|
break;
|
|
case 'a': s.append(hour() >= 12 ? "pm" : "am"); break;
|
|
case 'A': s.append(hour() >= 12 ? "PM" : "AM"); break;
|
|
case 'B': s.printf("%03d", beat()); break;
|
|
case 'g': s.append((hour() % 12) ? (int)hour() % 12 : 12); break;
|
|
case 'G': s.append(hour()); break;
|
|
case 'h': s.printf("%02d", (hour() % 12) ? (int)hour() % 12 : 12); break;
|
|
case 'H': s.printf("%02d", (int)hour()); break;
|
|
case 'i': s.printf("%02d", (int)minute()); break;
|
|
case 's': s.printf("%02d", (int)second()); break;
|
|
case 'u': s.printf("%06d", (int)floor(fraction() * 1000000)); break;
|
|
case 'I': s.append(!utc() && m_tz->dst(toTimeStamp(error)) ? 1 : 0);
|
|
break;
|
|
case 'P': rfc_colon = true; /* break intentionally missing */
|
|
case 'O':
|
|
if (utc()) {
|
|
s.printf("+0%s0", rfc_colon ? ":" : "");
|
|
} else {
|
|
int offset = m_tz->offset(toTimeStamp(error));
|
|
s.printf("%c%02d%s%02d",
|
|
(offset < 0 ? '-' : '+'), abs(offset / 3600),
|
|
rfc_colon ? ":" : "", abs((offset % 3600) / 60));
|
|
}
|
|
break;
|
|
case 'T': s.append(utc() ? "GMT" : m_time->tz_abbr); break;
|
|
case 'e': s.append(utc() ? "UTC" : m_tz->name()); break;
|
|
case 'Z': s.append(utc() ? 0 : m_tz->offset(toTimeStamp(error)));
|
|
break;
|
|
case 'c':
|
|
if (utc()) {
|
|
s.printf("%04d-%02d-%02dT%02d:%02d:%02d+0:0",
|
|
year(), month(), day(), hour(), minute(), second());
|
|
} else {
|
|
int offset = m_tz->offset(toTimeStamp(error));
|
|
s.printf("%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
|
|
year(), month(), day(), hour(), minute(), second(),
|
|
(offset < 0 ? '-' : '+'),
|
|
abs(offset / 3600), abs((offset % 3600) / 60));
|
|
}
|
|
break;
|
|
case 'r':
|
|
if (utc()) {
|
|
s.printf("%3s, %02d %3s %04d %02d:%02d:%02d +00",
|
|
shortWeekdayName(), day(), shortMonthName(), year(),
|
|
hour(), minute(), second());
|
|
} else {
|
|
int offset = m_tz->offset(toTimeStamp(error));
|
|
s.printf("%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d",
|
|
shortWeekdayName(), day(), shortMonthName(), year(),
|
|
hour(), minute(), second(),
|
|
(offset < 0 ? '-' : '+'),
|
|
abs(offset / 3600), abs((offset % 3600) / 60));
|
|
}
|
|
break;
|
|
case 'U': s.printf("%lld", toTimeStamp(error)); break;
|
|
case '\\':
|
|
if (i < format.size()) i++; /* break intentionally missing */
|
|
default:
|
|
s.append(format[i]);
|
|
break;
|
|
}
|
|
}
|
|
return s.detach();
|
|
}
|
|
|
|
String DateTime::stdcFormat(CStrRef format) const {
|
|
struct tm ta;
|
|
timelib_time_offset *offset = nullptr;
|
|
ta.tm_sec = second();
|
|
ta.tm_min = minute();
|
|
ta.tm_hour = hour();
|
|
ta.tm_mday = day();
|
|
ta.tm_mon = month() - 1;
|
|
ta.tm_year = year() - 1900;
|
|
ta.tm_wday = dow();
|
|
ta.tm_yday = doy();
|
|
if (utc()) {
|
|
ta.tm_isdst = 0;
|
|
ta.tm_gmtoff = 0;
|
|
ta.tm_zone = "GMT";
|
|
} else {
|
|
offset = timelib_get_time_zone_info(m_time->sse, m_time->tz_info);
|
|
ta.tm_isdst = offset->is_dst;
|
|
ta.tm_gmtoff = offset->offset;
|
|
ta.tm_zone = offset->abbr;
|
|
}
|
|
|
|
int max_reallocs = 5;
|
|
size_t buf_len = 256, real_len;
|
|
char *buf = (char *)malloc(buf_len);
|
|
while ((real_len = strftime(buf, buf_len, format.data(), &ta)) == buf_len ||
|
|
real_len == 0) {
|
|
buf_len *= 2;
|
|
free(buf);
|
|
buf = (char *)malloc(buf_len);
|
|
if (!--max_reallocs) {
|
|
break;
|
|
}
|
|
}
|
|
if (!utc()) {
|
|
timelib_time_offset_dtor(offset);
|
|
}
|
|
if (real_len && real_len != buf_len) {
|
|
return String(buf, real_len, AttachString);
|
|
}
|
|
free(buf);
|
|
throw_invalid_argument("format: (over internal buffer)");
|
|
return String();
|
|
}
|
|
|
|
Array DateTime::toArray(ArrayFormat format) const {
|
|
Array ret;
|
|
bool error;
|
|
switch (format) {
|
|
case TimeMap:
|
|
ret.set("seconds", second());
|
|
ret.set("minutes", minute());
|
|
ret.set("hours", hour());
|
|
ret.set("mday", day());
|
|
ret.set("wday", dow());
|
|
ret.set("mon", month());
|
|
ret.set("year", year());
|
|
ret.set("yday", doy());
|
|
ret.set("weekday", weekdayName());
|
|
ret.set("month", monthName());
|
|
ret.set(0, toTimeStamp(error));
|
|
break;
|
|
case TmMap:
|
|
{
|
|
struct tm tm;
|
|
toTm(tm);
|
|
ret.set("tm_sec", tm.tm_sec);
|
|
ret.set("tm_min", tm.tm_min);
|
|
ret.set("tm_hour", tm.tm_hour);
|
|
ret.set("tm_mday", tm.tm_mday);
|
|
ret.set("tm_mon", tm.tm_mon);
|
|
ret.set("tm_year", tm.tm_year);
|
|
ret.set("tm_wday", tm.tm_wday);
|
|
ret.set("tm_yday", tm.tm_yday);
|
|
ret.set("tm_isdst", tm.tm_isdst);
|
|
}
|
|
break;
|
|
case TmVector:
|
|
{
|
|
struct tm tm;
|
|
toTm(tm);
|
|
ret.append(tm.tm_sec);
|
|
ret.append(tm.tm_min);
|
|
ret.append(tm.tm_hour);
|
|
ret.append(tm.tm_mday);
|
|
ret.append(tm.tm_mon);
|
|
ret.append(tm.tm_year);
|
|
ret.append(tm.tm_wday);
|
|
ret.append(tm.tm_yday);
|
|
ret.append(tm.tm_isdst);
|
|
}
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool DateTime::fromString(CStrRef input, SmartObject<TimeZone> tz,
|
|
const char* format /*=NUL*/) {
|
|
struct timelib_error_container *error;
|
|
timelib_time *t;
|
|
if (format) {
|
|
#ifdef TIMELIB_HAVE_INTERVAL
|
|
t = timelib_parse_from_format((char*)format, (char*)input.data(),
|
|
input.size(), &error, TimeZone::GetDatabase());
|
|
#else
|
|
throw NotImplementedException("timelib version too old");
|
|
#endif
|
|
} else {
|
|
t = timelib_strtotime((char*)input.data(), input.size(),
|
|
&error, TimeZone::GetDatabase());
|
|
}
|
|
int error1 = error->error_count;
|
|
setLastErrors(error);
|
|
|
|
if (m_timestamp == -1) {
|
|
fromTimeStamp(0);
|
|
}
|
|
if (tz.get()) {
|
|
setTimezone(tz);
|
|
} else {
|
|
setTimezone(TimeZone::Current());
|
|
}
|
|
|
|
// needed if any date part is missing
|
|
timelib_fill_holes(t, m_time.get(), 0);
|
|
timelib_update_ts(t, m_tz->get());
|
|
|
|
int error2;
|
|
m_timestamp = timelib_date_to_int(t, &error2);
|
|
if (error1 || error2) {
|
|
timelib_tzinfo_dtor(t->tz_info);
|
|
timelib_time_dtor(t);
|
|
return false;
|
|
}
|
|
|
|
m_time = TimePtr(t, time_deleter());
|
|
m_tz = NEWOBJ(TimeZone)(t->tz_info);
|
|
return true;
|
|
}
|
|
|
|
SmartObject<DateTime> DateTime::cloneDateTime() const {
|
|
bool err;
|
|
SmartObject<DateTime> ret(NEWOBJ(DateTime)(toTimeStamp(err), true));
|
|
ret->setTimezone(m_tz);
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// comparison
|
|
|
|
SmartObject<DateInterval> DateTime::diff(SmartObject<DateTime> datetime2, bool absolute) {
|
|
#ifdef TIMELIB_HAVE_INTERVAL
|
|
timelib_rel_time *rel = timelib_diff(m_time.get(), datetime2.get()->m_time.get());
|
|
if (absolute) {
|
|
TIMELIB_REL_INVERT_SET(rel, 0);
|
|
}
|
|
SmartObject<DateInterval> di(NEWOBJ(DateInterval)(rel));
|
|
return di;
|
|
#else
|
|
throw NotImplementedException("timelib version too old");
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// sun
|
|
|
|
static const StaticString s_sunrise("sunrise");
|
|
static const StaticString s_sunset("sunset");
|
|
static const StaticString s_transit("transit");
|
|
static const StaticString s_civil_twilight_begin("civil_twilight_begin");
|
|
static const StaticString s_civil_twilight_end("civil_twilight_end");
|
|
static const StaticString s_nautical_twilight_begin("nautical_twilight_begin");
|
|
static const StaticString s_nautical_twilight_end("nautical_twilight_end");
|
|
static const StaticString
|
|
s_astronomical_twilight_begin("astronomical_twilight_begin");
|
|
static const StaticString
|
|
s_astronomical_twilight_end("astronomical_twilight_end");
|
|
|
|
Array DateTime::getSunInfo(double latitude, double longitude) const {
|
|
Array ret;
|
|
timelib_sll sunrise, sunset, transit;
|
|
double ddummy;
|
|
|
|
/* Get sun up/down and transit */
|
|
int rs = timelib_astro_rise_set_altitude(m_time.get(), longitude, latitude,
|
|
-35.0/60, 1, &ddummy, &ddummy,
|
|
&sunrise, &sunset, &transit);
|
|
switch (rs) {
|
|
case -1: /* always below */
|
|
ret.set(s_sunrise, false);
|
|
ret.set(s_sunset, false);
|
|
break;
|
|
case 1: /* always above */
|
|
ret.set(s_sunrise, true);
|
|
ret.set(s_sunset, true);
|
|
break;
|
|
default:
|
|
ret.set(s_sunrise, sunrise);
|
|
ret.set(s_sunset, sunset);
|
|
}
|
|
ret.set(s_transit, transit);
|
|
|
|
/* Get civil twilight */
|
|
rs = timelib_astro_rise_set_altitude(m_time.get(), longitude, latitude,
|
|
-6.0, 0,
|
|
&ddummy, &ddummy, &sunrise, &sunset,
|
|
&transit);
|
|
switch (rs) {
|
|
case -1: /* always below */
|
|
ret.set(s_civil_twilight_begin, false);
|
|
ret.set(s_civil_twilight_end, false);
|
|
break;
|
|
case 1: /* always above */
|
|
ret.set(s_civil_twilight_begin, true);
|
|
ret.set(s_civil_twilight_end, true);
|
|
break;
|
|
default:
|
|
ret.set(s_civil_twilight_begin, sunrise);
|
|
ret.set(s_civil_twilight_end, sunset);
|
|
}
|
|
|
|
/* Get nautical twilight */
|
|
rs = timelib_astro_rise_set_altitude(m_time.get(), longitude, latitude,
|
|
-12.0, 0,
|
|
&ddummy, &ddummy, &sunrise, &sunset,
|
|
&transit);
|
|
switch (rs) {
|
|
case -1: /* always below */
|
|
ret.set(s_nautical_twilight_begin, false);
|
|
ret.set(s_nautical_twilight_end, false);
|
|
break;
|
|
case 1: /* always above */
|
|
ret.set(s_nautical_twilight_begin, true);
|
|
ret.set(s_nautical_twilight_end, true);
|
|
break;
|
|
default:
|
|
ret.set(s_nautical_twilight_begin, sunrise);
|
|
ret.set(s_nautical_twilight_end, sunset);
|
|
}
|
|
|
|
/* Get astronomical twilight */
|
|
rs = timelib_astro_rise_set_altitude(m_time.get(), longitude, latitude,
|
|
-18.0, 0,
|
|
&ddummy, &ddummy, &sunrise, &sunset,
|
|
&transit);
|
|
switch (rs) {
|
|
case -1: /* always below */
|
|
ret.set(s_astronomical_twilight_begin, false);
|
|
ret.set(s_astronomical_twilight_end, false);
|
|
break;
|
|
case 1: /* always above */
|
|
ret.set(s_astronomical_twilight_begin, true);
|
|
ret.set(s_astronomical_twilight_end, true);
|
|
break;
|
|
default:
|
|
ret.set(s_astronomical_twilight_begin, sunrise);
|
|
ret.set(s_astronomical_twilight_end, sunset);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant DateTime::getSunInfo(SunInfoFormat retformat,
|
|
double latitude, double longitude,
|
|
double zenith, double utc_offset,
|
|
bool calc_sunset) const {
|
|
if (retformat != ReturnTimeStamp &&
|
|
retformat != ReturnString &&
|
|
retformat != ReturnDouble) {
|
|
raise_warning("Wrong return format given, pick one of "
|
|
"SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING or "
|
|
"SUNFUNCS_RET_DOUBLE");
|
|
return false;
|
|
}
|
|
bool error;
|
|
double altitude = 90 - zenith;
|
|
if (utc_offset == -99999.0) {
|
|
if (utc()) {
|
|
utc_offset = 0;
|
|
} else {
|
|
utc_offset = m_tz->offset(toTimeStamp(error)) / 3600;
|
|
}
|
|
}
|
|
|
|
double h_rise, h_set; timelib_sll sunrise, sunset, transit;
|
|
int rs = timelib_astro_rise_set_altitude(m_time.get(), longitude, latitude,
|
|
altitude, altitude > -1 ? 1 : 0,
|
|
&h_rise, &h_set, &sunrise, &sunset,
|
|
&transit);
|
|
if (rs != 0) {
|
|
return false;
|
|
}
|
|
|
|
if (retformat == ReturnTimeStamp) {
|
|
return calc_sunset ? sunset : sunrise;
|
|
}
|
|
|
|
double N = (calc_sunset ? h_set : h_rise) + utc_offset;
|
|
if (N > 24 || N < 0) {
|
|
N -= floor(N / 24) * 24;
|
|
}
|
|
|
|
if (retformat == ReturnString) {
|
|
char retstr[6];
|
|
snprintf(retstr, sizeof(retstr),
|
|
"%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
|
|
return String(retstr, CopyString);
|
|
}
|
|
|
|
assert(retformat == ReturnDouble);
|
|
return N;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|