From dc3b317a89e807c7ea939568e8184cba252c84a3 Mon Sep 17 00:00:00 2001 From: Tzu-Jen Chan <126435471+tzujenchanmbd@users.noreply.github.com> Date: Thu, 8 May 2025 13:24:12 -0400 Subject: [PATCH 1/4] Add Local time data type and adjust timeframes.txt --- gtfs/spec/en/reference.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 254d9f76..0649b58d 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -90,6 +90,7 @@ Presence conditions applicable to fields and files: - **Integer** - An integer. - **Phone number** - A phone number. - **Time** - Time in the HH:MM:SS format (H:MM:SS is also accepted). The time is measured from "noon minus 12h" of the service day (effectively midnight except for days on which daylight savings time changes occur). For times occurring after midnight on the service day, enter the time as a value greater than 24:00:00 in HH:MM:SS.
*Example: `14:30:00` for 2:30PM or `25:35:00` for 1:35AM on the next day.* +- **Local time** - Time in the HH:MM:SS format (H:MM:SS is also accepted). Represents a wall-clock time shown in the local time of the specified location. - **Text** - A string of UTF-8 characters, which is aimed to be displayed and which must therefore be human readable. - **Timezone** - TZ timezone from the [https://www.iana.org/time-zones](https://www.iana.org/time-zones). Timezone names never contain the space character but may contain an underscore. Refer to [http://en.wikipedia.org/wiki/List\_of\_tz\_zones](http://en.wikipedia.org/wiki/List\_of\_tz\_zones) for a list of valid values.
*Example: `Asia/Tokyo`, `America/Los_Angeles` or `Africa/Cairo`.* - **URL** - A fully qualified URL that includes http:// or https://, and any special characters in the URL must be correctly escaped. See the following [http://www.w3.org/Addressing/URL/4\_URI\_Recommentations.html](http://www.w3.org/Addressing/URL/4\_URI\_Recommentations.html) for a description of how to create fully qualified URL values. @@ -403,14 +404,14 @@ There must not be overlapping time intervals for the same `timeframe_group_id` a | 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.
Values greater than `24:00:00` are forbidden. An empty value in `start_time` is considered `00:00:00`.

Conditionally Required:
- **Required** if `timeframes.end_time` is defined.
- **Forbidden** otherwise | -| `end_time` | Time | **Conditionally Required** | Defines the end of a timeframe. The interval does not include the end time.
Values greater than `24:00:00` are forbidden. An empty value in `end_time` is considered `24:00:00`.

Conditionally Required:
- **Required** if `timeframes.start_time` is defined.
- **Forbidden** otherwise | +| `start_time` | Local time | **Conditionally Required** | Defines the beginning of a timeframe. The interval includes the start time.
Values greater than `24:00:00` are forbidden. An empty value in `start_time` is considered `00:00:00`.

Conditionally Required:
- **Required** if `timeframes.end_time` is defined.
- **Forbidden** otherwise | +| `end_time` | Local time | **Conditionally Required** | Defines the end of a timeframe. The interval does not include the end time.
Values greater than `24:00:00` are forbidden. An empty value in `end_time` is considered `24:00:00`.

Conditionally Required:
- **Required** if `timeframes.start_time` is defined.
- **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](#timeframestxt), 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. +- The “time-of-day” for the fare event is computed relative to “current day” using GTFS Local time field-type semantics. ### rider_categories.txt From b3b3c478f2b2cb054ae8c6b2ef9267e71fc2fe7c Mon Sep 17 00:00:00 2001 From: Tzu-Jen Chan <126435471+tzujenchanmbd@users.noreply.github.com> Date: Thu, 8 May 2025 14:18:35 -0400 Subject: [PATCH 2/4] Clarify general desc for fare_transfer_rules --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 0649b58d..fcf09268 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -539,7 +539,7 @@ Fare rules for transfers between legs of travel defined in [`fare_leg_rules.txt` To process the cost of a multi-leg journey: -1. The applicable fare leg groups defined in `fare_leg_rules.txt` should be determined for all individual legs of travel based on the rider’s journey. +1. The applicable fare leg groups defined in `fare_leg_rules.txt` should be determined for all individual legs or effective fare legs of travel based on the rider’s journey. 2. The file [fare_transfer_rules.txt](#fare_transfer_rulestxt) must be filtered by the fields that define the characteristics of the transfer, these fields are: - `fare_transfer_rules.from_leg_group_id` - `fare_transfer_rules.to_leg_group_id`
From dacce377444ce03cdedc8edbbfb8944ad594be82 Mon Sep 17 00:00:00 2001 From: Tzu-Jen Chan <126435471+tzujenchanmbd@users.noreply.github.com> Date: Thu, 8 May 2025 18:22:56 -0400 Subject: [PATCH 3/4] Add "timer starts from the first matching leg" clarification --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index fcf09268..0654233b 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -560,7 +560,7 @@ To process the cost of a multi-leg journey: | `to_leg_group_id` | Foreign ID referencing `fare_leg_rules.leg_group_id` | Optional | Identifies a group of post-transfer fare leg rules.

If there are no matching `fare_transfer_rules.to_leg_group_id` values to the `leg_group_id` being filtered, empty `fare_transfer_rules.to_leg_group_id` will be matched by default.

An empty entry in `fare_transfer_rules.to_leg_group_id` corresponds to all leg groups defined under `fare_leg_rules.leg_group_id` excluding the ones listed under `fare_transfer_rules.to_leg_group_id` | | `transfer_count` | Non-zero integer | **Conditionally Forbidden** | Defines how many consecutive transfers the transfer rule may be applied to.

Valid options are:
`-1` - No limit.
`1` or more - Defines how many transfers the transfer rule may span.

If a sub-journey matches multiple records with different `transfer_count`s, then the rule with the minimum `transfer_count` that is greater than or equal to the current transfer count of the sub-journey is to be selected.

Conditionally Forbidden:
- **Forbidden** if `fare_transfer_rules.from_leg_group_id` does not equal `fare_transfer_rules.to_leg_group_id`.
- **Required** if `fare_transfer_rules.from_leg_group_id` equals `fare_transfer_rules.to_leg_group_id`. | | `duration_limit` | Positive integer | Optional | Defines the duration limit of the transfer.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_transfer_rules.duration_limit` must be empty. | -| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_transfer_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- **Required** if `fare_transfer_rules.duration_limit` is defined.
- **Forbidden** if `fare_transfer_rules.duration_limit` is empty. | +| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_transfer_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

When a transfer rule with the same `from_leg_group_id` and `to_leg_group_id` is matched multiple times consecutively within a multi-leg journey, the `duration_limit` specified by the rule should be measured starting from the first matched leg.

Conditionally Required:
- **Required** if `fare_transfer_rules.duration_limit` is defined.
- **Forbidden** if `fare_transfer_rules.duration_limit` is empty. | | `fare_transfer_type` | Enum | **Required** | Indicates the cost processing method of transferring between legs in a journey:
![](examples/2-leg.svg)
Valid options are:
`0` - From-leg `fare_leg_rules.fare_product_id` plus `fare_transfer_rules.fare_product_id`; A + AB.
`1` - From-leg `fare_leg_rules.fare_product_id` plus `fare_transfer_rules.fare_product_id` plus to-leg `fare_leg_rules.fare_product_id`; A + AB + B.
`2` - `fare_transfer_rules.fare_product_id`; AB.

Cost processing interactions between multiple transfers in a journey:
![](examples/3-leg.svg)
`fare_transfer_type`Processing A > BProcessing B > C
`0`A + ABS + BC
`1`A + AB +BS + BC + C
`2`ABS + BC
Where S indicates the total processed cost of the preceding leg(s) and transfer(s). | | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | Optional | The fare product required to transfer between two fare legs. If empty, the cost of the transfer rule is 0.| From 9ff1c18656364d1969d48fd50683427b83109a08 Mon Sep 17 00:00:00 2001 From: Tzu-Jen Chan <126435471+tzujenchanmbd@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:54:16 -0400 Subject: [PATCH 4/4] Replace current/next with first/last --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 0654233b..666f5ff8 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -560,7 +560,7 @@ To process the cost of a multi-leg journey: | `to_leg_group_id` | Foreign ID referencing `fare_leg_rules.leg_group_id` | Optional | Identifies a group of post-transfer fare leg rules.

If there are no matching `fare_transfer_rules.to_leg_group_id` values to the `leg_group_id` being filtered, empty `fare_transfer_rules.to_leg_group_id` will be matched by default.

An empty entry in `fare_transfer_rules.to_leg_group_id` corresponds to all leg groups defined under `fare_leg_rules.leg_group_id` excluding the ones listed under `fare_transfer_rules.to_leg_group_id` | | `transfer_count` | Non-zero integer | **Conditionally Forbidden** | Defines how many consecutive transfers the transfer rule may be applied to.

Valid options are:
`-1` - No limit.
`1` or more - Defines how many transfers the transfer rule may span.

If a sub-journey matches multiple records with different `transfer_count`s, then the rule with the minimum `transfer_count` that is greater than or equal to the current transfer count of the sub-journey is to be selected.

Conditionally Forbidden:
- **Forbidden** if `fare_transfer_rules.from_leg_group_id` does not equal `fare_transfer_rules.to_leg_group_id`.
- **Required** if `fare_transfer_rules.from_leg_group_id` equals `fare_transfer_rules.to_leg_group_id`. | | `duration_limit` | Positive integer | Optional | Defines the duration limit of the transfer.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_transfer_rules.duration_limit` must be empty. | -| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_transfer_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

When a transfer rule with the same `from_leg_group_id` and `to_leg_group_id` is matched multiple times consecutively within a multi-leg journey, the `duration_limit` specified by the rule should be measured starting from the first matched leg.

Conditionally Required:
- **Required** if `fare_transfer_rules.duration_limit` is defined.
- **Forbidden** if `fare_transfer_rules.duration_limit` is empty. | +| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_transfer_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the first leg in transfer sub-journey and the arrival fare validation of the last leg in transfer sub-journey.
`1` - Between the departure fare validation of the first leg in transfer sub-journey and the departure fare validation of the last leg in transfer sub-journey.
`2` - Between the arrival fare validation of the first leg in transfer sub-journey and the departure fare validation of the last leg in transfer sub-journey.
`3` - Between the arrival fare validation of the first leg in transfer sub-journey and the arrival fare validation of the last leg in transfer sub-journey.

When a transfer rule with the same `from_leg_group_id` and `to_leg_group_id` is matched multiple times consecutively within a multi-leg journey, the `duration_limit` specified by the rule should be measured starting from the first matched leg.

Conditionally Required:
- **Required** if `fare_transfer_rules.duration_limit` is defined.
- **Forbidden** if `fare_transfer_rules.duration_limit` is empty. | | `fare_transfer_type` | Enum | **Required** | Indicates the cost processing method of transferring between legs in a journey:
![](examples/2-leg.svg)
Valid options are:
`0` - From-leg `fare_leg_rules.fare_product_id` plus `fare_transfer_rules.fare_product_id`; A + AB.
`1` - From-leg `fare_leg_rules.fare_product_id` plus `fare_transfer_rules.fare_product_id` plus to-leg `fare_leg_rules.fare_product_id`; A + AB + B.
`2` - `fare_transfer_rules.fare_product_id`; AB.

Cost processing interactions between multiple transfers in a journey:
![](examples/3-leg.svg)
`fare_transfer_type`Processing A > BProcessing B > C
`0`A + ABS + BC
`1`A + AB +BS + BC + C
`2`ABS + BC
Where S indicates the total processed cost of the preceding leg(s) and transfer(s). | | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | Optional | The fare product required to transfer between two fare legs. If empty, the cost of the transfer rule is 0.|