write a zsh script that will take a time in local time, and output that time in local time (with timezone), and a bunch of other timezones. Set this preset to default to eastern US, UTC and CET
Here's a zsh script that converts a given local time to multiple timezones:
```zsh
#!/bin/zsh
# Function to display usage
usage() {
echo "Usage: $0 [TIME]"
echo ""
echo "Convert a local time to multiple timezones"
echo ""
echo "Arguments:"
echo " TIME Time in format HH:MM or 'HH:MM AM/PM' (defaults to current time)"
echo ""
echo "Examples:"
echo " $0 14:30"
echo " $0 '2:30 PM'"
echo " $0 '9:15 AM'"
echo " $0"
echo ""
echo "Output timezones:"
echo " - Local time (with timezone)"
echo " - Eastern US (America/New_York)"
echo " - UTC"
echo " - Central European Time (Europe/Berlin)"
}
# Function to convert and display time in multiple timezones
convert_time() {
local input_time="$1"
local base_date
# If no time provided, use current time
if [[ -z "$input_time" ]]; then
base_date=$(date)
echo "Using current time..."
else
# Try to parse the input time for today's date
# Handle both 24-hour and 12-hour formats
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
# 12-hour format
base_date=$(date -j -f "%I:%M %p" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null)
else
# 24-hour format
base_date=$(date -j -f "%H:%M" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null)
fi
# If parsing failed, try a different approach
if [[ $? -ne 0 ]] || [[ -z "$base_date" ]]; then
# For Linux/GNU date (fallback)
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
base_date=$(date -d "$input_time" 2>/dev/null)
else
base_date=$(date -d "today $input_time" 2>/dev/null)
fi
if [[ $? -ne 0 ]]; then
echo "Error: Could not parse time '$input_time'"
echo "Please use format HH:MM or 'HH:MM AM/PM'"
return 1
fi
else
# Convert back to full date string for macOS
base_date=$(date -j -f "%Y-%m-%d %H:%M:%S" "$base_date" 2>/dev/null)
fi
fi
echo "Time Conversion Results:"
echo "========================"
# Get the epoch timestamp from our base date
if command -v gdate >/dev/null 2>&1; then
# Use GNU date if available (common on macOS with coreutils)
epoch=$(gdate -d "$base_date" +%s 2>/dev/null)
elif date --version 2>/dev/null | grep -q GNU; then
# GNU date (Linux)
epoch=$(date -d "$base_date" +%s 2>/dev/null)
else
# BSD date (macOS default)
epoch=$(date -j -f "%a %b %d %H:%M:%S %Z %Y" "$base_date" +%s 2>/dev/null)
fi
if [[ -z "$epoch" ]] || [[ $? -ne 0 ]]; then
# Fallback: if we can't parse, use current time
epoch=$(date +%s)
echo "Warning: Using current time as fallback"
fi
# Define timezone mappings
local -A timezones
timezones[Local]=""
timezones["Eastern US"]="America/New_York"
timezones[UTC]="UTC"
timezones[CET]="Europe/Berlin"
# Display times in each timezone
for tz_name in "Local" "Eastern US" "UTC" "CET"; do
if [[ "$tz_name" == "Local" ]]; then
# Local time
local_time=$(date -r "$epoch" "+%Y-%m-%d %H:%M:%S %Z (%z)")
printf "%-12s: %s\n" "$tz_name" "$local_time"
else
# Other timezones
local tz_time
if command -v gdate >/dev/null 2>&1; then
# GNU date
tz_time=$(TZ="${timezones[$tz_name]}" gdate -d "@$epoch" "+%Y-%m-%d %H:%M:%S %Z (%z)")
else
# BSD date
tz_time=$(TZ="${timezones[$tz_name]}" date -r "$epoch" "+%Y-%m-%d %H:%M:%S %Z (%z)")
fi
printf "%-12s: %s\n" "$tz_name" "$tz_time"
fi
done
}
# Main script logic
main() {
# Check for help flag
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
usage
return 0
fi
# Convert the time
convert_time "$1"
}
# Run the main function with all arguments
main "$@"
```
To use this script:
1. **Save it to a file** (e.g., `timeconv.zsh`)
2. **Make it executable**: `chmod +x timeconv.zsh`
3. **Run it**:
```bash
# Convert current time
./timeconv.zsh
# Convert 2:30 PM
./timeconv.zsh "2:30 PM"
# Convert 14:30 (24-hour format)
./timeconv.zsh 14:30
# Convert 9:15 AM
./timeconv.zsh "9:15 AM"
# Show help
./timeconv.zsh --help
```
**Example output:**
```
Time Conversion Results:
========================
Local : 2024-01-15 14:30:00 PST (-0800)
Eastern US : 2024-01-15 17:30:00 EST (-0500)
UTC : 2024-01-15 22:30:00 UTC (+0000)
CET : 2024-01-15 23:30:00 CET (+0100)
```
**Features:**
- Handles both 12-hour (2:30 PM) and 24-hour (14:30) time formats
- Defaults to current time if no time is provided
- Shows local time with timezone information
- Converts to Eastern US, UTC, and CET as requested
- Works on both macOS (BSD date) and Linux (GNU date)
- Includes error handling and help documentation
The script automatically detects your local timezone and converts the input time to the specified timezones, showing the full date, time, timezone abbreviation, and UTC offset for clarity.This is great. Can you compress the code so that it can work as a zsh function in zshrc, and also compress the output so it is just on one line, and skip the date if the time is on the same data as localtime
Here's a compressed version that works as a zsh function:
```zsh
timeconv() {
local input_time="$1"
local epoch base_date
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Usage: timeconv [TIME] - Convert time to Local/Eastern/UTC/CET (e.g., timeconv '2:30 PM' or timeconv 14:30)"
return 0
fi
if [[ -z "$input_time" ]]; then
epoch=$(date +%s)
else
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
base_date=$(date -j -f "%I:%M %p" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "$input_time" 2>/dev/null)
else
base_date=$(date -j -f "%H:%M" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "today $input_time" 2>/dev/null)
fi
if [[ -z "$base_date" ]]; then
echo "Error: Could not parse time '$input_time'"
return 1
fi
if command -v gdate >/dev/null 2>&1; then
epoch=$(gdate -d "$base_date" +%s 2>/dev/null)
elif date --version 2>&1 | grep -q GNU; then
epoch=$(date -d "$base_date" +%s 2>/dev/null)
else
epoch=$(date -j -f "%a %b %d %H:%M:%S %Z %Y" "$base_date" +%s 2>/dev/null || date +%s)
fi
fi
local today=$(date +%Y-%m-%d)
local target_date=$(date -r "$epoch" +%Y-%m-%d)
local date_prefix=""
[[ "$today" != "$target_date" ]] && date_prefix="$target_date "
local local_time=$(date -r "$epoch" "+${date_prefix}%H:%M %Z")
local eastern_time=$(TZ="America/New_York" date -r "$epoch" "+%H:%M %Z")
local utc_time=$(TZ="UTC" date -r "$epoch" "+%H:%M %Z")
local cet_time=$(TZ="Europe/Berlin" date -r "$epoch" "+%H:%M %Z")
echo "Local: $local_time | Eastern: $eastern_time | UTC: $utc_time | CET: $cet_time"
}
```
**Usage:**
```bash
# Current time
timeconv
# 12-hour format
timeconv "2:30 PM"
# 24-hour format
timeconv 14:30
# Help
timeconv -h
```
**Example output:**
```bash
# Same day:
Local: 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET
# Different day (when conversion crosses midnight):
Local: 2025-01-16 01:30 PST | Eastern: 04:30 EST | UTC: 09:30 UTC | CET: 10:30 CET
```
The function:
- Works as a single zsh function you can add to your `.zshrc`
- Outputs everything on one line
- Only shows the date when the target time is different from today's local date
- Maintains all the timezone conversion functionality
- Handles both 12-hour and 24-hour input formats
- Works on both macOS and Linux
Just add this function to your `.zshrc` file and run `source ~/.zshrc` to start using it!Almost what I want -- always show the date at the start, but don't show it for any timezone that shares the same date
Here's the updated function:
```zsh
timeconv() {
local input_time="$1"
local epoch base_date
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Usage: timeconv [TIME] - Convert time to Local/Eastern/UTC/CET (e.g., timeconv '2:30 PM' or timeconv 14:30)"
return 0
fi
if [[ -z "$input_time" ]]; then
epoch=$(date +%s)
else
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
base_date=$(date -j -f "%I:%M %p" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "$input_time" 2>/dev/null)
else
base_date=$(date -j -f "%H:%M" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "today $input_time" 2>/dev/null)
fi
if [[ -z "$base_date" ]]; then
echo "Error: Could not parse time '$input_time'"
return 1
fi
if command -v gdate >/dev/null 2>&1; then
epoch=$(gdate -d "$base_date" +%s 2>/dev/null)
elif date --version 2>&1 | grep -q GNU; then
epoch=$(date -d "$base_date" +%s 2>/dev/null)
else
epoch=$(date -j -f "%a %b %d %H:%M:%S %Z %Y" "$base_date" +%s 2>/dev/null || date +%s)
fi
fi
local local_date=$(date -r "$epoch" +%Y-%m-%d)
local eastern_date=$(TZ="America/New_York" date -r "$epoch" +%Y-%m-%d)
local utc_date=$(TZ="UTC" date -r "$epoch" +%Y-%m-%d)
local cet_date=$(TZ="Europe/Berlin" date -r "$epoch" +%Y-%m-%d)
local local_time=$(date -r "$epoch" "+$local_date %H:%M %Z")
local eastern_time=$(TZ="America/New_York" date -r "$epoch" "+%H:%M %Z")
[[ "$eastern_date" != "$local_date" ]] && eastern_time="$eastern_date $eastern_time"
local utc_time=$(TZ="UTC" date -r "$epoch" "+%H:%M %Z")
[[ "$utc_date" != "$local_date" ]] && utc_time="$utc_date $utc_time"
local cet_time=$(TZ="Europe/Berlin" date -r "$epoch" "+%H:%M %Z")
[[ "$cet_date" != "$local_date" ]] && cet_time="$cet_date $cet_time"
echo "Local: $local_time | Eastern: $eastern_time | UTC: $utc_time | CET: $cet_time"
}
```
**Example outputs:**
```bash
# When all timezones are on the same date:
timeconv "2:00 PM"
Local: 2024-01-15 14:00 PST | Eastern: 17:00 EST | UTC: 22:00 UTC | CET: 23:00 CET
# When some timezones cross into the next day:
timeconv "11:30 PM"
Local: 2024-01-15 23:30 PST | Eastern: 2024-01-16 02:30 EST | UTC: 2024-01-16 07:30 UTC | CET: 2024-01-16 08:30 CET
# When converting a time that puts local in a different day:
timeconv "2:00 AM"
Local: 2024-01-15 02:00 PST | Eastern: 05:00 EST | UTC: 10:00 UTC | CET: 11:00 CET
```
Now the function:
- Always shows the date for Local time
- Only shows the date for other timezones if their date differs from the local date
- Keeps everything on one line
- Maintains all the parsing and timezone conversion functionalityokay, one more addition. Take a second argument, and add that timezone to the list of timezones to print
Here's the updated function with support for a second timezone argument:
```zsh
timeconv() {
local input_time="$1"
local custom_tz="$2"
local epoch base_date
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Usage: timeconv [TIME] [TIMEZONE] - Convert time to Local/Eastern/UTC/CET/Custom (e.g., timeconv '2:30 PM' 'Asia/Tokyo')"
return 0
fi
if [[ -z "$input_time" ]]; then
epoch=$(date +%s)
else
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
base_date=$(date -j -f "%I:%M %p" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "$input_time" 2>/dev/null)
else
base_date=$(date -j -f "%H:%M" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "today $input_time" 2>/dev/null)
fi
if [[ -z "$base_date" ]]; then
echo "Error: Could not parse time '$input_time'"
return 1
fi
if command -v gdate >/dev/null 2>&1; then
epoch=$(gdate -d "$base_date" +%s 2>/dev/null)
elif date --version 2>&1 | grep -q GNU; then
epoch=$(date -d "$base_date" +%s 2>/dev/null)
else
epoch=$(date -j -f "%a %b %d %H:%M:%S %Z %Y" "$base_date" +%s 2>/dev/null || date +%s)
fi
fi
local local_date=$(date -r "$epoch" +%Y-%m-%d)
local eastern_date=$(TZ="America/New_York" date -r "$epoch" +%Y-%m-%d)
local utc_date=$(TZ="UTC" date -r "$epoch" +%Y-%m-%d)
local cet_date=$(TZ="Europe/Berlin" date -r "$epoch" +%Y-%m-%d)
local local_time=$(date -r "$epoch" "+$local_date %H:%M %Z")
local eastern_time=$(TZ="America/New_York" date -r "$epoch" "+%H:%M %Z")
[[ "$eastern_date" != "$local_date" ]] && eastern_time="$eastern_date $eastern_time"
local utc_time=$(TZ="UTC" date -r "$epoch" "+%H:%M %Z")
[[ "$utc_date" != "$local_date" ]] && utc_time="$utc_date $utc_time"
local cet_time=$(TZ="Europe/Berlin" date -r "$epoch" "+%H:%M %Z")
[[ "$cet_date" != "$local_date" ]] && cet_time="$cet_date $cet_time"
local output="Local: $local_time | Eastern: $eastern_time | UTC: $utc_time | CET: $cet_time"
if [[ -n "$custom_tz" ]]; then
local custom_date=$(TZ="$custom_tz" date -r "$epoch" +%Y-%m-%d 2>/dev/null)
if [[ $? -eq 0 ]]; then
local custom_time=$(TZ="$custom_tz" date -r "$epoch" "+%H:%M %Z")
[[ "$custom_date" != "$local_date" ]] && custom_time="$custom_date $custom_time"
output="$output | Custom: $custom_time"
else
echo "Error: Invalid timezone '$custom_tz'"
return 1
fi
fi
echo "$output"
}
```
**Usage examples:**
```bash
# Standard usage (no custom timezone)
timeconv "2:30 PM"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET
# With custom timezone
timeconv "2:30 PM" "Asia/Tokyo"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Custom: 2024-01-16 07:30 JST
# Current time with custom timezone
timeconv "" "Australia/Sydney"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Custom: 2024-01-16 09:30 AEDT
# Just time argument (no custom timezone)
timeconv "11:45 PM"
Local: 2024-01-15 23:45 PST | Eastern: 2024-01-16 02:45 EST | UTC: 2024-01-16 07:45 UTC | CET: 2024-01-16 08:45 CET
# With invalid timezone (shows error)
timeconv "2:30 PM" "Invalid/Timezone"
Error: Invalid timezone 'Invalid/Timezone'
```
**Common timezone examples you can use:**
- `Asia/Tokyo`
- `Australia/Sydney`
- `Europe/London`
- `America/Los_Angeles`
- `America/Chicago`
- `Asia/Shanghai`
- `Pacific/Auckland`
The function now:
- Takes an optional second argument for a custom timezone
- Validates the custom timezone and shows an error if invalid
- Applies the same date logic (only shows date if different from local date)
- Labels the custom timezone as "Custom" in the output
- Maintains backward compatibility (works the same if no second argument provided)can you do this so that the custom timezone is labelled with the timezone not 'Custom'
Here's the updated function that labels the custom timezone with its name:
```zsh
timeconv() {
local input_time="$1"
local custom_tz="$2"
local epoch base_date
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Usage: timeconv [TIME] [TIMEZONE] - Convert time to Local/Eastern/UTC/CET/Custom (e.g., timeconv '2:30 PM' 'Asia/Tokyo')"
return 0
fi
if [[ -z "$input_time" ]]; then
epoch=$(date +%s)
else
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
base_date=$(date -j -f "%I:%M %p" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "$input_time" 2>/dev/null)
else
base_date=$(date -j -f "%H:%M" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "today $input_time" 2>/dev/null)
fi
if [[ -z "$base_date" ]]; then
echo "Error: Could not parse time '$input_time'"
return 1
fi
if command -v gdate >/dev/null 2>&1; then
epoch=$(gdate -d "$base_date" +%s 2>/dev/null)
elif date --version 2>&1 | grep -q GNU; then
epoch=$(date -d "$base_date" +%s 2>/dev/null)
else
epoch=$(date -j -f "%a %b %d %H:%M:%S %Z %Y" "$base_date" +%s 2>/dev/null || date +%s)
fi
fi
local local_date=$(date -r "$epoch" +%Y-%m-%d)
local eastern_date=$(TZ="America/New_York" date -r "$epoch" +%Y-%m-%d)
local utc_date=$(TZ="UTC" date -r "$epoch" +%Y-%m-%d)
local cet_date=$(TZ="Europe/Berlin" date -r "$epoch" +%Y-%m-%d)
local local_time=$(date -r "$epoch" "+$local_date %H:%M %Z")
local eastern_time=$(TZ="America/New_York" date -r "$epoch" "+%H:%M %Z")
[[ "$eastern_date" != "$local_date" ]] && eastern_time="$eastern_date $eastern_time"
local utc_time=$(TZ="UTC" date -r "$epoch" "+%H:%M %Z")
[[ "$utc_date" != "$local_date" ]] && utc_time="$utc_date $utc_time"
local cet_time=$(TZ="Europe/Berlin" date -r "$epoch" "+%H:%M %Z")
[[ "$cet_date" != "$local_date" ]] && cet_time="$cet_date $cet_time"
local output="Local: $local_time | Eastern: $eastern_time | UTC: $utc_time | CET: $cet_time"
if [[ -n "$custom_tz" ]]; then
local custom_date=$(TZ="$custom_tz" date -r "$epoch" +%Y-%m-%d 2>/dev/null)
if [[ $? -eq 0 ]]; then
local custom_time=$(TZ="$custom_tz" date -r "$epoch" "+%H:%M %Z")
[[ "$custom_date" != "$local_date" ]] && custom_time="$custom_date $custom_time"
# Extract just the city/region name from the timezone for cleaner labeling
local tz_label=$(echo "$custom_tz" | sed 's/.*\///')
output="$output | $tz_label: $custom_time"
else
echo "Error: Invalid timezone '$custom_tz'"
return 1
fi
fi
echo "$output"
}
```
**Usage examples:**
```bash
# With Asia/Tokyo
timeconv "2:30 PM" "Asia/Tokyo"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Tokyo: 2024-01-16 07:30 JST
# With Australia/Sydney
timeconv "2:30 PM" "Australia/Sydney"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Sydney: 2024-01-16 09:30 AEDT
# With Europe/London
timeconv "2:30 PM" "Europe/London"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | London: 22:30 GMT
# With America/Chicago
timeconv "2:30 PM" "America/Chicago"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Chicago: 16:30 CST
```
The function now extracts the city/region name from the timezone (the part after the last `/`) and uses that as the label. So:
- `Asia/Tokyo` becomes `Tokyo:`
- `Australia/Sydney` becomes `Sydney:`
- `Europe/London` becomes `London:`
- `America/Los_Angeles` becomes `Los_Angeles:`
This gives you cleaner, more readable output while still being informative about which timezone is being displayed.would it be possible to search the timezone database for matches if there is no exact match? Like, return Asia/Tokyo for Tokyo
Yes! Here's the updated function that searches the timezone database for partial matches:
```zsh
timeconv() {
local input_time="$1"
local custom_tz="$2"
local epoch base_date
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Usage: timeconv [TIME] [TIMEZONE] - Convert time to Local/Eastern/UTC/CET/Custom (e.g., timeconv '2:30 PM' Tokyo)"
return 0
fi
# Function to find timezone
find_timezone() {
local search_term="$1"
local tz_dirs=("/usr/share/zoneinfo" "/System/Library/TimeZones")
# First try exact match
for dir in $tz_dirs; do
[[ -d "$dir" ]] || continue
if TZ="$search_term" date >/dev/null 2>&1; then
echo "$search_term"
return 0
fi
done
# Then try partial match (case insensitive)
for dir in $tz_dirs; do
[[ -d "$dir" ]] || continue
local matches=($(find "$dir" -type f -path "*${search_term}*" 2>/dev/null | head -5))
for match in $matches; do
local tz_name=${match#$dir/}
if TZ="$tz_name" date >/dev/null 2>&1; then
echo "$tz_name"
return 0
fi
done
done
# Try case-insensitive search in common timezones
local -a common_timezones=(
"Asia/Tokyo" "Asia/Shanghai" "Asia/Hong_Kong" "Asia/Singapore" "Asia/Seoul"
"Europe/London" "Europe/Paris" "Europe/Berlin" "Europe/Rome" "Europe/Madrid"
"America/New_York" "America/Los_Angeles" "America/Chicago" "America/Denver"
"Australia/Sydney" "Australia/Melbourne" "Pacific/Auckland" "America/Toronto"
"Asia/Kolkata" "Asia/Dubai" "Asia/Bangkok" "Europe/Amsterdam" "Europe/Stockholm"
)
for tz in $common_timezones; do
if [[ "$tz" == *"${search_term}"* ]] || [[ "${tz:l}" == *"${search_term:l}"* ]]; then
echo "$tz"
return 0
fi
done
return 1
}
if [[ -z "$input_time" ]]; then
epoch=$(date +%s)
else
if [[ "$input_time" =~ "AM|PM|am|pm" ]]; then
base_date=$(date -j -f "%I:%M %p" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "$input_time" 2>/dev/null)
else
base_date=$(date -j -f "%H:%M" "$input_time" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -d "today $input_time" 2>/dev/null)
fi
if [[ -z "$base_date" ]]; then
echo "Error: Could not parse time '$input_time'"
return 1
fi
if command -v gdate >/dev/null 2>&1; then
epoch=$(gdate -d "$base_date" +%s 2>/dev/null)
elif date --version 2>&1 | grep -q GNU; then
epoch=$(date -d "$base_date" +%s 2>/dev/null)
else
epoch=$(date -j -f "%a %b %d %H:%M:%S %Z %Y" "$base_date" +%s 2>/dev/null || date +%s)
fi
fi
local local_date=$(date -r "$epoch" +%Y-%m-%d)
local eastern_date=$(TZ="America/New_York" date -r "$epoch" +%Y-%m-%d)
local utc_date=$(TZ="UTC" date -r "$epoch" +%Y-%m-%d)
local cet_date=$(TZ="Europe/Berlin" date -r "$epoch" +%Y-%m-%d)
local local_time=$(date -r "$epoch" "+$local_date %H:%M %Z")
local eastern_time=$(TZ="America/New_York" date -r "$epoch" "+%H:%M %Z")
[[ "$eastern_date" != "$local_date" ]] && eastern_time="$eastern_date $eastern_time"
local utc_time=$(TZ="UTC" date -r "$epoch" "+%H:%M %Z")
[[ "$utc_date" != "$local_date" ]] && utc_time="$utc_date $utc_time"
local cet_time=$(TZ="Europe/Berlin" date -r "$epoch" "+%H:%M %Z")
[[ "$cet_date" != "$local_date" ]] && cet_time="$cet_date $cet_time"
local output="Local: $local_time | Eastern: $eastern_time | UTC: $utc_time | CET: $cet_time"
if [[ -n "$custom_tz" ]]; then
local found_tz=$(find_timezone "$custom_tz")
if [[ -n "$found_tz" ]]; then
local custom_date=$(TZ="$found_tz" date -r "$epoch" +%Y-%m-%d 2>/dev/null)
if [[ $? -eq 0 ]]; then
local custom_time=$(TZ="$found_tz" date -r "$epoch" "+%H:%M %Z")
[[ "$custom_date" != "$local_date" ]] && custom_time="$custom_date $custom_time"
local tz_label=$(echo "$found_tz" | sed 's/.*\///')
[[ "$found_tz" != "$custom_tz" ]] && echo "Found timezone: $found_tz" >&2
output="$output | $tz_label: $custom_time"
fi
else
echo "Error: No timezone found matching '$custom_tz'"
return 1
fi
fi
echo "$output"
}
```
**Usage examples:**
```bash
# Exact timezone still works
timeconv "2:30 PM" "Asia/Tokyo"
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Tokyo: 2024-01-16 07:30 JST
# Now you can search by city name
timeconv "2:30 PM" "Tokyo"
Found timezone: Asia/Tokyo
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Tokyo: 2024-01-16 07:30 JST
# Other examples
timeconv "2:30 PM" "London"
Found timezone: Europe/London
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | London: 22:30 GMT
timeconv "2:30 PM" "Sydney"
Found timezone: Australia/Sydney
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Sydney: 2024-01-16 09:30 AEDT
# Partial matches work too
timeconv "2:30 PM" "angeles"
Found timezone: America/Los_Angeles
Local: 2024-01-15 14:30 PST | Eastern: 17:30 EST | UTC: 22:30 UTC | CET: 23:30 CET | Los_Angeles: 14:30 PST
```
The function now:
1. **First tries exact matches** (so `Asia/Tokyo` still works as before)
2. **Searches the system timezone database** for partial matches
3. **Falls back to a common timezone list** with case-insensitive matching
4. **Shows which timezone was found** when it's different from what you typed (printed to stderr so it doesn't interfere with the main output)
5. **Handles various city names** like Tokyo, London, Sydney, Shanghai, etc.
This makes it much more user-friendly - you can just type city names instead of remembering the full timezone paths!Can you think of a way to get this to parse days as well as times as input, i.e. '2025-08-29 18:00' or 'Tues 18:00' as well as 18:00
Yes! Here's the updated function that can parse dates along with times:
```zsh
timeconv() {
local input_time="$1"
local custom_tz="$2"
local epoch base_date
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "Usage: timeconv [TIME] [TIMEZONE] - Convert time to Local/Eastern/UTC/CET/Custom"
echo "Examples: timeconv '18:00' Tokyo, timeconv '2025-08-29 18:00', timeconv 'Tues 6pm'"
return 0
fi
# Function to find timezone
find_timezone() {
local search_term="$1"
local tz_dirs=("/usr/share/zoneinfo" "/System/Library/TimeZones")
# First try exact match
for dir in $tz_dirs; do
[[ -d "$dir" ]] || continue
if TZ="$search_term" date >/dev/null 2>&1; then
echo "$search_term"
return 0
fi
done
# Then try partial match (case insensitive)
for dir in $tz_dirs; do
[[ -d "$dir" ]] || continue
local matches=($(find "$dir" -type f -path "*${search_term}*" 2>/dev/null | head -5))
for match in $matches; do
local tz_name=${match#$dir/}
if TZ="$tz_name" date >/dev/null 2>&1; then
echo "$tz_name"
return 0
fi
done
done
# Try case-insensitive search in common timezones
local -a common_timezones=(
"Asia/Tokyo" "Asia/Shanghai" "Asia/Hong_Kong" "Asia/Singapore" "Asia/Seoul"
"Europe/London" "Europe/Paris" "Europe/Berlin" "Europe/Rome" "Europe/Madrid"
"America/New_York" "America/Los_Angeles" "America/Chicago" "America/Denver"
"Australia/Sydney" "Australia/Melbourne" "Pacific/Auckland" "America/Toronto"
"Asia/Kolkata" "Asia/Dubai" "Asia/Bangkok" "Europe/Amsterdam" "Europe/Stockholm"
)
for tz in $common_timezones; do
if [[ "$tz" == *"${search_term}"* ]] || [[ "${tz:l}" == *"${search_term:l}"* ]]; then
echo "$tz"
return 0
fi
done
return 1
}
if [[ -z "$input_time" ]]; then
epoch=$(date +%s)
else
# Try different parsing strategies
local parse_success=false
# Strategy 1: Try GNU/BSD date directly (handles many formats)
if command -v gdate >/dev/null 2>&1; then
epoch=$(gdate -d "$input_time" +%s 2>/dev/null)
[[ $? -eq 0 ]] && parse_success=true
elif date --version 2>&1 | grep -q GNU; then
epoch=$(date -d "$input_time" +%s 2>/dev/null)
[[ $? -eq 0 ]] && parse_success=true
fi
# Strategy 2: BSD date with various format attempts (macOS)
if [[ "$parse_success" == false ]]; then
# Try ISO format: 2025-08-29 18:00
if [[ "$input_time" =~ '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{1,2}:[0-9]{2}' ]]; then
epoch=$(date -j -f "%Y-%m-%d %H:%M" "$input_time" +%s 2>/dev/null)
[[ $? -eq 0 ]] && parse_success=true
fi
# Try ISO format with AM/PM: 2025-08-29 6:00 PM
if [[ "$parse_success" == false ]] && [[ "$input_time" =~ 'AM|PM|am|pm' ]]; then
epoch=$(date -j -f "%Y-%m-%d %I:%M %p" "$input_time" +%s 2>/dev/null)
[[ $? -eq 0 ]] && parse_success=true
fi
# Try weekday + time formats (Mon 18:00, Tuesday 6pm, etc.)
if [[ "$parse_success" == false ]] && [[ "$input_time" =~ '(Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)' ]]; then
# For macOS, we need to be more creative with weekday parsing
local today_epoch=$(date +%s)
local today_dow=$(date -r $today_epoch +%u) # 1=Monday, 7=Sunday
# Extract day and time parts
local day_part=$(echo "$input_time" | grep -o -E '(Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)' | head -1)
local time_part=$(echo "$input_time" | sed -E 's/(Mon|Tue|Wed|Thu|Fri|Sat|Sun|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)[[:space:]]*//i')
if [[ -n "$day_part" ]] && [[ -n "$time_part" ]]; then
# Convert day name to number (1=Monday, 7=Sunday)
local target_dow
case "${day_part:l}" in
mon|monday) target_dow=1 ;;
tue|tuesday) target_dow=2 ;;
wed|wednesday) target_dow=3 ;;
thu|thursday) target_dow=4 ;;
fri|friday) target_dow=5 ;;
sat|saturday) target_dow=6 ;;
sun|sunday) target_dow=7 ;;
esac
# Calculate days until target day (0 = today, positive = future, negative would be past week)
local days_offset=$(( (target_dow - today_dow + 7) % 7 ))
# Get the target date
local target_date=$(date -j -v+${days_offset}d +%Y-%m-%d)
# Parse the time part
if [[ "$time_part" =~ 'AM|PM|am|pm' ]]; then
epoch=$(date -j -f "%Y-%m-%d %I:%M %p" "$target_date $time_part" +%s 2>/dev/null)
else
epoch=$(date -j -f "%Y-%m-%d %H:%M" "$target_date $time_part" +%s 2>/dev/null)
fi
[[ $? -eq 0 ]] && parse_success=true
fi
fi
# Try just time formats (fallback to today)
if [[ "$parse_success" == false ]]; then
if [[ "$input_time" =~ 'AM|PM|am|pm' ]]; then
epoch=$(date -j -f "%I:%M %p" "$input_time" +%s 2>/dev/null)
else
epoch=$(date -j -f "%H:%M" "$input_time" +%s 2>/dev/null)
fi
[[ $? -eq 0 ]] && parse_success=true
fi
fi
if [[ "$parse_success" == false ]]; then
echo "Error: Could not parse time '$input_time'"
echo "Try formats like: '18:00', '2025-08-29 18:00', 'Tues 6:00 PM', 'Friday 14:30'"
return 1
fi
fi
local local_date=$(date -r "$epoch" +%Y-%m-%d)
local eastern_date=$(TZ="America/New_York" date -r "$epoch" +%Y-%m-%d)
local utc_date=$(TZ="UTC" date -r "$epoch" +%Y-%m-%d)
local cet_date=$(TZ="Europe/Berlin" date -r "$epoch" +%Y-%m-%d)
local local_time=$(date -r "$epoch" "+$local_date %H:%M %Z")
local eastern_time=$(TZ="America/New_York" date -r "$epoch" "+%H:%M %Z")
[[ "$eastern_date" != "$local_date" ]] && eastern_time="$eastern_date $eastern_time"
local utc_time=$(TZ="UTC" date -r "$epoch" "+%H:%M %Z")
[[ "$utc_date" != "$local_date" ]] && utc_time="$utc_date $utc_time"
local cet_time=$(TZ="Europe/Berlin" date -r "$epoch" "+%H:%M %Z")
[[ "$cet_date" != "$local_date" ]] && cet_time="$cet_date $cet_time"
local output="Local: $local_time | Eastern: $eastern_time | UTC: $utc_time | CET: $cet_time"
if [[ -n "$custom_tz" ]]; then
local found_tz=$(find_timezone "$custom_tz")
if [[ -n "$found_tz" ]]; then
local custom_date=$(TZ="$found_tz" date -r "$epoch" +%Y-%m-%d 2>/dev/null)
if [[ $? -eq 0 ]]; then
local custom_time=$(TZ="$found_tz" date -r "$epoch" "+%H:%M %Z")
[[ "$custom_date" != "$local_date" ]] && custom_time="$custom_date $custom_time"
local tz_label=$(echo "$found_tz" | sed 's/.*\///')
[[ "$found_tz" != "$custom_tz" ]] && echo "Found timezone: $found_tz" >&2
output="$output | $tz_label: $custom_time"
fi
else
echo "Error: No timezone found matching '$custom_tz'"
return 1
fi
fi
echo "$output"
}
```
**Usage examples:**
```bash
# Just time (uses today)
timeconv "18:00"
Local: 2024-01-15 18:00 PST | Eastern: 21:00 EST | UTC: 2024-01-16 02:00 UTC | CET: 2024-01-16 03:00 CET
# Full date and time
timeconv "2025-08-29 18:00"
Local: 2025-08-29 18:00 PDT | Eastern: 21:00 EDT | UTC: 2025-08-30 01:00 UTC | CET: 2025-08-30 03:00 CEST
# Weekday formats
timeconv "Tues 18:00"
Local: 2024-01-16 18:00 PST | Eastern: 21:00 EST | UTC: 2024-01-17 02:00 UTC | CET: 2024-01-17 03:00 CET
timeconv "Friday 6:00 PM" Tokyo
Found timezone: Asia/Tokyo
Local: 2024-01-19 18:00 PST | Eastern: 21:00 EST | UTC: 2024-01-20 02:00 UTC | CET: 2024-01-20 03:00 CET | Tokyo: 2024-01-20 11:00 JST
# Various formats work
timeconv "Monday 14:30"
timeconv "Wed 2:15 PM"
timeconv "2025-12-25 09:00"
```
The function now handles:
1. **Just times**: `18:00`, `6:00 PM` (uses today's date)
2. **Full ISO dates**: `2025-08-29 18:00`, `2025-08-29 6:00 PM`
3. **Weekday formats**: `Tues 18:00`, `Friday 6:00 PM`, `Monday 14:30`
4. **Various day abbreviations**: Mon, Tue, Wed, Thu, Fri, Sat, Sun and full names
5. **Mixed AM/PM and 24-hour formats** with any of the above
The weekday parsing finds the next occurrence of that day (or today if it's the same weekday), making it very convenient for scheduling and meeting times!