"""Common timezone names (civilian, military and combined)

These productions are a collection of common civilian and
military timezone names.  The list of names is by no means
exhaustive (nor definitive), but it gives most timezones
at least one named value (to make it possible to enter the
name), and it doesn't repeat any names (I hope ;) ).  You
have three major classes of names, civilian (EST, PST, GMT,
UTC), military single-character (A,B,C,D,E...) and military
phonetic spelling (Alpha, Bravo... Zulu).  The military
variants are combined into a single production, however.

	civilian_timezone_name -- the "familiar" timezones, most
		real-world data entry will want to use this as their
		"timezone" definition I'm guessing.
		
	military_timezone_name -- military timezones in the two
		formats outlined above.
		
	timezone_name -- combination of the two above into a
		single production.

Interpreter:

	TimeZoneNameInterpreter -- see below for details, by
		default takes the timezone name and converts to
		a second offset in West-negative format.  Note:
		this is the _opposite_ of the time module, but is
		the more commonly used format AFAIK.  Null matches
		will return a default TimeZone as specified.
"""
from simpleparse import objectgenerator, common
from simpleparse.common import phonetics
import time

c = {}

timezone_data = []
civilian_data = [
	# Basically this defines our recognised input locales,
	# it is by no means exhaustive, but it gives fairly
	# good coverage with minimal overlap
	('NZDT',46800),
	('IDLE',43200),
	('NZST',43200),
	('NZT',43200),
	('AESST',39600),
	('ACSST',37800),
	('CADT',37800),
	('SADT',37800),
	('AEST',36000),
	('EAST',36000),
	('GST',36000),
	('LIGT',36000),
	('ACST',34200),
	('CAST',34200),
	('SAT',34200),
	('AWSST',32400),
	('JST',32400),
	('KST',32400),
	('WDT',32400),
	('MT',30600),
	('AWST',28800),
	('CCT',28800),
	('WADT',28800),
	('WST',28800),
	('JT',27000),
	('WAST',25200),
	('IT',12600),
	('BT',10800),
	('EETDST',10800),
	('MSK', 10800),
	('CETDST',7200),
	('EET',7200),
	('FWT',7200),
	('IST',7200),
	('MEST',7200),
	('METDST',7200),
	('SST',7200),
	('BST',3600),
	('CET',3600),
	('DNT',3600),
	('DST',3600),
	('FST',3600),
	('MET',3600),
	('MEWT',3600),
	('MEZ',3600),
	('NOR',3600),
	('SET',3600),
	('SWT',3600),
	('WETDST',3600),
	('GMT',0),
	('UTC', 0),
	('WET',0),
	('WAT',-3600),
	('NDT',-5400),
	('AT', -7200),
	('ADT',-10800),
	('NFT',-9000),
	('NST',-9000),
	('AST',-14400),
	('EDT',-14400),
	('ZP4',-14400),
	('CDT',-18000),
	('EST',-18000),
	('ZP5',-18000),
	('CST',-21600),
	('MDT',-21600),
	('ZP6',-21600),
	('MST',-25200),
	('PDT',-25200),
	('PST',-28800),
	('YDT',-28800),
	('HDT',-32400),
	('YST',-32400),
	('AKST',-32400),
	
	('AHST',-36000),
	('HST',-36000),
	('CAT',-36000),
	('NT',-39600),
	('IDLW',-43200),
]
timezone_data = timezone_data + civilian_data
### add military timezones
##A-I then K-Z are used...
## z = 0
## a - i, k-m -> + values up to 12
## n-y - values up to -12
## what a totally messed up system!
## I've checked with a number of sites, they all seem to think
## it works this way... darned if I can figure out why they don't
## make N -12, o -11 etceteras so that z would come in order and you'd
## have a simple progression around the globe... sigh.
zulu_data = [
	('A', 3600), ('B', 7200), ('C', 10800), ('D', 14400), ('E', 18000),
	('F', 21600), ('G', 25200), ('H', 28800), ('I', 32400), ('K', 36000),
	('L', 39600), ('M', 43200),
	('N', -3600), ('O', -7200), ('P', -10800), ('Q', -14400), ('R', -18000),
	('S', -21600), ('T', -25200), ('U', -28800), ('V', -32400), ('W', -36000),
	('X', -39600), ('Y', -43200),
	('Z', 0),
]	
# now add these, plus the expanded versions to the dict above...
# note that we only allow capitalised versions of the military
# zones!
tztemp = []
for key, value in zulu_data:
	for item in phonetics._letters:
		if item[0] == key:
			tztemp.append( (item, value) )
# order is important here, want longer first
zulu_data = tztemp + zulu_data
del tztemp
# and call that done for now, folks...
timezone_data = timezone_data + zulu_data
# the rules are really big, but oh well...
def _build( data ):
	"""Build the name:time map and match rule for each dataset"""
	data = data[:]
	data.sort() # get shortest and least values first forcefully...
	# then reverse that, to get longest first...
	data.reverse()
	names = []
	mapping = {}
	for key,value in data:
		names.append( objectgenerator.Literal(value=key))
		mapping[key] = value
	rule = objectgenerator.FirstOfGroup(
		children = names
	)
	return mapping, rule
zulu_mapping, zulu_rule          = _build( zulu_data )
civilian_mapping, civilian_rule  = _build( civilian_data )
timezone_mapping, timezone_rule  = _build( timezone_data )

c[ "military_timezone_name" ] = zulu_rule
c[ "civilian_timezone_name" ] = civilian_rule
c[ "timezone_name" ] = timezone_rule

common.share(c)

import time
if time.daylight:
	LOCAL_ZONE = time.altzone
else:
	LOCAL_ZONE = time.timezone
# account for time module's different counting procedure...
LOCAL_ZONE = -LOCAL_ZONE

class TimeZoneNameInterpreter:
	"""Intepret a timezone specified as a military or civilian timezone name

	Return value is an offset from UTC given in seconds.
	If a null-match is passed uses the passed defaultZone.
	Returns values in seconds difference from UTC (negative
	West) divided by the passed "seconds" argument.
	"""
	def __init__( self, defaultZone=LOCAL_ZONE, seconds=1.0):
		"""
		defaultZone -- ofset in seconds to be returned if there
			is no value specified (null-match)
		seconds -- divisor applied to the value before returning,
			if you want hours, use 3600.0, if you want minutes,
			use 60.0, if you want days (why?), use 86400.0
		"""
		self.defaultZone = defaultZone
		self.seconds = seconds
	def __call__( self, (tag, left, right, children), buffer ):
		value = buffer[ left: right ]
		if value:
			try:
				return timezone_mapping[ value ]/self.seconds
			except KeyError:
				raise ValueError( "Unrecognised (but parsed!) TimeZone Name %s found at character position %s"%(value, left))
		else:
			return self.defaultZone/self.seconds



syntax highlighted by Code2HTML, v. 0.9.1