A beginner-friendly Rails API-only project that provides timezone information and current time for countries around the world.
- Get current time for multiple countries
- Automatic timezone detection from IP address
- Time gap calculation between your location and other countries
- List all available timezones (UTC/GMT offsets)
- View countries within a specific timezone
- No database required
- Fully Dockerized
- Ruby 4.0
- Rails 8.1.1 (API mode)
- Docker & Docker Compose
- ActiveSupport::TimeZone
# Build and start the application
docker compose up --build
# The API will be available at http://localhost:3000# Install dependencies
bundle install
# Start the server
bin/rails serverAll endpoints are versioned under /api/v1.
GET /api/v1/time/current?countries=Japan,China
Features:
- Returns current time for multiple countries
- Automatically detects user's timezone from IP address
- Calculates time gap between user's location and requested countries
Example Response:
{
"results": [
{
"country": "Japan",
"timezone": "Asia/Tokyo",
"currentTime": "2026-01-03T15:00:00+09:00",
"date": "2026-01-03",
"time": "15:00:00",
"gap": {
"hours": 2.0,
"description": "+2 hours",
"fromTimezone": "Asia/Bangkok"
}
},
{
"country": "China",
"timezone": "Asia/Shanghai",
"currentTime": "2026-01-03T14:00:00+08:00",
"date": "2026-01-03",
"time": "14:00:00",
"gap": {
"hours": 1.0,
"description": "+1 hour",
"fromTimezone": "Asia/Bangkok"
}
}
],
"userTimezone": {
"timezone": "Asia/Bangkok",
"offset": "+07:00",
"detectedFromIp": true
}
}Note: The
gapfield shows the time difference from the user's detected timezone. If timezone detection fails (e.g., local IP), the response will not includegapdata anduserTimezone.detectedFromIpwill befalse.
GET /api/v1/zones
Example Response:
{
"count": 150,
"zones": [
{
"name": "Pacific/Midway",
"offset": "-11:00",
"utcOffset": -39600
},
...
]
}GET /api/v1/zones/Asia%2FTokyo
Note: Use URL encoding for the zone name (replace
/with%2F)
Example Response:
{
"name": "Asia/Tokyo",
"offset": "+09:00",
"currentTime": "2026-01-03T15:00:00+09:00",
"date": "2026-01-03",
"time": "15:00:00",
"countries": ["Japan"]
}TimeStay/
├── app/
│ ├── controllers/
│ │ ├── application_controller.rb
│ │ └── api/
│ │ └── v1/
│ │ ├── zones_controller.rb # Timezone endpoints
│ │ └── time/
│ │ └── current_controller.rb # Current time endpoint
│ └── services/
│ ├── timezone_service.rb # Timezone business logic
│ └── ip_timezone_service.rb # IP-based timezone detection
├── config/
│ ├── routes.rb # API routes
│ ├── timezone_mappings.yml # Country → Timezone data
│ └── initializers/
│ └── geocoder.rb # Geocoder configuration
├── Dockerfile
└── docker-compose.yml
This project follows clean architecture principles:
| Layer | Responsibility |
|---|---|
| Controllers | Handle HTTP requests and responses |
| Services | Contain business logic |
| Config YAML | Store static data (country mappings) |
The API supports 80+ countries including:
- Asia: Japan, China, South Korea, India, Singapore, Thailand, etc.
- Europe: UK, France, Germany, Italy, Spain, etc.
- Americas: USA, Canada, Mexico, Brazil, Argentina, etc.
- Middle East: Saudi Arabia, UAE, Israel, Turkey, etc.
- Africa: Egypt, South Africa, Nigeria, Kenya, etc.
- Oceania: Australia, New Zealand, Fiji, etc.
See config/timezone_mappings.yml for the full list.
Using curl:
# Get current time for Japan and China
curl "http://localhost:3000/api/v1/time/current?countries=Japan,China"
# List all timezones
curl "http://localhost:3000/api/v1/zones"
# Get Tokyo timezone details
curl "http://localhost:3000/api/v1/zones/Asia%2FTokyo"This project uses ActiveSupport::TimeZone which is built into Rails. Timezone data is maintained by the IANA and included in Ruby's tzinfo gem. Country mappings are stored in a simple YAML file.
Service objects keep controllers thin and focused on HTTP concerns. All business logic lives in TimezoneService, making the code:
- Easier to test
- More reusable
- Simpler to understand
Using /api/v1 allows us to make breaking changes in the future (v2, v3) without breaking existing clients.
This project is for learning purposes. Feel free to use and modify as needed.