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 functionality
okay, 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!