Skip to content

Commit

Permalink
Add variable fares by time or day (google#357)
Browse files Browse the repository at this point in the history
* Added timeframes

* Add date-based fares

* Update timeframes.txt description

* Change end time from required to optional

* Records with the same timeframe id must cover a 24h period

* no overlap

* remove requirement on 24h coverage and overlapping

* fix typo

* Revert changes in timeframes.end_time

* move service_id to timeframes.txt

* Move timeframes to the fares v2 section

* rename timeframe_id to timeframe_group_id

* place timeframes.txt with other Fares v2 files

* Rename from/to fields + change empty semantics

- Rename from_timeframe_id and to_timeframe_id to start_timeframe_group_id and to_timeframe_group_id
- Change empty semantics so that an empty entry means the fare isn't affected (as opposed to the "empty means everything except")

* add line break

* Add timeframes description

* Make timeframe fields required

* service_id references calendar or calendar_dates

* Add requirements

- the whole day must be covered with timeframes if one is defined
- there should not be overlapping time frames with the same timeframe_group_id and service_id

* typo

* Update timeframe fields description

* end time is not included in the interval

* Add timeframe_type field

* Overlapping time frames must not be defined

for the same timeframe_group_id and service_id

* introduce override

Introduce an override field in fare_leg_rules and remove the requirement of needing the whole day covered with timeframes if one is defined.

* improve override description

* Revert "improve override description"

This reverts commit d4afc6e.

* improve override description

* update timeframe fields description

changes "a row" to "one row"

* remove timeframe fields for no exact matched found

* Update descriptions for empty values:

- removed the "If there are no matching `fare_leg_rules.start_timeframe_group_id` values to the
`timeframe_group_id` being filtered..."
- added "that rule will match a particular leg if either start_timeframe_group_id is empty, or if there exists at least a row in `timeframes.txt` where all..."

* Both values need to be empty or neither should be empty

as discussed in the last working group meeting

* Change to local time semantics

As discussed in the working group meeting

* Changed name start/end_timeframe_group_id

As discussed in working group meeting

* Remove timeframe_type & priority fields

Changes as discussed in working group meeting

* revise "start" to "end"

* Remove "initial" after Gavriel's comment

Gavriel pointed out in the proposal document that "initial" is un-necessary https://docs.google.com/document/d/1N3WpgAh2OxPuE5Vvjbd6mjvKK42M7mSVdxscVvzP8SU/edit

* Remove unnecessary empty description

* Changes based on Jeremy's feedback

Comment google#357 (comment)

1. Specify "time of day" of fare leg's start/end time
2. Clarify the start_time/end_time are in timeframes.txt

Additional: change "row" to " "record" to synchronize the rest of GTFS.

* Modify empty wording

Based on Hal's suggestion - google#357 (comment)

* Move overlapping requirement out of service_id description

---------

Co-authored-by: omar-kabbani <[email protected]>
Co-authored-by: Tzu-Jen Chan <[email protected]>
  • Loading branch information
3 people authored and gcamp committed Sep 25, 2023
1 parent c1dd969 commit 816ed5c
Showing 1 changed file with 32 additions and 3 deletions.
35 changes: 32 additions & 3 deletions gtfs/spec/en/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This document defines the format and structure of the files that comprise a GTFS
- [calendar\_dates.txt](#calendar_datestxt)
- [fare\_attributes.txt](#fare_attributestxt)
- [fare\_rules.txt](#fare_rulestxt)
- [timeframes.txt](#timeframestxt)
- [fare\_media.txt](#fare_mediatxt)
- [fare\_products.txt](#fare_productstxt)
- [fare\_leg\_rules.txt](#fare_leg_rulestxt)
Expand Down Expand Up @@ -110,6 +111,7 @@ This specification defines the following files:
| [calendar_dates.txt](#calendar_datestxt) | **Conditionally Required** | Exceptions for the services defined in the [calendar.txt](#calendartxt). <br><br>Conditionally Required:<br> - **Required** if [calendar.txt](#calendartxt) is omitted. In which case [calendar_dates.txt](#calendar_datestxt) must contain all dates of service. <br> - Optional otherwise. |
| [fare_attributes.txt](#fare_attributestxt) | Optional | Fare information for a transit agency's routes. |
| [fare_rules.txt](#fare_rulestxt) | Optional | Rules to apply fares for itineraries. |
| [timeframes.txt](#timeframestxt) | Optional | Date and time periods to use in fare rules for fares that depend on date and time factors. |
| [fare_media.txt](#fare_mediatxt) | Optional | To describe the fare media that can be employed to use fare products. <br><br>File [fare_media.txt](#fare_mediatxt) describes concepts that are not represented in [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). As such, the use of [fare_media.txt](#fare_mediatxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). |
| [fare_products.txt](#fare_productstxt) | Optional | To describe the different types of tickets or fares that can be purchased by riders.<br><br>File [fare_products.txt](#fare_productstxt) describes fare products that are not represented in [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). As such, the use of [fare_products.txt](#fare_productstxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). |
| [fare_leg_rules.txt](#fare_leg_rulestxt) | Optional | Fare rules for individual legs of travel.<br><br>File [fare_leg_rules.txt](#fare_leg_rulestxt) provides a more detailed method for modeling fare structures. As such, the use of [fare_leg_rules.txt](#fare_leg_rulestxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). |
Expand Down Expand Up @@ -345,6 +347,27 @@ For examples that demonstrate how to specify a fare structure with [fare_rules.t
| `destination_id` | Foreign ID referencing `stops.zone_id` | Optional | Identifies a destination zone. If a fare class has multiple destination zones, create a record in [fare_rules.txt](#fare_rules.txt) for each `destination_id`.<hr>*Example: The `origin_id` and `destination_id` fields could be used together to specify that fare class "b" is valid for travel between zones 3 and 4, and for travel between zones 3 and 5, the [fare_rules.txt](#fare_rules.txt) file would contain these records for the fare class:* <br>`fare_id,...,origin_id,destination_id` <br>`b,...,3,4`<br> `b,...,3,5` |
| `contains_id` | Foreign ID referencing `stops.zone_id` | Optional | Identifies the zones that a rider will enter while using a given fare class. Used in some systems to calculate correct fare class. <hr>*Example: If fare class "c" is associated with all travel on the GRT route that passes through zones 5, 6, and 7 the [fare_rules.txt](#fare_rules.txt) would contain these records:* <br> `fare_id,route_id,...,contains_id` <br> `c,GRT,...,5` <br>`c,GRT,...,6` <br>`c,GRT,...,7` <br> *Because all `contains_id` zones must be matched for the fare to apply, an itinerary that passes through zones 5 and 6 but not zone 7 would not have fare class "c". For more detail, see [https://code.google.com/p/googletransitdatafeed/wiki/FareExamples](https://code.google.com/p/googletransitdatafeed/wiki/FareExamples) in the GoogleTransitDataFeed project wiki.* |

### timeframes.txt

File: **Optional**

Primary key (*)

Used to describe fares that can vary based on the time of day, the day of the week, or a particular day in the year. Timeframes can be associated with fare products in `fare_leg_rules.txt`. <br>
There must not be overlapping time intervals for the same `timeframe_group_id` and `service_id` values.

| Field Name | Type | Presence | Description |
| ------ | ------ | ------ | ------ |
| `timeframe_group_id` | ID | **Required** | Identifies a timeframe or set of timeframes. |
| `start_time` | Time | **Conditionally Required** | Defines the beginning of a timeframe. The interval includes the start time.<br> Values greater than `24:00:00` are forbidden. An empty value in `start_time` is considered `00:00:00`. <br><br> Conditionally Required:<br> - **Required** if `timeframes.end_time` is defined.<br> - **Forbidden** otherwise |
| `end_time` | Time | **Conditionally Required** | Defines the end of a timeframe. The interval does not include the end time.<br> Values greater than `24:00:00` are forbidden. An empty value in `end_time` is considered `24:00:00`. <br><br> Conditionally Required:<br> - **Required** if `timeframes.start_time` is defined.<br> - **Forbidden** otherwise |
| `service_id` | Foreign ID referencing `calendar.service_id` or `calendar_dates.service_id` | **Required** | Identifies a set of dates that a timeframe is in effect. |

#### Timeframe Local Time Semantics
- When evaluating a fare event’s time against `timeframes.txt`, the event time is computed in local time using the local timezone, as determined by the `stop_timezone`, if specified, of the stop or parent station for the fare event. If not specified, the feed’s agency timezone should be used instead.
- The “current day” is the current date of the fare event’s time, computed relative to the local timezone. The “current day” may be different from the service day of a fare leg’s trip, especially for trips that extend past midnight.
- The “time-of-day” for the fare event is computed relative to “current day” using GTFS Time field-type semantics.

### fare_media.txt

File: **Optional**
Expand Down Expand Up @@ -380,7 +403,7 @@ To describe the different types of tickets or fares that can be purchased by rid

File: **Optional**

Primary Key (`network_id, from_area_id, to_area_id, fare_product_id`)
Primary Key (`network_id, from_area_id, to_area_id, from_timeframe_group_id, to_timeframe_group_id, fare_product_id`)

Fare rules for individual legs of travel.

Expand All @@ -391,14 +414,18 @@ To process the cost of a leg:
1. The file `fare_leg_rules.txt` must be filtered by the fields that define the characteristics of travel, these fields are:
- `fare_leg_rules.network_id`
- `fare_leg_rules.from_area_id`
- `fare_leg_rules.to_area_id`<br/>
- `fare_leg_rules.to_area_id`
- `fare_leg_rules.from_timeframe_group_id`
- `fare_leg_rules.to_timeframe_group_id`<br/>
<br/>

2. If the leg exactly matches a record in `fare_leg_rules.txt` based on the characteristics of travel, that record must be processed to determine the cost of the leg.
<br/>

3. If no exact matches are found, then empty entries in `fare_leg_rules.network_id`, `fare_leg_rules.from_area_id`, and `fare_leg_rules.to_area_id` must be checked to process the cost of the leg:
- An empty entry in `fare_leg_rules.network_id` corresponds to all networks defined in `routes.txt` excluding the ones listed under `fare_leg_rules.network_id`
- An empty entry in `fare_leg_rules.from_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.from_area_id`
- An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id`<br/>
- An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id`
<br/>

4. If the leg does not match any of the rules described above, then the fare is unknown.
Expand All @@ -411,6 +438,8 @@ To process the cost of a leg:
| `network_id` | Foreign ID referencing `routes.network_id` | Optional | Identifies a route network that applies for the fare leg rule.<br><br>If there are no matching `fare_leg_rules.network_id` values to the `network_id` being filtered, empty `fare_leg_rules.network_id` will be matched by default.<br><br> An empty entry in `fare_leg_rules.network_id` corresponds to all networks defined in `routes.txt` excluding the ones listed under `fare_leg_rules.network_id` |
| `from_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies a departure area.<br><br>If there are no matching `fare_leg_rules.from_area_id` values to the `area_id` being filtered, empty `fare_leg_rules.from_area_id` will be matched by default. <br><br>An empty entry in `fare_leg_rules.from_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.from_area_id` |
| `to_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies an arrival area.<br><br>If there are no matching `fare_leg_rules.to_area_id` values to the `area_id` being filtered, empty `fare_leg_rules.to_area_id` will be matched by default.<br><br> An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id` |
| `from_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the start of the fare leg.<br><br>The “start time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled departure time of a bus at the start of a fare leg where the rider boards and validates their fare. For the rule matching semantics below, the start time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s departure event should be used for timezone resolution, where appropriate.<br><br>For a fare leg rule that specifies a `from_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in `timeframes.txt` where all of the following conditions are true<br>- The value of `timeframe_group_id` is equal to the `from_timeframe_group_id` value.<br>- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s start time.<br>- The “time-of-day” of the fare leg's start time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.<br><br>An empty `fare_leg_rules.from_timeframe_group_id` indicates that the start time of the leg does not affect the matching of this rule. |
| `to_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the end of the fare leg.<br><br>The “end time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled arrival time of a bus at the end of a fare leg where the rider gets off and validates their fare. For the rule matching semantics below, the end time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s arrival event should be used for timezone resolution, where appropriate.<br><br>For a fare leg rule that specifies a `to_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in `timeframes.txt` where all of the following conditions are true<br>- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.<br>- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.<br>- The “time-of-day” of the fare leg's end time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.<br><br>An empty `fare_leg_rules.to_timeframe_group_id` indicates that the end time of the leg does not affect the matching of this rule. |
| `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | **Required** | The fare product required to travel the leg. |

### fare_transfer_rules.txt
Expand Down

0 comments on commit 816ed5c

Please sign in to comment.