+ double ofs = expr_ymd_to_ofs (y, m, d, e, n, ya, ma, da);
+ return ofs != SYSMIS ? ofs * DAY_S : SYSMIS;
+}
+\f
+/* A date unit. */
+enum date_unit
+ {
+ DATE_YEARS,
+ DATE_QUARTERS,
+ DATE_MONTHS,
+ DATE_WEEKS,
+ DATE_DAYS,
+ DATE_HOURS,
+ DATE_MINUTES,
+ DATE_SECONDS
+ };
+
+/* Stores in *UNIT the unit whose name is NAME.
+ Return success. */
+static enum date_unit
+recognize_unit (struct substring name, const struct expression *e,
+ const struct expr_node *n, enum date_unit *unit)
+{
+ struct unit_name
+ {
+ enum date_unit unit;
+ const struct substring name;
+ };
+ static const struct unit_name unit_names[] =
+ {
+ { DATE_YEARS, SS_LITERAL_INITIALIZER ("years") },
+ { DATE_QUARTERS, SS_LITERAL_INITIALIZER ("quarters") },
+ { DATE_MONTHS, SS_LITERAL_INITIALIZER ("months") },
+ { DATE_WEEKS, SS_LITERAL_INITIALIZER ("weeks") },
+ { DATE_DAYS, SS_LITERAL_INITIALIZER ("days") },
+ { DATE_HOURS, SS_LITERAL_INITIALIZER ("hours") },
+ { DATE_MINUTES, SS_LITERAL_INITIALIZER ("minutes") },
+ { DATE_SECONDS, SS_LITERAL_INITIALIZER ("seconds") },
+ };
+ const int n_unit_names = sizeof unit_names / sizeof *unit_names;
+
+ const struct unit_name *un;
+
+ for (un = unit_names; un < &unit_names[n_unit_names]; un++)
+ if (ss_equals_case (un->name, name))
+ {
+ *unit = un->unit;
+ return true;
+ }
+
+ msg_at (SE, expr_location (e, n),
+ _("Unrecognized date unit `%.*s'. "
+ "Valid date units are `%s', `%s', `%s', "
+ "`%s', `%s', `%s', `%s', and `%s'."),
+ (int) ss_length (name), ss_data (name),
+ "years", "quarters", "months",
+ "weeks", "days", "hours", "minutes", "seconds");
+
+ return false;
+}
+
+/* Returns the number of whole years from DATE1 to DATE2,
+ where a year is defined as the same or later month, day, and
+ time of day. */
+static int
+year_diff (double date1, double date2)
+{
+ int y1, m1, d1, yd1;
+ int y2, m2, d2, yd2;
+ int diff;
+
+ assert (date2 >= date1);
+ calendar_offset_to_gregorian (date1 / DAY_S, &y1, &m1, &d1, &yd1);
+ calendar_offset_to_gregorian (date2 / DAY_S, &y2, &m2, &d2, &yd2);
+
+ diff = y2 - y1;
+ if (diff > 0)
+ {
+ int yd1 = 32 * m1 + d1;
+ int yd2 = 32 * m2 + d2;
+ if (yd2 < yd1
+ || (yd2 == yd1 && fmod (date2, DAY_S) < fmod (date1, DAY_S)))
+ diff--;