#!/bin/sh
#
#  tzdiff - displays timezone differences with localtime.
#
#  Copyright (c) 2016 - 2018 Masato Minda
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#  1. Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#  SUCH DAMAGE.
#
#  $Id: tzdiff,v 1.51 2019/05/15 02:49:35 minmin Exp $

format='%F %R %Z'
max=10

__check_match () {
	if [ `echo ${1} | wc -w` -eq 1 ]; then
		return 0
	fi
	ls -dF ${1}
	exit 1
}

__set_zoneinfo () {

	case "${1}" in
	*\**)
		__check_match "${1}*"
		zi=`echo ${1}*`
		;;

	*)	zi=${1}
		;;
	esac

	while [ ! -f ${zi} ]
	do
		if [ -d ${zi} ]; then
			ls -dF ${zi}/*
			exit 1
		fi

		__check_match "${zi}*"
		zi=`echo ${zi}*`
		if [ ! -e ${zi} ]; then
			__check_match "*/${zi}"
			zi=`echo */${zi}`
			if [ ! -e ${zi} ]; then
				__check_match "*/${zi}"
				zi=`echo */${zi}`
				if [ ! -e ${zi} ]; then
					ls -F
					exit 2
				fi
			fi
		fi
	done
}

__make_column () {
	if [ ${H_flag} -ne 0 ]; then
		echo "%s"
		return
	fi

	local _ltz="${1}"
	local _ltd="$(__date_with_format ${ut} ${_ltz})"

	if [ ${#_ltz} -gt ${#_ltd} ]; then
		echo "%-${#_ltz}s"
	else
		echo "%-${#_ltd}s"
	fi
}

__usage () {

	cat <<- EOT >&2
	Usage: ${0##*/} [-0] [-n count] [-f format] [-t time] timezone [timezone ...] [count] [0]
	    -0:  round down to hour
	    -n:  max hours (default: 10)
	    -f:  output format (using '+output_fmt' of 'date' command)
	    -t:  start time (YYYY-mm-ddThh:mm or YYYYmmddThhmm)
	    -H:  scripting mode (with timezone name)
	    -HH: scripting mode (without timezone name)
	    -N:  without localtime
	EOT
	exit 2
}

if date --version >/dev/null 2>&1; then
	# for GNU date
	__date_with_format () {
		if [ $# -eq 2 ]; then
			TZ=:${2} date --date=@${1} +"${format}"
		else
			date --date=@${1} +"${format}"
		fi
	}
	__date_parse () {
		date --date="${1}" +%s
	}
else
	# for BSD date
	__date_with_format () {
		if [ $# -eq 2 ]; then
			TZ=:${2} date -j -r ${1} +"${format}"
		else
			date -j -r ${1} +"${format}"
		fi
	}

	case `uname -s` in

	FreeBSD|Darwin)
		__date_parse () {
			date -j -f %Y%m%dT%H%M $(echo ${1}| tr -d :-) +%s
		}
		;;
	*)
		__date_parse () {		# NetBSD / OpenBSD
			date -j +%s $(echo ${1} | tr -d 'T:-')
		}
		;;
	esac
fi

cd /usr/share/zoneinfo || exit

H_flag=0

while getopts '0hHNn:f:t:' cmd_arg
do
	case "${cmd_arg}" in
	0)	zero_flag=YES ;;
	n)	max=${OPTARG} ;;
	f)	format="${OPTARG}" ;;
	t)	nt=${OPTARG} ;;
	H)	H_flag=$((H_flag + 1)) ;;
	N)	N_flag=YES ;;
	h|*)	__usage ;;
	esac
done >&2

shift $((OPTIND - 1))

fs='   '				# 3 spaces
[ ${H_flag} -ne 0 ] && fs='	'	# TAB

if [ $# -eq 0 ]; then
	ls -F
	exit
fi

id=0

while [ $# -gt 0 ]
do
	case $1 in

	[0-9]*)
		if [ ${1} -eq 0 ]; then
			zero_flag=YES
		else
			max=${1}
		fi
		;;
	*)
		__set_zoneinfo "${1}"
		eval tz${id}=${zi}
		id=$((id + 1))
		;;
	esac
	shift
done

if [ ${id} -eq 0 ]; then
	__usage
fi

case "x${nt}" in

x[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9] | x[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9])
	ut=`__date_parse "${nt}"` || exit ;;
x)
	ut=`date +%s`;;
*)
	__usage;;
esac

if [ x${zero_flag} = x"YES" ]; then
	ut=$(( ( ${ut} / 3600 ) * 3600 ))	# round down
fi

# make format
i=0
while [ ${i} -lt ${id} ]; do
	eval tz=\$tz${i}
	eval fmt${i}=$(__make_column ${tz})
	i=$((i + 1))
done

# print timezone name
if [ ${H_flag} -lt 2 ]; then
	i=0
	while [ ${i} -lt ${id} ]; do
		eval tz=\$tz${i}
		eval fmt=\$fmt${i}
		i=$((i + 1))
		if [ ${i} -eq ${id} ]; then
			printf "%s\n" "${tz}"
		else
			printf "${fmt}${fs}" "${tz}"
		fi
	done
fi

# print time
j=0
while [ ${j} -lt ${max} ]
do
	i=0
	while [ ${i} -lt ${id} ]; do
		eval tz=\$tz${i}
		eval fmt=\$fmt${i}
		printf "${fmt}" "$(__date_with_format ${ut} ${tz})"
		i=$((i + 1))
		[ ! -z "${N_flag}" -a ${i} -eq ${id} ] || printf "${fs}"
	done
	if [ -z "${N_flag}" ]; then
		__date_with_format ${ut}
	else
		echo
	fi
	ut=$((ut + 3600))
	j=$((j + 1))
done
