(*
$Id$
ISO TC184/SC4/WG12 N4923 - ISO/IS 10303-41 Fundamentals of product description and support - EXPRESS
supersedes ISO TC184/SC4/WG12 N2887
*) 


SCHEMA date_time_schema;

REFERENCE FROM basic_attribute_schema   -- ISO 10303-41
  (description_attribute,
   get_description_value); 

REFERENCE FROM measure_schema   -- ISO 10303-41
  (time_measure_with_unit); 

REFERENCE FROM support_resource_schema   -- ISO 10303-41
  (bag_to_set,
   identifier,
   label,
   text); 


TYPE ahead_or_behind = ENUMERATION OF 
   (ahead,
    exact,
    behind);
END_TYPE; 

TYPE date_time_or_event_occurrence = SELECT
   (date_time_select, 
    event_occurrence);
END_TYPE; 

TYPE date_time_select = SELECT
   (date, 
    date_and_time, 
    local_time);
END_TYPE; 

TYPE day_in_month_number = INTEGER;
WHERE
  WR1: {1 <= SELF <= 31};
END_TYPE; 

TYPE day_in_week_number = INTEGER;
WHERE
  WR1: { 1 <= SELF <= 7 };
END_TYPE; 

TYPE day_in_year_number = INTEGER;
WHERE
  WR1: {1 <= SELF <= 366};
END_TYPE; 

TYPE hour_in_day = INTEGER;
WHERE
  WR1: { 0 <= SELF < 24 };
END_TYPE; 

TYPE minute_in_hour = INTEGER;
WHERE
  WR1: { 0 <= SELF <= 59 };
END_TYPE; 

TYPE month_in_year_number = INTEGER;
WHERE
  WR1: { 1 <= SELF <= 12 };
END_TYPE; 

TYPE second_in_minute = REAL;
WHERE
  WR1: { 0 <= SELF <= 60.0 };
END_TYPE; 

TYPE week_in_year_number = INTEGER;
WHERE
  WR1: { 1 <= SELF <= 53 };
END_TYPE; 

TYPE year_number = INTEGER;
END_TYPE; 

ENTITY calendar_date
  SUBTYPE OF (date);
  day_component : day_in_month_number;
  month_component : month_in_year_number;
WHERE
  WR1: valid_calendar_date (SELF);
END_ENTITY;

ENTITY coordinated_universal_time_offset;
  hour_offset : INTEGER;
  minute_offset : OPTIONAL INTEGER;
  sense : ahead_or_behind;
DERIVE
  actual_minute_offset : INTEGER := NVL(minute_offset,0);
WHERE
  WR1: { 0 <= hour_offset < 24 };
  WR2: { 0 <= actual_minute_offset <= 59 };
  WR3: NOT (((hour_offset <> 0) OR (actual_minute_offset <>0)) AND (sense = exact));
END_ENTITY;

ENTITY date
  SUPERTYPE OF (ONEOF (calendar_date,
                       ordinal_date,
                       week_of_year_and_day_date,
                       year_month));
  year_component : year_number;
END_ENTITY;

ENTITY date_and_time;
  date_component : date;
  time_component : local_time;
END_ENTITY;

ENTITY date_role;
  name : label;
DERIVE
  description : text := get_description_value (SELF);
WHERE
  WR1: SIZEOF (USEDIN (SELF, 'BASIC_ATTRIBUTE_SCHEMA.' + 'DESCRIPTION_ATTRIBUTE.DESCRIBED_ITEM')) <= 1;
END_ENTITY;

ENTITY date_time_role;
  name : label;
DERIVE
  description : text := get_description_value (SELF);
WHERE
  WR1: SIZEOF (USEDIN (SELF, 'BASIC_ATTRIBUTE_SCHEMA.' + 'DESCRIPTION_ATTRIBUTE.DESCRIBED_ITEM')) <= 1;
END_ENTITY;

ENTITY event_occurrence;
  id : identifier;
  name : label;
  description : OPTIONAL text;
END_ENTITY;

ENTITY event_occurrence_context_role;
  name : label;
  description : OPTIONAL text;
END_ENTITY;

ENTITY event_occurrence_relationship;
  name : label;
  description : OPTIONAL text;
  relating_event : event_occurrence;
  related_event : event_occurrence;
END_ENTITY;

ENTITY event_occurrence_role;
  name : label;
  description : OPTIONAL text;
END_ENTITY;

ENTITY local_time;
  hour_component : hour_in_day;
  minute_component : OPTIONAL minute_in_hour;
  second_component : OPTIONAL second_in_minute;
  zone : coordinated_universal_time_offset;
WHERE
  WR1: valid_time (SELF);
END_ENTITY;

ENTITY ordinal_date
  SUBTYPE OF (date);
  day_component : day_in_year_number;
WHERE
  WR1: (NOT leap_year(SELF.year_component) AND { 1 <= day_component <= 365 }) OR (leap_year(SELF.year_component) AND { 1 <= day_component <= 366 });
END_ENTITY;

ENTITY relative_event_occurrence
  SUBTYPE OF (event_occurrence);
  base_event : event_occurrence;
  offset : time_measure_with_unit;
END_ENTITY;

ENTITY time_interval;
  id : identifier;
  name : label;
  description : OPTIONAL text;
END_ENTITY;

ENTITY time_interval_relationship;
  name : label;
  description : OPTIONAL text;
  relating_time_interval : time_interval;
  related_time_interval : time_interval;
END_ENTITY;

ENTITY time_interval_role;
  name : label;
  description : OPTIONAL text;
END_ENTITY;

ENTITY time_interval_with_bounds
  SUBTYPE OF (time_interval);
  primary_bound : OPTIONAL date_time_or_event_occurrence;
  secondary_bound : OPTIONAL date_time_or_event_occurrence;
  duration : OPTIONAL time_measure_with_unit;
WHERE
  WR1: NOT (EXISTS(secondary_bound) AND EXISTS(duration));
  WR2: EXISTS(primary_bound) OR EXISTS(secondary_bound);
END_ENTITY;

ENTITY time_role;
  name : label;
DERIVE
  description : text := get_description_value (SELF);
WHERE
  WR1: SIZEOF (USEDIN (SELF, 'BASIC_ATTRIBUTE_SCHEMA.' + 'DESCRIPTION_ATTRIBUTE.DESCRIBED_ITEM')) <= 1;
END_ENTITY;

ENTITY week_of_year_and_day_date
  SUBTYPE OF (date);
  week_component : week_in_year_number;
  day_component : OPTIONAL day_in_week_number;
END_ENTITY;

ENTITY year_month 
  SUBTYPE OF (date);
  month_component : month_in_year_number;
END_ENTITY;

FUNCTION acyclic_event_occurrence_relationship
 (relation : event_occurrence_relationship; relatives : SET[1:?] OF event_occurrence; specific_relation : STRING) : BOOLEAN; 
 LOCAL
      x : SET OF event_occurrence_relationship;
    END_LOCAL;

    IF relation.relating_event IN relatives THEN
      RETURN (FALSE);
    END_IF;
    x := QUERY(evnt <* bag_to_set(USEDIN(relation.relating_event, 'DATE_TIME_SCHEMA.' + 'EVENT_OCCURRENCE_RELATIONSHIP.' + 'RELATED_EVENT')) | specific_relation IN TYPEOF(evnt));
    REPEAT i := 1 TO HIINDEX(x);
      IF NOT acyclic_event_occurrence_relationship(x[i], relatives + relation.relating_event, specific_relation) THEN
        RETURN (FALSE);
      END_IF;
    END_REPEAT;
    RETURN (TRUE);
END_FUNCTION; 

FUNCTION acyclic_time_interval_relationship
 (relation : time_interval_relationship; relatives : SET[1:?] OF time_interval; specific_relation : STRING) : BOOLEAN; 
  LOCAL
      x : SET OF time_interval_relationship;
    END_LOCAL;

    IF relation.relating_time_interval IN relatives THEN
      RETURN (FALSE);
    END_IF;
    x := QUERY(ti <* bag_to_set(USEDIN(relation.relating_time_interval, 'DATE_TIME_SCHEMA.' + 'TIME_INTERVAL_RELATIONSHIP.' + 'RELATED_TIME_INTERVAL')) | specific_relation IN TYPEOF(ti));
    REPEAT i := 1 TO HIINDEX(x);
      IF NOT acyclic_time_interval_relationship(x[i], relatives + relation.relating_time_interval, specific_relation) THEN
        RETURN (FALSE);
      END_IF;
    END_REPEAT;
    RETURN (TRUE);
END_FUNCTION; 

FUNCTION leap_year
 (year : year_number) : BOOLEAN; 
  IF ((((year MOD 4) = 0) AND ((year MOD 100) <> 0)) OR ((year MOD 400) = 0)) THEN
    RETURN (TRUE);
  ELSE
    RETURN (FALSE);
  END_IF;
END_FUNCTION; 

FUNCTION valid_calendar_date
 (date : calendar_date) : LOGICAL; 
  CASE date.month_component OF
    1  : RETURN({ 1 <= date.day_component <= 31 });
    2  : BEGIN
           IF (leap_year(date.year_component)) THEN
             RETURN({ 1 <= date.day_component <= 29 });
           ELSE
             RETURN({ 1 <= date.day_component <= 28 });
           END_IF;
         END;
    3  : RETURN({ 1 <= date.day_component <= 31 });
    4  : RETURN({ 1 <= date.day_component <= 30 });
    5  : RETURN({ 1 <= date.day_component <= 31 });
    6  : RETURN({ 1 <= date.day_component <= 30 });
    7  : RETURN({ 1 <= date.day_component <= 31 });
    8  : RETURN({ 1 <= date.day_component <= 31 });
    9  : RETURN({ 1 <= date.day_component <= 30 });
    10 : RETURN({ 1 <= date.day_component <= 31 });
    11 : RETURN({ 1 <= date.day_component <= 30 });
    12 : RETURN({ 1 <= date.day_component <= 31 });
  END_CASE;
  RETURN (FALSE);
END_FUNCTION; 

FUNCTION valid_time
 (time : local_time) : BOOLEAN; 
  IF EXISTS(time.second_component) THEN
    RETURN (EXISTS(time.minute_component));
  ELSE
    RETURN (TRUE);
  END_IF;
END_FUNCTION; 

END_SCHEMA;  -- date_time_schema

