#!/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.47 2018/12/20 01:37:37 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 [ ! -z "${H_flag}" ]; 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 () {
echo "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.
	more info: <https://github.com/belgianbeer/tzdiff>" >&2

	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
			local _tmp _ret
			_tmp=`mktemp /tmp/.tzdiffXXXXXXXXXX`
			trap "rm -f ${_tmp}" 0 1 2 3 5 15
			touch -t `echo ${1} | tr -d 'T:-'` ${_tmp} || exit
			_ret=`stat -f %Sm -t %s ${_tmp}`
			rm -f ${_tmp}
			echo ${_ret}
		}
		;;
	esac
fi

cd /usr/share/zoneinfo || exit

while getopts '0hHn: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=YES ;;
	h|*)	__usage ;;
	esac
done >&2

shift $((OPTIND - 1))

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 ))
fi

i=0
while [ ${i} -lt ${id} ]
do
	eval tz=\$tz${i}
	eval fmt${i}=$(__make_column ${tz})
	eval fmt=\$fmt${i}
	[ -z "${H_flag}" ] && printf "${fmt}\t" "${tz}"
	i=$((i + 1))
done
[ -z "${H_flag}" ] && echo ''

j=0
while [ ${j} -lt ${max} ]
do
	i=0
	while [ ${i} -lt ${id} ]
	do
		eval tz=\$tz${i}
		eval fmt=\$fmt${i}
		printf "${fmt}\t" "$(__date_with_format ${ut} ${tz})"
		i=$((i + 1))
	done
	__date_with_format ${ut}
	ut=$((ut + 3600))
	j=$((j + 1))
done
