d629562011-11-05Martin Nilsson #pike __REAL_VERSION__
319c192017-12-14Stephen R. van den Berg #pragma strict_types
3700122010-11-23Martin Stjernholm  //! This module contains special values used by various modules, e.g. //! a null value used both by @[Sql] and @[Standards.JSON]. //! //! In many ways these values should be considered constant, but it is //! possible for a program to replace them with extended versions, //! provided they don't break the behavior of the base classes defined //! here. Since there is no good mechanism to handle such extending in //! several steps, pike libraries should preferably ensure that the //! base classes defined here provide required functionality directly. //! //! @note //! Since resolving using the dot operator in e.g. @[Val.null] is //! done at compile time, replacement of these values often must take //! place very early (typically in a loader before the bulk of the //! pike code is compiled) to be effective in such cases. For this //! reason, pike libraries should use dynamic resolution through e.g. //! @expr{->@} or @expr{master()->resolv()@} instead.
7e6b342011-09-06Martin Stjernholm class Boolean //! Common base class for @[Val.True] and @[Val.False], mainly to //! facilitate typing. Do not create any instances of this. { constant is_val_boolean = 1; //! Nonzero recognition constant that can be used to recognize both //! @[Val.true] and @[Val.false]. string encode_json(); }
3700122010-11-23Martin Stjernholm class True //! Type for the @[Val.true] object. Do not create more instances of //! this - use @[Val.true] instead. {
7e6b342011-09-06Martin Stjernholm  inherit Boolean;
3700122010-11-23Martin Stjernholm  constant is_val_true = 1; //! Nonzero recognition constant. string encode_json() {return "true";} // The following shouldn't be necessary if there's only one // instance, but that might not always be the case. protected int __hash()
efae9f2017-12-21Martin Nilsson  { return 34123; }
3700122010-11-23Martin Stjernholm  protected int `== (mixed other)
efae9f2017-12-21Martin Nilsson  { return objectp (other) && [int]([object]other)->is_val_true; }
3700122010-11-23Martin Stjernholm  protected mixed cast (string type) { switch (type) { case "int": return 1; case "string": return "1"; default: error ("Cannot cast %O to %s.\n", this, type); } } protected string _sprintf (int flag) { return flag == 'O' && "Val.true"; } } class False //! Type for the @[Val.false] object. Do not create more instances of //! this - use @[Val.false] instead. {
7e6b342011-09-06Martin Stjernholm  inherit Boolean;
3700122010-11-23Martin Stjernholm  constant is_val_false = 1; //! Nonzero recognition constant. protected int `!() {return 1;} string encode_json() {return "false";} protected int __hash() {return 54634;} protected int `== (mixed other)
319c192017-12-14Stephen R. van den Berg  {return objectp (other) && [int]([object]other)->is_val_false;}
3700122010-11-23Martin Stjernholm  protected mixed cast (string type) { switch (type) { case "int": return 0; case "string": return "0"; default: error ("Cannot cast %O to %s.\n", this, type); } } protected string _sprintf (int flag) { return flag == 'O' && "Val.false"; } }
7e6b342011-09-06Martin Stjernholm Boolean true = True(); Boolean false = False();
de4d2e2017-12-14Stephen R. van den Berg //! Objects that represent the boolean values true and false. In a
3700122010-11-23Martin Stjernholm //! boolean context these evaluate to true and false, respectively. //! //! They produce @expr{1@} and @expr{0@}, respectively, when cast to //! integer, and @expr{"1"@} and @expr{"0"@} when cast to string. They //! do however not compare as equal to the integers 1 and 0, or any //! other values. @[Val.true] only compares (and hashes) as equal with //! other instances of @[True] (although there should be as few as //! possible). Similarly, @[Val.false] is only considered equal to //! other @[False] instances. //! //! @[Protocols.JSON] uses these objects to represent the JSON //! literals @expr{true@} and @expr{false@}.
2652e22011-08-31Martin Stjernholm //! //! @note //! The correct way to programmatically recognize these values is //! something like //! //! @code
de4d2e2017-12-14Stephen R. van den Berg //! if (objectp(something) && ([object]something)->is_val_true) ...
2652e22011-08-31Martin Stjernholm //! @endcode //! //! and //! //! @code
de4d2e2017-12-14Stephen R. van den Berg //! if (objectp(something) && ([object]something)->is_val_false) ...
2652e22011-08-31Martin Stjernholm //! @endcode //! //! respectively. See @[Val.null] for rationale.
0df91f2011-09-06Martin Stjernholm //! //! @note //! Pike natively uses integers (zero and non-zero) as booleans. These //! objects don't change that, and unless there's a particular reason //! to use these objects it's better to stick to e.g. 0 and 1 for //! boolean values - that is both more efficient and more in line with //! existing coding practice. These objects are intended for cases //! where integers and booleans occur in the same place and it is //! necessary to distinguish them.
3700122010-11-23Martin Stjernholm 
83045c2011-03-05Martin Stjernholm //! @class Null
3700122010-11-23Martin Stjernholm //! Type for the @[Val.null] object. Do not create more instances of //! this - use @[Val.null] instead.
83045c2011-03-05Martin Stjernholm constant Null = Builtin.Null;
3700122010-11-23Martin Stjernholm 
83045c2011-03-05Martin Stjernholm //! @endclass
3700122010-11-23Martin Stjernholm  Null null = Null(); //! Object that represents a null value. //! //! In general, null is a value that represents the lack of a real //! value in the domain of some type. For instance, in a type system //! with a null value, a variable of string type typically can hold //! any string and also null to signify no string at all (which is //! different from the empty string). Pike natively uses the integer 0 //! (zero) for this, but since 0 also is a real integer it is //! sometimes necessary to have a different value for null, and then //! this object is preferably used. //! //! This object is false in a boolean context. It does not cast to //! anything, and it is not equal to anything else but other instances //! of @[Null] (which should be avoided). //! //! This object is used by the @[Sql] module to represent SQL NULL, //! and it is used by @[Protocols.JSON] to represent the JSON literal //! @expr{null@}. //! //! @note //! Do not confuse the null value with @[UNDEFINED]. Although //! @[UNDEFINED] often is used to represent the lack of a real value, //! and it can be told apart from an ordinary zero integer with some //! effort, it is transient in nature (for instance, it automatically //! becomes an ordinary zero when inserted in a mapping). That makes //! it unsuitable for use as a reliable null value. //!
2652e22011-08-31Martin Stjernholm //! @note //! The correct way to programmatically recognize @[Val.null] is //! something like //! //! @code
de4d2e2017-12-14Stephen R. van den Berg //! if (objectp(something) && ([object]something)->is_val_null) ...
2652e22011-08-31Martin Stjernholm //! @endcode //! //! That way it's possible for other code to replace it with an //! extended class, or create their own variants which needs to behave //! like @[Val.null]. //!
3700122010-11-23Martin Stjernholm //! @fixme //! The Oracle glue currently uses static null objects which won't be //! affected if this object is replaced.
319c192017-12-14Stephen R. van den Berg 
3794dd2017-12-21Martin Nilsson #define NANOSECONDS 1000000000
319c192017-12-14Stephen R. van den Berg 
3794dd2017-12-21Martin Nilsson //! The local timezone without daylight-saving correction. //! @note //! This value is determined once at process start, and cached for //! the lifetime of the process. int local_timezone = lambda() { int tprobe = time(1); for(;; tprobe -= 4 * 30 * 24 * 3600) { // Go back 1/3 of a year mapping(string:int) tm = localtime(tprobe); if (!tm->isdst) // Find a spot without daylight-saving return tm->timezone; } }();
319c192017-12-14Stephen R. van den Berg 
3794dd2017-12-21Martin Nilsson //! @param timezone //! Timezone in seconds @b{west@} from UTC //! (must include daylight-saving time adjustment). //! @returns //! ISO formatted timezone @b{east@} from UTC. //! @seealso //! @[localtime()] final string iso_timezone(int timezone) { if (timezone) { string res; if (timezone < 0) timezone = -timezone, res = "+"; else res = "-"; res += sprintf("%02d", timezone / 3600); if (timezone %= 3600) { res += sprintf(":%02d", timezone / 60); if (timezone %= 60) res += sprintf(":%02d", timezone); } return res; } return ""; }
319c192017-12-14Stephen R. van den Berg 
3794dd2017-12-21Martin Nilsson //! @param tm //! Standard tm mapping, optionally extended with @expr{nsec@} for //! nanoseconds. //! @returns //! ISO formatted time. //! @seealso //! @[mktime()] string iso_time(mapping(string:int) tm) { string res = sprintf("%02d:%02d", tm->hour, tm->min); int usec = tm->nsec; if (tm->sec || usec) { res += sprintf(":%02d", tm->sec); if (usec) { int msec; res += (usec /= 1000) * 1000 != tm->nsec ? sprintf(".%09d", tm->nsec) : (msec = usec / 1000) * 1000 != usec ? sprintf(".%06d", usec) : sprintf(".%03d", msec); } } return res; } //! Base class for time related objects. Supports arithmetic with //! @[Interval] objects. //! @note //! Should not be used by itself. //! @seealso //! @[Timestamp], @[Time], @[Interval] class Timebase { //! Nanoseconds since epoch (for absolute time classes, //! epoch equals @expr{1970/01/01 00:00:00 UTC@}). int nsecs; array(int) _encode() { return ({nsecs}); } void _decode(array(int) x) { nsecs = x[0]; }
6cd2b42018-01-12Stephen R. van den Berg  protected int __hash() { return nsecs; }
3794dd2017-12-21Martin Nilsson  //! @param sec //! Seconds since epoch. //! @param nsec //! Nanoseconds since epoch. variant protected void create(void|mapping(string:int) tm) { } variant protected void create(this_program copy) { nsecs = [int]copy->nsecs; } variant protected void create(int|float sec, void|int nsec) { nsecs = (int)(sec * NANOSECONDS + nsec); } //! Microseconds since epoch; reflects the equivalent value as the basic //! member @[nsecs]. int|float `usecs() { return nsecs % 1000 ? nsecs / 1000.0 : nsecs / 1000; } // FIXME This refdoc below gets lost //! Microseconds since epoch; actually modifies the basic //! member @[nsecs]. int `usecs=(int usec) { nsecs = 1000 * usec; return usec; } protected mixed `+(mixed that) { if (objectp(that) && ([object]that)->is_interval) { this_program n; if (([object]that)->days || ([object]that)->months) { mapping(string:int) t = tm(); if (([object]that)->days) if (t->mday) t->mday += [int]([object]that)->days; else error("Adding days is not supported\n"); if (([object]that)->months) if (zero_type(t->mon)) error("Adding months is not supported\n"); else t->mon += [int]([object]that)->months; /* * Perform a manual daylight-saving correction, because mktime() gets * it wrong for days/months across a daylight-saving switch. */ int oldtimezone = t->timezone; t = (n = this_program(t))->tm(); n->nsecs += (t->timezone - oldtimezone) * NANOSECONDS; } else n = this_program(this); n->nsecs += [int]([object]that)->nsecs; return n; } else if (!intp(that) && !floatp(that)) error("Cannot add %O\n", that); this_program n = this_program(); n->nsecs = (int)(nsecs + [float|int]that * NANOSECONDS); return n; } protected mixed `-(mixed that) { return this + -that; } protected int(0..1) `<(mixed that) { return intp(that) ? nsecs < [int]that * NANOSECONDS : floatp(that) ? nsecs < [float]that * NANOSECONDS : objectp(that) && nsecs < [int]([object]that)->nsecs && !zero_type(([object]that)->nsecs); } protected int(0..1) `==(mixed that) { return objectp(that) ? nsecs == [int]([object]that)->nsecs && !zero_type(([object]that)->nsecs) : intp(that) ? nsecs == [int]that * NANOSECONDS : floatp(that) && (float)nsecs == [float]that * NANOSECONDS; } public mapping(string:int) tm() { return (["nsec": abs(nsecs) % NANOSECONDS ]); } //! Can be casted to string, float and int. //! Casting it to float and int return unix-time values. //! @seealso //! @[mktime()], @[gmtime()] protected mixed cast(string to) { switch (to) { case "string": return (nsecs < 0 ? "-" : "") + iso_time(tm()); case "float": return nsecs / NANOSECONDS.0; case "int":
0b1a2f2018-01-12Stephen R. van den Berg  return nsecs / NANOSECONDS;
3794dd2017-12-21Martin Nilsson  default: return UNDEFINED; } } protected string _sprintf(int fmt, mapping(string:mixed) params) { switch (fmt) { case 's': return (string)this; default: return sprintf(sprintf("%%*%c", fmt), params, 0); } } } //! Lightweight time type. Stores absolute times in a day //! with nanosecond resolution. Normalised value range is between //! @expr{00:00:00.000000000@} and @expr{23:59:59.999999999@}. //! Values outside this range are accepted, and have arithmetically //! sound results. Supports arithmetic with @[Interval] and @[Date] objects. //! Cast it to @expr{int@} or @expr{float@} to obtain seconds since //! @expr{00:00@}. //! @seealso //! @[Interval], @[Date], @[TimeTZ], @[Range] class Time { inherit Timebase; constant is_time = 1; //! @param hour //! Hours since epoch. //! @param min //! Minutes since epoch. //! @param sec //! Seconds since epoch. //! @param nsec //! Nanoseconds since epoch. variant protected void create(int hour, int min, void|int sec, void|int nsec) { nsecs = ((((hour * 60) + min) * 60 + sec) * NANOSECONDS) + nsec; } variant protected void create(int sec) { nsecs = sec * NANOSECONDS; } // FIXME This refdoc below gets lost //! @param tm //! Standard tm mapping, optionally extended with @expr{nsec@} for //! nanoseconds. //! Passed values will not be normalised. //! Days/months/years are ignored. variant protected void create(mapping(string:int) tm) { create(tm->hour, tm->min, tm->sec, tm->nsec); } protected mixed `+(mixed that) { if (objectp(that) && ([object]that)->is_date) return that + this; return ::`+(that); } //! @returns //! A tm-like mapping which describes the time. The mapping will //! include an extra @expr{nsec@} component. //! @seealso //! @[mktime()], @[gmtime()] public mapping(string:int) tm() { int hourleft; int secleft = (hourleft = abs(nsecs) / NANOSECONDS) % 60; int minleft = (hourleft /= 60) % 60; hourleft /= 60; return ::tm() + (["hour":hourleft, "min":minleft, "sec":secleft]); } protected string _sprintf(int fmt, mapping(string:mixed) params) { if (fmt == 'O') return sprintf("Time(%s)", (string)this); return ::_sprintf(fmt, params); } } //! Lightweight time type with timezone. Equivalent to @[Time], but //! stores a timezone offset as well. //! Cast it to @expr{int@} or @expr{float@} to obtain seconds since //! @expr{00:00@}. //! @seealso //! @[Time], @[Date], @[Interval], @[Range] class TimeTZ { inherit Time; //! Timezone offset in seconds @b{west@} from UTC //! (includes daylight-saving time adjustment). //! @note //! ISO time format displays the timezone in seconds @b{east@} from UTC. //! @seealso //! @[localtime()] int timezone; array(int) _encode() { return ({nsecs, timezone}); } void _decode(array(int) x) { nsecs = x[0]; timezone = x[1]; } //! Stores just the time of the day components, but including //! the correct timezone offset with any daylight-saving correction //! active on the date specified. //! @param year //! Absolute year (e.g. 1900 == 1900, 2000 = 2000, 2017 == 2017). //! @param month //! Month of the year (January == 1 ... December == 12). //! @param day //! Day of the month (typically between 1 and 31). //! @param hour //! Hour of the day (typically between 0 and 23). //! @param min //! Minutes (typically between 0 and 59). //! @param sec //! Seconds (typically between 0 and 59). //! @param nsec //! Nanoseconds (typically between 0 and 999999999). //! @note //! Specified values are expected in the localised time (i.e. relative //! to the current timezone locale, including daylight-saving correction). //! @note //! If any of these values are offered in a denormalised range, //! they will be normalised relative to the startdate offered. //! @seealso //! @[Timestamp], @[Date], @[Interval], @[Time] variant protected void create(int year, int month, int day, int hour, int min, void|int sec, void|int nsec) { mapping(string:int) t = localtime(mktime((["year":year - 1900, "mon":month - 1, "mday":day, "hour":hour, "min":min, "sec":sec]))); t->nsec = nsec; create(t); } // FIXME This refdoc below gets lost //! @param tm //! Standard tm mapping, optionally extended with @expr{nsec@} for //! nanoseconds. Any specified @expr{timezone@} is used as is. //! Passed values will not be normalised. //! Days/months/years are ignored. variant protected void create(mapping(string:int) tm) { timezone = zero_type(tm->timezone) ? local_timezone : tm->timezone; ::create(tm->hour, tm->min, tm->sec, tm->nsec); } variant protected void create(int hour, int min, void|int sec, void|int nsec) { timezone = local_timezone; ::create(hour, min, sec, nsec); } variant protected void create(this_program copy) { timezone = [int]copy->timezone; ::create(copy); } public mapping(string:int) tm() { return ::tm() + (["timezone":timezone]); } protected mixed cast(string to) { if (to == "string") return ::cast(to) + iso_timezone(timezone); return ::cast(to); } protected string _sprintf(int fmt, mapping(string:mixed) params) { if (fmt == 'O') return sprintf("TimeTZ(%s)", (string)this); return ::_sprintf(fmt, params); } } //! Lightweight time and date interval type. //! It stores the interval in integers of nanoseconds, days and months. //! Supports arithmetic with @[Time], @[TimeTZ], @[Timestamp] //! and @[Date] objects. //! Cast it to @expr{int@} or @expr{float@} to obtain seconds. //! @note //! Depending on daylight-saving time, a day may not equal 24 hours. //! @note //! The number of days in a month depends on the the time of the year. //! @seealso //! @[Timestamp], @[Date], @[Time] class Interval { inherit Time; constant is_interval = 1; //! Number of days; may not be equal to 24 hours per day, depending //! on daylight-saving time. int days; //! Number of months; the number of days per month varies accordingly. int months; array(int) _encode() { return ({nsecs, days, months}); } void _decode(array(int) x) { nsecs = x[0]; days = x[1]; months = x[2]; } variant protected void create(this_program copy) { ::create(copy); days = copy->days; months = copy->months; } protected mixed `*(mixed that) { this_program n = this_program(this); if (intp(that)) { n->nsecs *= that; n->days *= that; n->months *= that; } else if (floatp(that)) { n->nsecs = (int)(nsecs * that); n->days = (int)(days * that); n->months = (int)(months * that); if (days && n->days % days || months && n->months % months) error("Cannot create fractional days or months\n"); } else error("Cannot add %O\n", that); return n; } protected mixed `/(mixed that) { if (!intp(that) && !floatp(that)) error("Cannot divide by %O\n", that); this_program n = this_program(this); n->nsecs = (int)(nsecs / that); n->days = (int)(days / that); n->months = (int)(months / that); if (days && n->days % days || months && n->months % months) error("Cannot create fractional days or months\n"); return n; } protected mixed `+(mixed that) { if (!objectp(that) || !([object]that)->is_interval) error("Cannot add %O\n", that); this_program n = this_program(this); n->nsecs += ([object]that)->nsecs; n->days += ([object]that)->days; n->months += ([object]that)->months; return n; } protected mixed `-(void|mixed that) { this_program n = this_program(this); if (zero_type(that)) { n->nsecs = -n->nsecs; n->days = -n->days; n->months = -n->months; } else if (!objectp(that) || !([object]that)->is_interval) error("Cannot substract %O\n", that); else { n->nsecs -= ([object]that)->nsecs; n->days -= ([object]that)->days; n->months -= ([object]that)->months; } return n; } protected int(0..1) `<(mixed that) { return objectp(that) && ([object]that)->is_interval && ( months <= ([object]that)->months && days <= ([object]that)->days && nsecs < ([object]that)->nsecs || months <= ([object]that)->months && days < ([object]that)->days && nsecs <= ([object]that)->nsecs || months < ([object]that)->months && days <= ([object]that)->days && nsecs <= ([object]that)->nsecs); } protected int(0..1) `==(mixed that) { return objectp(that) && ([object]that)->is_interval && months == [int]([object]that)->months && days == [int]([object]that)->days && nsecs == [int]([object]that)->nsecs; } //! @returns //! A tm-like mapping which describes the interval. The mapping will //! include an extra @expr{nsec@} component, and optionally //! @expr{isnegative = 1@} if the interval is a negative time interval. //! @seealso //! @[mktime()], @[gmtime()] public mapping(string:int) tm() { return ::tm() + (["mon":months, "mday":days]) + (nsecs < 0 ? (["isnegative":1]) : ([])); } //! Casting intervals with @expr{days@} or @expr{months@} to @expr{int@} //! or @expr{float@} is not possible since the units are not constant. //! Casting an interval to @expr{string@} will return a value which //! is SQL-compliant. protected mixed cast(string to) { switch (to) { case "string": { string res = months ? sprintf("%d MONTH", months) : ""; if (days) res += (res > "" ? " " : "") + sprintf("%d DAY", days); return res + (nsecs ? (res > "" ? " " : "") + ::cast(to) : ""); } case "float": case "int": if (months || days) error("Interval contains variable units and cannot be casted\n"); } return ::cast(to); } protected string _sprintf(int fmt, mapping(string:mixed) params) { if (fmt == 'O') return sprintf("Interval(%s)", (string)this); return ::_sprintf(fmt, params); } } //! Lightweight time and date type. The values point at absolute points //! in time. The values are stored internally with nanosecond resolution //! since epoch (@expr{1970/01/01 00:00:00 UTC@}). Supports arithmetic //! with @[Interval] objects. //! Cast it to @expr{int@} or @expr{float@} to obtain unix_time. //! @seealso //! @[Interval], @[Date], @[Range], @[localtime()], @[mktime()] class Timestamp { inherit Timebase; constant is_timestamp = 1; //! @param year //! Absolute year (e.g. 1900 == 1900, 2000 = 2000, 2017 == 2017). //! @param month //! Month of the year (January == 1 ... December == 12). //! @param day //! Day of the month (typically between 1 and 31). //! @param hour //! Hour of the day (typically between 0 and 23). //! @param min //! Minutes (typically between 0 and 59). //! @param sec //! Seconds (typically between 0 and 59). //! @param nsec //! Nanoseconds (typically between 0 and 999999999). //! @note //! Specified values are expected in the localised time (i.e. relative //! to the current timezone locale, including daylight-saving correction). //! @note //! If any of these values are offered in a denormalised range, //! they will be normalised relative to the startdate offered. //! I.e. it allows primitive year/month/day/hour/minute/second/nanosecond //! arithmetic. For more advanced arithmetic you must use @[Interval] //! objects. //! @seealso //! @[localtime()], @[mktime()] variant protected void create(int year, int month, int day, void|int hour, void|int min, void|int sec, void|int nsec) { create((["year":year - 1900, "mon":month - 1, "mday":day, "hour":hour, "min":min, "sec":sec, "nsec":nsec ])); } variant protected void create(int unix_time, void|int nsec) { ::create(unix_time, nsec); } variant protected void create(object/*Date*/ copy) { // Force the date to be regarded in the localised timezone. create([mapping(string:int)]copy->tm() - (<"timezone">)); } variant protected void create(mapping(string:int) tm) { create(mktime(tm), tm->nsec); } protected mixed `-(void|mixed that) { if (zero_type(that)) error("Cannot negate %O\n", this); if (objectp(that)) { if (([object]that)->is_date) that = this_program([object]that); if (([object]that)->is_timestamp) { Interval n = Interval(); n->nsecs = nsecs - [int]([object]that)->nsecs; return n; } if (!([object]that)->is_interval) error("Cannot substract %O\n", that); } return this + -that; } inline protected int(0..1) `<(mixed that) { return intp(that) ? (int)this < that : ::`<(that); } //! @returns //! The same as @[localtime()], but augmented //! by an extra member called @expr{nsec@}. //! @seealso //! @[localtime()] public mapping(string:int) tm() { return localtime((int)this) + (["nsec": nsecs % NANOSECONDS ]); }
7f01cc2018-01-08Stephen R. van den Berg  //! @seealso //! @[System.TM()->strftime()] public string(1..255) strftime(string(1..255) format) { return System.TM((int)this)->strftime(format); } //! @seealso //! @[System.TM()->strptime()] public bool strptime(string(1..255) format, string(1..255) data) { System.TM t = System.TM(); bool ret = t->strptime(format, data); if (ret) ::create(t->unix_time()); return ret; }
3794dd2017-12-21Martin Nilsson  //! When cast to string it returns an ISO formatted timestamp //! that includes daylight-saving and timezone corrections. protected mixed cast(string to) { switch (to) { case "string": { mapping(string:int) t = tm();
a028322017-12-28Martin Nilsson  string res = sprintf("%04d-%02d-%02d",
3794dd2017-12-21Martin Nilsson  t->year + 1900, t->mon+1, t->mday); if (t->hour || t->min || t->sec || t->nsec) res += " " + iso_time(t); return res + iso_timezone(t->timezone); } } return ::cast(to); } protected string _sprintf(int fmt, mapping(string:mixed) params) { if (fmt == 'O') return sprintf("Timestamp(%s)", (string)this); return ::_sprintf(fmt, params); } } //! Lightweight date type. Stores internally in days since epoch. //! Supports arithmetic with @[Interval], @[Timestamp], @[Time] //! and @[TimeTZ] objects. //! Cast it to @expr{int@} or @expr{float@} to obtain unix_time. //! @seealso //! @[Interval], @[Timestamp], @[Time], @[TimeTZ], @[Range] class Date { constant is_date = 1;
a028322017-12-28Martin Nilsson  //! Since 1970-01-01 (epoch).
3794dd2017-12-21Martin Nilsson  int days; array(int) _encode() { return ({days}); } void _decode(array(int) x) { days = x[0]; }
6cd2b42018-01-12Stephen R. van den Berg  protected int __hash() { return days; }
3794dd2017-12-21Martin Nilsson  //! variant protected void create(int year, int month, int day) { create((["year":year - 1900, "mon":month - 1, "mday":day])); } variant protected void create(this_program copy) { days = [int]copy->days; } variant protected void create(Timestamp copy) { days = copy->nsecs / (24 * 3600 * NANOSECONDS) - (copy->nsecs < 0); } variant protected void create(mapping(string:int) tm) {
fe4c152017-12-24Stephen R. van den Berg  create(mktime(tm + (["isdst":0, "timezone":0])));
3794dd2017-12-21Martin Nilsson  } variant protected void create(int unix_time) {
0b1a2f2018-01-12Stephen R. van den Berg  days = unix_time / (24 * 3600);
3794dd2017-12-21Martin Nilsson  } variant protected void create(float unix_time) { create((int)unix_time); } variant protected void create() { } protected mixed `+(mixed that) { object n = this_program(this); if (objectp(that)) { if (([object]that)->is_interval) { n->days += ([object]that)->days; if(([object]that)->months) { mapping(string:int) t = [mapping(string:int)]n->tm(); t->mon += ([object]that)->months; n = this_program(t); } if (([object]that)->nsecs) (n = Timestamp(n))->nsecs += ([object]that)->nsecs; } else if (([object]that)->is_time) { mapping(string:int) t = [mapping(string:int)]n->tm() + [mapping(string:int)]([object]that)->tm(); n = Timestamp(t); } else error("Cannot add %O\n", that); } else if (intp(that)) n->days += that; else error("Cannot add %O\n", that); return n; } protected mixed `-(void|mixed that) { if (zero_type(that)) error("Cannot negate %O\n", this); if (objectp(that)) { Interval n = Interval(); if (([object]that)->is_date) { n->days = days - [int]([object]that)->days; return n; } if (([object]that)->is_timestamp) { n->nsecs = Timestamp(this)->nsecs - [int]([object]that)->nsecs; return n; } error("Cannot substract %O\n", that); } return this + -that; } inline protected int(0..1) `<(mixed that) { return intp(that) ? (int)this < that : floatp(that) ? (float)this < that : objectp(that) && (([object]that)->is_date ? days < ([object]that)->days : ([object]that)->is_timestamp && days * 24 * 3600 * NANOSECONDS < ([object]that)->nsecs); } inline protected int(0..1) `>(mixed that) { return intp(that) ? (int)this > that : floatp(that) ? (float)this > that : objectp(that) && (([object]that)->is_date ? days > ([object]that)->days : ([object]that)->is_timestamp && days * 24 * 3600 * NANOSECONDS > ([object]that)->nsecs); } protected int(0..1) `==(mixed that) { return objectp(that) && (days == [int]([object]that)->days && !zero_type(([object]that)->days) || !zero_type(([object]that)->nsecs) && days * 24 * 3600 * NANOSECONDS == [int]([object]that)->nsecs) || intp(that) && (int)this == [int]that || floatp(that) && (float)this == [float]that; } public mapping(string:int) tm() { return gmtime((int)this); }
7f01cc2018-01-08Stephen R. van den Berg  //! @seealso //! @[System.TM()->strftime()] public string(1..255) strftime(string(1..255) format) { return System.TM((int)this)->strftime(format); } //! @seealso //! @[System.TM()->strptime()] public bool strptime(string(1..255) format, string(1..255) data) { System.TM t = System.TM(); bool ret = t->strptime(format, data); if (ret) create(t->unix_time()); return ret; }
3794dd2017-12-21Martin Nilsson  protected mixed cast(string to) { switch (to) { case "string": { mapping(string:int) t = tm();
a028322017-12-28Martin Nilsson  return sprintf("%04d-%02d-%02d", t->year + 1900, t->mon+1, t->mday);
3794dd2017-12-21Martin Nilsson  } case "float": return (float)(int)this; case "int": return days * 24 * 3600; default: return UNDEFINED; } } protected string _sprintf(int fmt, mapping(string:mixed) params) { switch (fmt) { case 'O': return sprintf("Date(%s)", (string)this); case 's': return (string)this; } return sprintf(sprintf("%%*%c", fmt), params, 0); } }