9edc07112b
When object support was first added to HHVM, a class named "Instance" was introduced (deriving from ObjectData) to represent instances of user defined classes. Since then, things have evolved and HPHPc and HPHPi have been retired, and now there really is no needed to have ObjectData and Instance be separate classes anymore. As a first step towards merging ObjectData and Instance together, this diff puts their definitions in the same .h file and puts their implementations in the same .cpp file. A few small changes were necessary to fix issues with cyclical includes: (1) Repo/emitter related parts of class.cpp and class.h were moved to class-emit.cpp and class-emit.h; (2) the contents of "vm/core_types.h" was moved to "base/types.h"; and (3) a few functions that didn't appear to be hot were moved from .h files and the corresponding .cpp files.
956 linhas
29 KiB
C++
956 linhas
29 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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 "hphp/runtime/base/time/datetime.h"
|
|
#include "hphp/runtime/base/time/dateinterval.h"
|
|
#include "hphp/runtime/base/complex_types.h"
|
|
#include "hphp/runtime/base/util/string_buffer.h"
|
|
#include "hphp/runtime/base/runtime_error.h"
|
|
#include "hphp/runtime/base/type_conversions.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/array/array_init.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);
|
|
}
|
|
|
|
const StaticString
|
|
s_year("year"),
|
|
s_month("month"),
|
|
s_day("day"),
|
|
s_hour("hour"),
|
|
s_minute("minute"),
|
|
s_second("second"),
|
|
s_zone("zone"),
|
|
s_zone_type("zone_type"),
|
|
s_fraction("fraction"),
|
|
s_warning_count("warning_count"),
|
|
s_warnings("warnings"),
|
|
s_error_count("error_count"),
|
|
s_errors("errors"),
|
|
s_is_localtime("is_localtime"),
|
|
s_is_dst("is_dst"),
|
|
s_tz_abbr("tz_abbr"),
|
|
s_tz_id("tz_id"),
|
|
s_weekday("weekday"),
|
|
s_relative("relative"),
|
|
s_tm_sec("tm_sec"),
|
|
s_tm_min("tm_min"),
|
|
s_tm_hour("tm_hour"),
|
|
s_tm_mday("tm_mday"),
|
|
s_tm_mon("tm_mon"),
|
|
s_tm_year("tm_year"),
|
|
s_tm_wday("tm_wday"),
|
|
s_tm_yday("tm_yday"),
|
|
s_tm_isdst("tm_isdst"),
|
|
s_unparsed("unparsed"),
|
|
s_seconds("seconds"),
|
|
s_minutes("minutes"),
|
|
s_hours("hours"),
|
|
s_mday("mday"),
|
|
s_wday("wday"),
|
|
s_mon("mon"),
|
|
s_yday("yday");
|
|
|
|
#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(), TimeZone::GetTimeZoneInfoRaw);
|
|
|
|
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(),
|
|
TimeZone::GetTimeZoneInfoRaw);
|
|
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 DateFormat::RFC822: return rfcFormat(DateFormatRFC822);
|
|
case DateFormat::RFC850: return rfcFormat(DateFormatRFC850);
|
|
case DateFormat::RFC1036: return rfcFormat(DateFormatRFC1036);
|
|
case DateFormat::RFC1123: return rfcFormat(DateFormatRFC1123);
|
|
case DateFormat::RFC2822: return rfcFormat(DateFormatRFC2822);
|
|
case DateFormat::RFC3339: return rfcFormat(DateFormatRFC3339);
|
|
case DateFormat::ISO8601: return rfcFormat(DateFormatISO8601);
|
|
case DateFormat::Cookie: return rfcFormat(DateFormatCookie);
|
|
case DateFormat::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("%" PRId64, 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 ArrayFormat::TimeMap:
|
|
ret.set(s_seconds, second());
|
|
ret.set(s_minutes, minute());
|
|
ret.set(s_hours, hour());
|
|
ret.set(s_mday, day());
|
|
ret.set(s_wday, dow());
|
|
ret.set(s_mon, month());
|
|
ret.set(s_year, year());
|
|
ret.set(s_yday, doy());
|
|
ret.set(s_weekday, weekdayName());
|
|
ret.set(s_month, monthName());
|
|
ret.set(0, toTimeStamp(error));
|
|
break;
|
|
case ArrayFormat::TmMap:
|
|
{
|
|
struct tm tm;
|
|
toTm(tm);
|
|
ret.set(s_tm_sec, tm.tm_sec);
|
|
ret.set(s_tm_min, tm.tm_min);
|
|
ret.set(s_tm_hour, tm.tm_hour);
|
|
ret.set(s_tm_mday, tm.tm_mday);
|
|
ret.set(s_tm_mon, tm.tm_mon);
|
|
ret.set(s_tm_year, tm.tm_year);
|
|
ret.set(s_tm_wday, tm.tm_wday);
|
|
ret.set(s_tm_yday, tm.tm_yday);
|
|
ret.set(s_tm_isdst, tm.tm_isdst);
|
|
}
|
|
break;
|
|
case ArrayFormat::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(),
|
|
TimeZone::GetTimeZoneInfoRaw);
|
|
#else
|
|
throw NotImplementedException("timelib version too old");
|
|
#endif
|
|
} else {
|
|
t = timelib_strtotime((char*)input.data(), input.size(),
|
|
&error, TimeZone::GetDatabase(),
|
|
TimeZone::GetTimeZoneInfoRaw);
|
|
}
|
|
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)(timelib_tzinfo_clone(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
|
|
|
|
const StaticString
|
|
s_sunrise("sunrise"),
|
|
s_sunset("sunset"),
|
|
s_transit("transit"),
|
|
s_civil_twilight_begin("civil_twilight_begin"),
|
|
s_civil_twilight_end("civil_twilight_end"),
|
|
s_nautical_twilight_begin("nautical_twilight_begin"),
|
|
s_nautical_twilight_end("nautical_twilight_end"),
|
|
s_astronomical_twilight_begin("astronomical_twilight_begin"),
|
|
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 != SunInfoFormat::ReturnTimeStamp &&
|
|
retformat != SunInfoFormat::ReturnString &&
|
|
retformat != SunInfoFormat::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 == SunInfoFormat::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 == SunInfoFormat::ReturnString) {
|
|
char retstr[6];
|
|
snprintf(retstr, sizeof(retstr),
|
|
"%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
|
|
return String(retstr, CopyString);
|
|
}
|
|
|
|
assert(retformat == SunInfoFormat::ReturnDouble);
|
|
return N;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|