Skip to content

Add in-the-time-zone extension#25041

Open
imranidz wants to merge 10 commits intoraycast:mainfrom
imranidz:ext/in-the-time-zone
Open

Add in-the-time-zone extension#25041
imranidz wants to merge 10 commits intoraycast:mainfrom
imranidz:ext/in-the-time-zone

Conversation

@imranidz
Copy link

@imranidz imranidz commented Feb 1, 2026

Description

In The (Time) Zone helps you coordinate across time zones effortlessly. See at a glance when your team is available with a color-coded timeline view.

Features:

Timeline View — See all your cities at once with working hours highlighted
Time Scrubbing — Use arrow keys to shift time and instantly see how it affects everyone
Smart Colors — Green (9-5 working), Yellow (7-9, 5-12 marginal), Red (12-7 sleeping)
Sunrise/Sunset — Know when the sun rises and sets in each city
Any City — Search and add any city in the world
Perfect for remote teams, scheduling international meetings, or planning calls with friends abroad.

Screencast

image image image

Checklist

- Rename extension slug to in-the-time-zone
- Prepare extension for Raycast Store
- updates timezones
- init
- Initial commit
@raycastbot raycastbot added the new extension Label for PRs with new extensions label Feb 1, 2026
@raycastbot
Copy link
Collaborator

Congratulations on your new Raycast extension! 🚀

We're currently experiencing a high volume of incoming requests. As a result, the initial review may take up to 10-15 business days.

Once the PR is approved and merged, the extension will be available on our Store.

@imranidz imranidz marked this pull request as ready for review February 1, 2026 06:02
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 1, 2026

Greptile Summary

This PR adds a new "In The (Time) Zone" Raycast extension that provides a color-coded timeline view for comparing multiple cities across time zones, with keyboard-driven time scrubbing and sunrise/sunset metadata. The architecture is clean — preferences are wired through correctly, city IDs are namespaced to avoid collision, and the suncalc/luxon integration is straightforward.

Two logic bugs in timeline-renderer.ts need attention before merge:

  • Leap-year day-offset buggetDayDiff computes year × 365 + ordinal, which produces an off-by-one error when the base and local cities straddle Dec 31 → Jan 1 of a leap year (e.g. 2024-12-31 vs 2025-01-01 both yield the same value, reporting "same day" instead of +1).
  • (base) label applied by offset, not by identity — any city that happens to share the same UTC offset as the base city is also labelled (base) in the compact timeline, which is incorrect for pairs like London + UTC in winter.

Additional minor items:

  • generateTimelineMarkdown is exported but never imported — dead code.
  • The "Reset to Now" action (and README) say "current time" but getNextHour() always advances to the next full hour.
  • Dropdown option titles use lowercase instead of Title Case ("5 minutes""5 Minutes").

Confidence Score: 2/5

  • Not safe to merge — two logic bugs in the rendering layer produce incorrect output in real-world scenarios.
  • The leap-year bug in getDayDiff will silently show wrong day offsets for users near year-end, and the offset-based isBase check mislabels cities sharing a UTC offset with the base. Both are visible data correctness issues affecting the primary UI.
  • extensions/in-the-time-zone/src/timeline-renderer.ts requires the most attention due to the two logic bugs identified.

Important Files Changed

Filename Overview
extensions/in-the-time-zone/src/timeline-renderer.ts Core rendering logic; contains two bugs: a leap-year off-by-one in getDayDiff and an offset-based isBase check that mislabels cities sharing the same UTC offset as the base.
extensions/in-the-time-zone/src/in-the-time-zone.tsx Main command entry point; preference values are now read and passed correctly. Manual Preferences interface (already flagged in prior review threads) still present.
extensions/in-the-time-zone/src/timeline-view.tsx Timeline Detail view; scrub actions use the passed-in preference values correctly. "Reset to Now" advances to the next full hour rather than the current moment, creating a label/behaviour mismatch.
extensions/in-the-time-zone/package.json Extension manifest; both scrub preferences are properly defined and used. Dropdown option titles use lowercase instead of Title Case.

Last reviewed commit: "Use scrub preference..."

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@imranidz imranidz marked this pull request as draft February 3, 2026 02:53
@imranidz imranidz marked this pull request as ready for review February 3, 2026 02:53
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +23 to +50
"name": "defaultScrubMinutes",
"type": "dropdown",
"title": "Default Scrub Minutes",
"description": "Arrow keys scrub this many minutes.",
"data": [
{
"title": "5 minutes",
"value": "5"
},
{
"title": "10 minutes",
"value": "10"
},
{
"title": "15 minutes",
"value": "15"
},
{
"title": "30 minutes",
"value": "30"
},
{
"title": "60 minutes",
"value": "60"
}
],
"default": "30",
"required": false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultScrubMinutes preference is defined but never used. The code hardcodes ±60 minutes and ±30 minutes for arrow key scrubbing (see in-the-time-zone.tsx:217-234 and timeline-view.tsx:95-118). Either remove this unused preference or implement it using getPreferenceValues<Preferences>().

@imranidz imranidz marked this pull request as draft February 3, 2026 04:41
@imranidz imranidz marked this pull request as ready for review February 3, 2026 04:41
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +23 to +50
"name": "defaultScrubMinutes",
"type": "dropdown",
"title": "Default Scrub Minutes",
"description": "Arrow keys scrub this many minutes.",
"data": [
{
"title": "5 minutes",
"value": "5"
},
{
"title": "10 minutes",
"value": "10"
},
{
"title": "15 minutes",
"value": "15"
},
{
"title": "30 minutes",
"value": "30"
},
{
"title": "60 minutes",
"value": "60"
}
],
"default": "30",
"required": false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultScrubMinutes preference is never used - arrow keys hardcode ±60 and ±30 minutes in in-the-time-zone.tsx:217-234 and timeline-view.tsx:95-118. Either remove this preference or implement it using getPreferenceValues<Preferences>().

@imranidz imranidz marked this pull request as draft February 5, 2026 05:11
@imranidz imranidz marked this pull request as ready for review February 5, 2026 05:11
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +3 to +6
interface Preferences {
defaultScrubMinutes: string;
optionScrubMinutes: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove manual Preferences interface - auto-generated in raycast-env.d.ts

Suggested change
interface Preferences {
defaultScrubMinutes: string;
optionScrubMinutes: string;
}
import { DateTime } from "luxon";

Context Used: Rule from dashboard - What: Don't manually define Preferences for getPreferenceValues() or commends Argument interfa... (source)

@0xdhrv 0xdhrv self-assigned this Feb 9, 2026
@raycastbot
Copy link
Collaborator

This pull request has been automatically marked as stale because it did not have any recent activity.

It will be closed if no further activity occurs in the next 7 days to keep our backlog clean 😊

@raycastbot raycastbot added the status: stalled Stalled due inactivity label Feb 23, 2026
Copy link
Contributor

@0xdhrv 0xdhrv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution 🔥

We already have an extension in the Store that deals with Team Timezone. Could we consider enhancing the existing extension below instead of creating another one?

You can extend the existing extension by adding new commands to it, since it seems to have related functionality.

If there are unique features or workflows you’re aiming to add, we’d love to hear them and see if they can be integrated into this to avoid duplication and improve discoverability.

This would help avoid duplication and keep related functionality consolidated in one place.
As mentioned in our extension guidelines here ↗

@0xdhrv 0xdhrv marked this pull request as draft February 23, 2026 11:26
@raycastbot raycastbot removed the status: stalled Stalled due inactivity label Feb 23, 2026
@imranidz
Copy link
Author

Thanks for your contribution 🔥

We already have an extension in the Store that deals with Team Timezone. Could we consider enhancing the existing extension below instead of creating another one?

You can extend the existing extension by adding new commands to it, since it seems to have related functionality.

If there are unique features or workflows you’re aiming to add, we’d love to hear them and see if they can be integrated into this to avoid duplication and improve discoverability.

This would help avoid duplication and keep related functionality consolidated in one place.
As mentioned in our extension guidelines here ↗

Hi @0xdhrv thanks for your comment. If you look at the apps they're fundamentally different. Timezone buddy tries to have the concept of keeping track of your friends in different time zones.

My app is designed to figure out what time it is in other time zones, then scrub effectively left and right to figure out the overlaps of reasonable times between different different time zones (thus the name, "in-the-timezone"). Use case:

  • Oh I need to meet with these 3 people: me in the US, someone in India and one in Switzerland. When can I meet the soonest that's reasonable for them. I want to know ASAP without having to pull up a conversion calculator.
  • Oh I have a process that's running that'll complete at 12PM UTC, which team member do I delegate checking this process to effectively?
  • Hmm it's now 3PM PST, this process finished at 12PM UTC, what should I see the local time logs to be at?

Fundamentally, it requires taking existing time, and scrubbing back and forth to find the corresponding time across the zones. This does not exist in the others and is philosophically different in it's operations and use.

I understand that you forsee an overlap, but I think they're fundamentally different use cases and adding to it would change the philosophy of these two apps. Let me know if you'd like me to define mine more clearly.

@raycastbot
Copy link
Collaborator

This pull request has been automatically marked as stale because it did not have any recent activity.

It will be closed if no further activity occurs in the next 7 days to keep our backlog clean 😊

@raycastbot raycastbot added the status: stalled Stalled due inactivity label Mar 13, 2026
@imranidz imranidz marked this pull request as ready for review March 19, 2026 00:17
@imranidz
Copy link
Author

imranidz commented Mar 19, 2026

@0xdhrv or any other admin , please let me know what the plan is and if I can provide further clarification. Again as I mentioned above I think these are different use cases.

@raycastbot raycastbot removed the status: stalled Stalled due inactivity label Mar 19, 2026
Comment on lines +47 to +57
function getDayDiff(localTime: DateTime, baseTime: DateTime): string {
// Compare calendar dates in their respective timezones
// Using ordinal (day-of-year) ensures we compare calendar days, not absolute timestamps
const localDays = localTime.year * 365 + localTime.ordinal;
const baseDays = baseTime.year * 365 + baseTime.ordinal;
const diff = localDays - baseDays;

if (diff === 0) return "";
if (diff > 0) return ` +${diff}`;
return ` ${diff}`;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Leap-year bug produces wrong day offset

The formula year * 365 + ordinal does not account for leap years (which have 366 days), causing an off-by-one error when the two cities straddle a Dec 31 → Jan 1 boundary in a leap year.

Concrete example: base is 2024-12-31 (ordinal = 366), local is 2025-01-01 (ordinal = 1):

  • baseDays = 2024 × 365 + 366 = 739126
  • localDays = 2025 × 365 + 1 = 739126
  • diff = 0 — shows "same day" instead of +1

Replace with Luxon's own diff to stay correct across all year boundaries:

Suggested change
function getDayDiff(localTime: DateTime, baseTime: DateTime): string {
// Compare calendar dates in their respective timezones
// Using ordinal (day-of-year) ensures we compare calendar days, not absolute timestamps
const localDays = localTime.year * 365 + localTime.ordinal;
const baseDays = baseTime.year * 365 + baseTime.ordinal;
const diff = localDays - baseDays;
if (diff === 0) return "";
if (diff > 0) return ` +${diff}`;
return ` ${diff}`;
}
function getDayDiff(localTime: DateTime, baseTime: DateTime): string {
const diff = Math.round(
localTime.startOf("day").valueOf() / 86400000 - baseTime.startOf("day").valueOf() / 86400000
);
if (diff === 0) return "";
if (diff > 0) return ` +${diff}`;
return ` ${diff}`;
}

const isLast = rowIndex === rows.length - 1;

// Build label: CityName [padded] TimeLabel [padded] GMT Delta
const isBase = row.offsetMinutes === 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 (base) label shown for any city with the same UTC offset

isBase is determined by whether the city's offset from the base is zero, not by whether it actually is the base city. Two cities that happen to share the same UTC offset at the selected time (e.g. London and UTC in winter) will both display (base), which is misleading.

Pass the base city ID through the config or compare against the baseCityId directly:

Suggested change
const isBase = row.offsetMinutes === 0;
const isBase = zoneId === baseCityId || (!baseCityId && row.offsetMinutes === 0 && rowIndex === 0);

(Alternatively, thread baseCityId as an extra field on each row object to keep the check explicit.)

<Action
title="Reset to Now"
icon={Icon.Clock}
onAction={() => onSetBaseISO(getNextHour())}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "Reset to Now" advances to the next full hour, not the current time

getNextHour() rounds up to the next full hour (e.g. at 2:45 PM it returns 3:00 PM). Both the README ("Use Cmd+N to reset to current time") and the action title "Reset to Now" imply the view returns to the actual current moment, which it does not.

If the intent is to snap to the nearest whole hour as a scheduling convenience, consider renaming the action (e.g. "Reset to Next Hour") and updating the README accordingly. If the intent genuinely is to reset to now, replace getNextHour() with new Date().toISOString() (same fix applies in in-the-time-zone.tsx line 221).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new extension Label for PRs with new extensions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants