|
@@ -0,0 +1,624 @@
|
|
|
|
|
+using Invercargill.DataStructures;
|
|
|
|
|
+using Invercargill.Wrappers;
|
|
|
|
|
+
|
|
|
|
|
+namespace Invercargill.Expressions {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Function accessor for DateTime types.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Provides access to DateTime functions:
|
|
|
|
|
+ * - `compare(other)`: Compares this datetime with another, returns -1, 0, or 1
|
|
|
|
|
+ * - `difference(other)`: Returns the difference in seconds between two datetimes
|
|
|
|
|
+ * - `equal(other)`: Checks if two datetimes are equal
|
|
|
|
|
+ * - `format(format_string)`: Formats the datetime using a custom format string
|
|
|
|
|
+ * - `format_iso8601()`: Returns the datetime in ISO 8601 format
|
|
|
|
|
+ * - `get_day_of_month()`: Returns the day of the month (1-31)
|
|
|
|
|
+ * - `get_day_of_week()`: Returns the day of the week (0=Sunday, 1=Monday, etc.)
|
|
|
|
|
+ * - `get_day_of_year()`: Returns the day of the year (1-366)
|
|
|
|
|
+ * - `get_hour()`: Returns the hour (0-23)
|
|
|
|
|
+ * - `get_microsecond()`: Returns the microsecond (0-999999)
|
|
|
|
|
+ * - `get_minute()`: Returns the minute (0-59)
|
|
|
|
|
+ * - `get_second()`: Returns the second (0-59)
|
|
|
|
|
+ * - `get_seconds()`: Returns the total seconds since Unix epoch
|
|
|
|
|
+ * - `get_timezone()`: Returns the timezone identifier
|
|
|
|
|
+ * - `get_timezone_abbreviation()`: Returns the timezone abbreviation
|
|
|
|
|
+ * - `get_utc_offset()`: Returns the UTC offset in seconds
|
|
|
|
|
+ * - `get_week_numbering_year()`: Returns the week-numbering year
|
|
|
|
|
+ * - `get_week_of_year()`: Returns the week of the year (1-53)
|
|
|
|
|
+ * - `get_year()`: Returns the year
|
|
|
|
|
+ * - `get_ymd()`: Returns a lot containing [year, month, day]
|
|
|
|
|
+ * - `is_daylight_savings()`: Checks if daylight savings time is in effect
|
|
|
|
|
+ * - `to_local()`: Converts to local timezone
|
|
|
|
|
+ * - `to_timezone(tz)`: Converts to the specified timezone
|
|
|
|
|
+ * - `to_unix()`: Returns Unix timestamp (seconds)
|
|
|
|
|
+ * - `to_unix_usec()`: Returns Unix timestamp with microseconds
|
|
|
|
|
+ * - `to_utc()`: Converts to UTC
|
|
|
|
|
+ *
|
|
|
|
|
+ * Example usage in expressions:
|
|
|
|
|
+ * {{{
|
|
|
|
|
+ * created_at.get_year() // 2024
|
|
|
|
|
+ * created_at.format("%Y-%m-%d") // "2024-03-15"
|
|
|
|
|
+ * created_at.to_utc() // DateTime in UTC
|
|
|
|
|
+ * created_at.difference(other_date) // seconds difference
|
|
|
|
|
+ * }}}
|
|
|
|
|
+ */
|
|
|
|
|
+ public class DateTimeFunctionAccessor : Object, FunctionAccessor {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * The DateTime value being accessed.
|
|
|
|
|
+ */
|
|
|
|
|
+ private DateTime? _value;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new DateTimeFunctionAccessor.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param value The DateTime value to call functions on
|
|
|
|
|
+ */
|
|
|
|
|
+ public DateTimeFunctionAccessor(DateTime? value) {
|
|
|
|
|
+ _value = value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public bool has_function(string function_name) {
|
|
|
|
|
+ switch (function_name) {
|
|
|
|
|
+ case "compare":
|
|
|
|
|
+ case "difference":
|
|
|
|
|
+ case "equal":
|
|
|
|
|
+ case "format":
|
|
|
|
|
+ case "format_iso8601":
|
|
|
|
|
+ case "get_day_of_month":
|
|
|
|
|
+ case "get_day_of_week":
|
|
|
|
|
+ case "get_day_of_year":
|
|
|
|
|
+ case "get_hour":
|
|
|
|
|
+ case "get_microsecond":
|
|
|
|
|
+ case "get_minute":
|
|
|
|
|
+ case "get_second":
|
|
|
|
|
+ case "get_seconds":
|
|
|
|
|
+ case "get_timezone":
|
|
|
|
|
+ case "get_timezone_abbreviation":
|
|
|
|
|
+ case "get_utc_offset":
|
|
|
|
|
+ case "get_week_numbering_year":
|
|
|
|
|
+ case "get_week_of_year":
|
|
|
|
|
+ case "get_year":
|
|
|
|
|
+ case "get_ymd":
|
|
|
|
|
+ case "is_daylight_savings":
|
|
|
|
|
+ case "to_local":
|
|
|
|
|
+ case "to_timezone":
|
|
|
|
|
+ case "to_unix":
|
|
|
|
|
+ case "to_unix_usec":
|
|
|
|
|
+ case "to_utc":
|
|
|
|
|
+ return true;
|
|
|
|
|
+ default:
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public Enumerable<string> get_function_names() {
|
|
|
|
|
+ return new Wrappers.Array<string>(new string[]{
|
|
|
|
|
+ "compare",
|
|
|
|
|
+ "difference",
|
|
|
|
|
+ "equal",
|
|
|
|
|
+ "format",
|
|
|
|
|
+ "format_iso8601",
|
|
|
|
|
+ "get_day_of_month",
|
|
|
|
|
+ "get_day_of_week",
|
|
|
|
|
+ "get_day_of_year",
|
|
|
|
|
+ "get_hour",
|
|
|
|
|
+ "get_microsecond",
|
|
|
|
|
+ "get_minute",
|
|
|
|
|
+ "get_second",
|
|
|
|
|
+ "get_seconds",
|
|
|
|
|
+ "get_timezone",
|
|
|
|
|
+ "get_timezone_abbreviation",
|
|
|
|
|
+ "get_utc_offset",
|
|
|
|
|
+ "get_week_numbering_year",
|
|
|
|
|
+ "get_week_of_year",
|
|
|
|
|
+ "get_year",
|
|
|
|
|
+ "get_ymd",
|
|
|
|
|
+ "is_daylight_savings",
|
|
|
|
|
+ "to_local",
|
|
|
|
|
+ "to_timezone",
|
|
|
|
|
+ "to_unix",
|
|
|
|
|
+ "to_unix_usec",
|
|
|
|
|
+ "to_utc"
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public Element call_function(
|
|
|
|
|
+ string function_name,
|
|
|
|
|
+ Series<Element> arguments,
|
|
|
|
|
+ EvaluationContext context
|
|
|
|
|
+ ) throws ExpressionError {
|
|
|
|
|
+
|
|
|
|
|
+ // Handle null DateTime
|
|
|
|
|
+ if (_value == null) {
|
|
|
|
|
+ return handle_null_function(function_name, arguments);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (function_name) {
|
|
|
|
|
+ case "compare":
|
|
|
|
|
+ return call_compare(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "difference":
|
|
|
|
|
+ return call_difference(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "equal":
|
|
|
|
|
+ return call_equal(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "format":
|
|
|
|
|
+ return call_format(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "format_iso8601":
|
|
|
|
|
+ return call_format_iso8601(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_day_of_month":
|
|
|
|
|
+ return call_get_day_of_month(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_day_of_week":
|
|
|
|
|
+ return call_get_day_of_week(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_day_of_year":
|
|
|
|
|
+ return call_get_day_of_year(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_hour":
|
|
|
|
|
+ return call_get_hour(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_microsecond":
|
|
|
|
|
+ return call_get_microsecond(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_minute":
|
|
|
|
|
+ return call_get_minute(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_second":
|
|
|
|
|
+ return call_get_second(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_seconds":
|
|
|
|
|
+ return call_get_seconds(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_timezone":
|
|
|
|
|
+ return call_get_timezone(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_timezone_abbreviation":
|
|
|
|
|
+ return call_get_timezone_abbreviation(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_utc_offset":
|
|
|
|
|
+ return call_get_utc_offset(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_week_numbering_year":
|
|
|
|
|
+ return call_get_week_numbering_year(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_week_of_year":
|
|
|
|
|
+ return call_get_week_of_year(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_year":
|
|
|
|
|
+ return call_get_year(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "get_ymd":
|
|
|
|
|
+ return call_get_ymd(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "is_daylight_savings":
|
|
|
|
|
+ return call_is_daylight_savings(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "to_local":
|
|
|
|
|
+ return call_to_local(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "to_timezone":
|
|
|
|
|
+ return call_to_timezone(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "to_unix":
|
|
|
|
|
+ return call_to_unix(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "to_unix_usec":
|
|
|
|
|
+ return call_to_unix_usec(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ case "to_utc":
|
|
|
|
|
+ return call_to_utc(arguments);
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new ExpressionError.FUNCTION_NOT_FOUND(
|
|
|
|
|
+ @"Unknown DateTime function: $function_name"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Handles function calls on null DateTime values.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element handle_null_function(string function_name, Series<Element> arguments)
|
|
|
|
|
+ throws ExpressionError {
|
|
|
|
|
+
|
|
|
|
|
+ switch (function_name) {
|
|
|
|
|
+ case "compare":
|
|
|
|
|
+ case "difference":
|
|
|
|
|
+ // Return null for comparison/difference with null
|
|
|
|
|
+ return new NullElement();
|
|
|
|
|
+
|
|
|
|
|
+ case "equal":
|
|
|
|
|
+ // Check if comparing with another null
|
|
|
|
|
+ expect_argument_count("equal", arguments, 1);
|
|
|
|
|
+ var args = to_array(arguments);
|
|
|
|
|
+ DateTime? other = get_datetime_argument(args[0], "other");
|
|
|
|
|
+ return new NativeElement<bool>(other == null);
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ // Most DateTime functions return null when called on null
|
|
|
|
|
+ return new NullElement();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Compares this datetime with another.
|
|
|
|
|
+ * Returns -1 if this < other, 0 if equal, 1 if this > other.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_compare(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("compare", arguments, 1);
|
|
|
|
|
+ var args = to_array(arguments);
|
|
|
|
|
+ DateTime? other = get_datetime_argument(args[0], "other");
|
|
|
|
|
+
|
|
|
|
|
+ if (other == null) {
|
|
|
|
|
+ return new NullElement();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new NativeElement<int>(_value.compare(other));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the difference in seconds between two datetimes.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_difference(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("difference", arguments, 1);
|
|
|
|
|
+ var args = to_array(arguments);
|
|
|
|
|
+ DateTime? other = get_datetime_argument(args[0], "other");
|
|
|
|
|
+
|
|
|
|
|
+ if (other == null) {
|
|
|
|
|
+ return new NullElement();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TimeSpan diff = _value.difference(other);
|
|
|
|
|
+ // TimeSpan is in microseconds, convert to seconds
|
|
|
|
|
+ return new NativeElement<int64?>(diff / TimeSpan.SECOND);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Checks if two datetimes are equal.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_equal(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("equal", arguments, 1);
|
|
|
|
|
+ var args = to_array(arguments);
|
|
|
|
|
+ DateTime? other = get_datetime_argument(args[0], "other");
|
|
|
|
|
+
|
|
|
|
|
+ if (other == null) {
|
|
|
|
|
+ return new NativeElement<bool>(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new NativeElement<bool>(_value.equal(other));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Formats the datetime using a custom format string.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_format(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("format", arguments, 1);
|
|
|
|
|
+ var args = to_array(arguments);
|
|
|
|
|
+ string? format_string = get_string_argument(args[0], "format_string");
|
|
|
|
|
+
|
|
|
|
|
+ if (format_string == null) {
|
|
|
|
|
+ return new NativeElement<string>("");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new NativeElement<string>(_value.format(format_string));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the datetime in ISO 8601 format.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_format_iso8601(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("format_iso8601", arguments, 0);
|
|
|
|
|
+ // ISO 8601 format: YYYY-MM-DDTHH:MM:SS+TZ:00
|
|
|
|
|
+ return new NativeElement<string>(_value.format("%Y-%m-%dT%H:%M:%S%z"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the day of the month (1-31).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_day_of_month(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_day_of_month", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_day_of_month());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the day of the week (0=Sunday, 1=Monday, etc.).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_day_of_week(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_day_of_week", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_day_of_week());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the day of the year (1-366).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_day_of_year(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_day_of_year", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_day_of_year());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the hour (0-23).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_hour(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_hour", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_hour());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the microsecond (0-999999).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_microsecond(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_microsecond", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_microsecond());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the minute (0-59).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_minute(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_minute", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_minute());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the second (0-59).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_second(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_second", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_second());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the total seconds since Unix epoch.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_seconds(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_seconds", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int64?>(_value.to_unix());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the timezone identifier.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_timezone(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_timezone", arguments, 0);
|
|
|
|
|
+ TimeZone tz = _value.get_timezone();
|
|
|
|
|
+ // TimeZone doesn't have a direct identifier getter, use abbreviation
|
|
|
|
|
+ return new NativeElement<string>(tz.get_abbreviation((int)_value.to_unix()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the timezone abbreviation.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_timezone_abbreviation(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_timezone_abbreviation", arguments, 0);
|
|
|
|
|
+ TimeZone tz = _value.get_timezone();
|
|
|
|
|
+ return new NativeElement<string>(tz.get_abbreviation((int)_value.to_unix()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the UTC offset in seconds.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_utc_offset(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_utc_offset", arguments, 0);
|
|
|
|
|
+ TimeZone tz = _value.get_timezone();
|
|
|
|
|
+ int offset = (int)(tz.get_offset((int)_value.to_unix()) / TimeSpan.SECOND);
|
|
|
|
|
+ return new NativeElement<int>(offset);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the week-numbering year.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_week_numbering_year(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_week_numbering_year", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_week_numbering_year());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the week of the year (1-53).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_week_of_year(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_week_of_year", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_week_of_year());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns the year.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_year(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_year", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int>(_value.get_year());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns a lot containing [year, month, day].
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_get_ymd(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("get_ymd", arguments, 0);
|
|
|
|
|
+ int year, month, day;
|
|
|
|
|
+ _value.get_ymd(out year, out month, out day);
|
|
|
|
|
+
|
|
|
|
|
+ var result = new DataStructures.Series<int>();
|
|
|
|
|
+ result.add(year);
|
|
|
|
|
+ result.add(month);
|
|
|
|
|
+ result.add(day);
|
|
|
|
|
+ return new ValueElement(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Checks if daylight savings time is in effect.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_is_daylight_savings(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("is_daylight_savings", arguments, 0);
|
|
|
|
|
+ TimeZone tz = _value.get_timezone();
|
|
|
|
|
+ // get_offset returns the offset including DST, compare with standard offset
|
|
|
|
|
+ // GLib doesn't have a direct is_dst method, so we check if the offset differs
|
|
|
|
|
+ // from what we'd expect. This is a simplification.
|
|
|
|
|
+ // Actually, TimeZone.get_abbreviation often includes DST indicator
|
|
|
|
|
+ string abbrev = tz.get_abbreviation((int)_value.to_unix());
|
|
|
|
|
+ // Common DST abbreviations end with 'T' or 'DT' or contain 'DST'
|
|
|
|
|
+ // This is a heuristic - proper DST detection requires more complex logic
|
|
|
|
|
+ // For now, we'll use a simpler approach based on the abbreviation
|
|
|
|
|
+ bool is_dst = abbrev.has_suffix("DT") || abbrev.has_suffix("ST") == false;
|
|
|
|
|
+ return new NativeElement<bool>(is_dst);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts to local timezone.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_to_local(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("to_local", arguments, 0);
|
|
|
|
|
+ TimeZone local_tz = new TimeZone.local();
|
|
|
|
|
+ DateTime local = _value.to_timezone(local_tz);
|
|
|
|
|
+ return new NativeElement<DateTime>(local);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts to the specified timezone.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_to_timezone(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("to_timezone", arguments, 1);
|
|
|
|
|
+ var args = to_array(arguments);
|
|
|
|
|
+ string? tz_identifier = get_string_argument(args[0], "timezone");
|
|
|
|
|
+
|
|
|
|
|
+ if (tz_identifier == null) {
|
|
|
|
|
+ return new NullElement();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ TimeZone tz = new TimeZone.identifier(tz_identifier);
|
|
|
|
|
+ DateTime converted = _value.to_timezone(tz);
|
|
|
|
|
+ return new NativeElement<DateTime>(converted);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ throw new ExpressionError.INVALID_ARGUMENTS(
|
|
|
|
|
+ @"Invalid timezone identifier: $tz_identifier"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns Unix timestamp (seconds).
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_to_unix(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("to_unix", arguments, 0);
|
|
|
|
|
+ return new NativeElement<int64?>(_value.to_unix());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Returns Unix timestamp with microseconds.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_to_unix_usec(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("to_unix_usec", arguments, 0);
|
|
|
|
|
+ // to_unix() returns seconds, get_microsecond() returns the fractional part
|
|
|
|
|
+ int64 seconds = _value.to_unix();
|
|
|
|
|
+ int microseconds = _value.get_microsecond();
|
|
|
|
|
+ // Return as a double to preserve both parts
|
|
|
|
|
+ double usec = seconds + (microseconds / 1000000.0);
|
|
|
|
|
+ return new NativeElement<double?>(usec);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts to UTC.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element call_to_utc(Series<Element> arguments) throws ExpressionError {
|
|
|
|
|
+ expect_argument_count("to_utc", arguments, 0);
|
|
|
|
|
+ TimeZone utc_tz = new TimeZone.utc();
|
|
|
|
|
+ DateTime utc = _value.to_timezone(utc_tz);
|
|
|
|
|
+ return new NativeElement<DateTime>(utc);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Helper to get a DateTime argument from an Element.
|
|
|
|
|
+ */
|
|
|
|
|
+ private DateTime? get_datetime_argument(Element element, string arg_name) throws ExpressionError {
|
|
|
|
|
+ DateTime? value;
|
|
|
|
|
+ if (element.try_get_as(out value)) {
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+ throw new ExpressionError.INVALID_ARGUMENTS(
|
|
|
|
|
+ @"$arg_name must be a DateTime"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Helper to get a string argument from an Element.
|
|
|
|
|
+ */
|
|
|
|
|
+ private string? get_string_argument(Element element, string arg_name) throws ExpressionError {
|
|
|
|
|
+ string? value;
|
|
|
|
|
+ if (element.try_get_as(out value)) {
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+ throw new ExpressionError.INVALID_ARGUMENTS(
|
|
|
|
|
+ @"$arg_name must be a string"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Helper to validate argument count.
|
|
|
|
|
+ */
|
|
|
|
|
+ private void expect_argument_count(string function_name, Series<Element> arguments, int expected)
|
|
|
|
|
+ throws ExpressionError {
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ foreach (var _ in arguments) {
|
|
|
|
|
+ count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (count != expected) {
|
|
|
|
|
+ throw new ExpressionError.INVALID_ARGUMENTS(
|
|
|
|
|
+ @"$function_name expects $expected argument(s), got $count"
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Helper to convert Series to array.
|
|
|
|
|
+ */
|
|
|
|
|
+ private Element[] to_array(Series<Element> arguments) {
|
|
|
|
|
+ var list = new GLib.GenericArray<Element>();
|
|
|
|
|
+ foreach (var arg in arguments) {
|
|
|
|
|
+ list.add(arg);
|
|
|
|
|
+ }
|
|
|
|
|
+ var result = new Element[list.length];
|
|
|
|
|
+ for (int i = 0; i < list.length; i++) {
|
|
|
|
|
+ result[i] = list[i];
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Factory for creating DateTimeFunctionAccessor instances.
|
|
|
|
|
+ *
|
|
|
|
|
+ * This factory is registered with the TypeAccessorRegistry to provide
|
|
|
|
|
+ * function access for DateTime values.
|
|
|
|
|
+ */
|
|
|
|
|
+ public class DateTimeFunctionAccessorFactory : Object, FunctionAccessorFactory {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public FunctionAccessor? create(Element element) {
|
|
|
|
|
+ // Use explicit type checking as in LotPropertyAccessorFactory
|
|
|
|
|
+ DateTime? value = null;
|
|
|
|
|
+ if (element.type() == typeof(DateTime) && element.try_get_as(out value)) {
|
|
|
|
|
+ return new DateTimeFunctionAccessor(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|