Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Commit

Permalink
Add timezone subcommand (#1089)
Browse files Browse the repository at this point in the history
* Add timezone subcommand

* Fix timezone info so it support ISO timezones correctly

* Update timestamp.cpp

* Update gridlabd-timezone

* Update gridlabd-timezone

* Create test_timezone.glm

* Update test_timezone.glm

* Added support for timezome format options

Co-authored-by: Alyona Teyber <[email protected]>
  • Loading branch information
David P. Chassin and aivanova5 authored Feb 15, 2022
1 parent 2684a1f commit a595ba3
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 18 deletions.
1 change: 1 addition & 0 deletions docs/GLM/Directive/Clock.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ Introducing more than one clock directive in a model is allowed. However, all el
# See also

* [share/tzinfo.txt](https://github.com/slacgismo/gridlabd/blob/master/gldcore/tzinfo.txt)
* [[/Subcommand/Timezone]]
36 changes: 36 additions & 0 deletions docs/Subcommand/Timezone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[[/Subommand/Timezone]] -- Subcommand to obtain the timezone at a location

# Synopsis

~~~
bash$ gridlabd timezone [LATITUDE,LONGITUDE]
~~~

# Description

The gridlabd timezone subcommand obtains the timezone for the location given by
LATITUDE,LONGITUDE or the local timezone based on the hostname's IP address.

## Options

* `-d|--debug`: output debugging output on error
* `-h|--help|help`: print full documentation
* `-v|--verbose`: output intermediate results

# Examples

The following command obtains the timezone for the current location as determine by the IP address of the host machine:

~~~
shell$ gridlabd timezone
~~~

The following command obtains the timezone for the location specified:

~~~
shell$ gridlabd timezone 37.5,-122.2
~~~

# See also

* [[/GLM/Directive/Clock]]
1 change: 1 addition & 0 deletions gldcore/scripts/Makefile.mk
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ bin_SCRIPTS += gldcore/scripts/gridlabd-python
bin_SCRIPTS += gldcore/scripts/gridlabd-require
bin_SCRIPTS += gldcore/scripts/gridlabd-requirements
bin_SCRIPTS += gldcore/scripts/gridlabd-template
bin_SCRIPTS += gldcore/scripts/gridlabd-timezone
bin_SCRIPTS += gldcore/scripts/gridlabd-trace
bin_SCRIPTS += gldcore/scripts/gridlabd-validate
bin_SCRIPTS += gldcore/scripts/gridlabd-version
Expand Down
14 changes: 14 additions & 0 deletions gldcore/scripts/autotest/test_timezone.glm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
clock
{
timezone PST+8PDT;
timezone "US/CA/Los Angeles";
timezone ${SHELL gridlabd timezone local};
}

#assert $(gridlabd timezone 37.5,-122.2) == "America/Los_Angeles"
#assert $(gridlabd timezone 33,-110) == "America/Phoenix"
#assert $(gridlabd timezone 36,-110) == "America/Denver"
#assert $(gridlabd timezone 40,-100) == "America/Chicago"
#assert $(gridlabd timezone 40,-80) == "America/New_York"
#assert $(gridlabd timezone 50,-120) == "America/Vancouver"
#assert $(gridlabd timezone 37.5,-122.2 -f=TZSPEC) == "PST+8PDT"
154 changes: 154 additions & 0 deletions gldcore/scripts/gridlabd-timezone
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/local/bin/python3
"""Syntax: gridlabd timezone [OPTIONS...] LOCATION
The gridlabd timezone subcommand obtains the timezone for the location given
by LOCATION, which may be in the form `LATITUDE,LONGITUDE`, `local`, or an IP
address of the form `A.B.C.D`.
OPTIONS:
-d|--debug output debugging output on error
-h|--help|help print full documentation
-v|--verbose output intermediate results
-f|--format[=FORMAT] change timezone format
FORMATS
TZINFO Timezone database standard (default, e.g., "America/Los_Angeles")
TZSPEC Timezone abbreviation standard (e.g., "PST+8PDT")
EXAMPLES:
The following command obtains the timezone for the current location as
determine by the IP address of the host machine:
shell$ gridlabd timezone local
America/Los_Angeles
The following command obtains the timezone for the location specified:
shell$ gridlabd timezone 37.5,-122.2
America/Los_Angeles
The following GLM directive sets the model timezone to the local timezone:
clock
{
timezone ${SHELL gridlabd timezone local};
}
"""

import sys, os, subprocess

verbose = False
debug = False
location = None
tz_format = 'TZINFO'

E_OK = 0
E_SYNTAX = 1
E_INVALID = 2
E_FAILED = 3

shared = os.getenv("GLD_ETC")
if not shared:
shared = "/usr/local/share/gridlabd/"
elif shared[-1] != '/':
shared += '/'

def output_verbose(msg):
if verbose:
print(f"VERBOSE [{os.path.basename(sys.argv[0])}]: {msg}",file=sys.stderr)

def output_error(msg,code=None):
print(f"ERROR [{os.path.basename(sys.argv[0])}]: {msg}",file=sys.stderr)
if type(code) is int:
exit(code)

def output_help():
print(__doc__)
exit(E_OK)

def output_syntax():
print(__doc__.split("\n")[0])
exit(E_SYNTAX)

def timezone_format(tz_info,tz_format=tz_format):

if tz_format == 'TZINFO':
return tz_info
elif tz_format == 'TZSPEC':
tzinfo_file = shared+"tzinfo.txt"
with open(tzinfo_file,"r") as f:
rule = None
found = False
for line in f.readlines():
if not line or line[0] == ';' or line[0] == '\n':
continue
elif line[0] != ' ':
rule = line
elif line.strip() == tz_info:
found = True
break
if found:
return rule.split(";")[0].split(",")[0]
else:
output_error(f"{tz_info} not found in {tzinfo_file}",E_FAILED)
else:
output_error(f"timezone format '{tz_format}' is not valid",E_INVALID)

try:
from timezonefinder import TimezoneFinder
import geocoder as gc

if len(sys.argv) == 1:
output_syntax()

while len(sys.argv) > 1:
args = sys.argv[1].split("=")
if len(args) == 1:
arg = args[0]
value = None
else:
arg = args[0]
value = '='.join(args[1:])
if arg in ["-d","--debug"]:
debug = True
elif arg in ["-v","--verbose"]:
verbose = True
elif arg in ["-h","--help","help"]:
output_help()
elif arg in ["-f","--format"]:
if value:
tz_format = value
elif arg[0] == '-':
output_error(f"option '{sys.argv[1]}' is not valid",E_SYNTAX)
else:
location = sys.argv[1]
del sys.argv[1]

if not location:
output_error("no location specified",E_SYNTAX)
elif location == 'local':
latlon = gc.ip('me').latlng
elif location.find(",") > 0:
latlon = list(map(lambda x:float(x),location.split(",")))
else:
latlon = gc.ip(location).latlng
if not type(latlon) is list or len(latlon) != 2 \
or type(latlon[0]) != float or type(latlon[1]) != float:
output_error("latlon format is not valid",E_INVALID)
output_verbose(f"latlon = {latlon}")

tz_info = TimezoneFinder().timezone_at(lat=latlon[0],lng=latlon[1])
print(timezone_format(tz_info,tz_format),file=sys.stdout)

exit(E_OK)

except Exception as err:

if debug:
raise
else:
output_error(str(err),E_FAILED)
2 changes: 2 additions & 0 deletions gldcore/scripts/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ geopandas==0.9.0
PyGithub==1.54.1
matplotlib==3.3.3
geopy==2.1.0
timezonefinder==5.2.0
geocoder==1.38.1
35 changes: 21 additions & 14 deletions gldcore/timestamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -737,25 +737,30 @@ int tz_info(const char *tzspec, char *tzname, char *std, char *dst, time_t *offs
}
}

const char *tz_locale(const char *country, const char *province, const char *city)
const char *tz_locale(const char *target)
{
extern const char *tz_name(const char *tzspec);
static char tzname[256]="";
char filepath[1024];
FILE *fp = NULL;
char buffer[1024];
char target[256];
int len = sprintf(target,"%s/%s/%s",country,province,city);
strcpy(tzname,target);
int len = strlen(target);
while ( isspace(target[len-1]) )
{
len--;
}

if ( find_file(TZFILE, NULL, R_OK,filepath,sizeof(filepath)) == NULL ) {
if ( find_file(TZFILE, NULL, R_OK,filepath,sizeof(filepath)) == NULL )
{
throw_exception("timezone specification file %s not found in GLPATH=%s: %s", TZFILE, getenv("GLPATH"), strerror(errno));
/* TROUBLESHOOT
The system could not locate the timezone file <code>tzinfo.txt</code>.
Check that the <code>etc</code> folder is included in the '''GLPATH''' environment variable and try again.
*/
}
fp = fopen(filepath,"r");
if ( fp == NULL ) {
if ( fp == NULL )
{
throw_exception("%s: access denied: %s", filepath, strerror(errno));
/* TROUBLESHOOT
The system was unable to read the timezone file. Check that the file has the correct permissions and try again.
Expand All @@ -771,13 +776,13 @@ const char *tz_locale(const char *country, const char *province, const char *cit
if ( strnicmp(locale,target,len)==0 )
{
fclose(fp);
return tz_name(tzname);
return tzname;
}
}
else
sscanf(buffer, "%[^,]", tzname);
}
throw_exception("tz_locale(char *country='%s', char *province='%s', char *city='%s'): not tzinfo entry found", country, province, city);
fclose(fp);
return NULL;
}

Expand All @@ -787,12 +792,14 @@ const char *tz_locale(const char *country, const char *province, const char *cit
const char *tz_name(const char *tzspec)
{
static char name[32] = "GMT";
char country[64], province[64], city[64];

if ( sscanf(tzspec,"%[^/]/%[^/]/%[^/]",country,province,city)==3 )
return tz_locale(country,province,city);

if ( tz_info(tzspec, name, NULL, NULL, NULL))

const char *result = tz_locale(tzspec);
if ( result )
{
tzspec = result;
}

if ( tz_info(tzspec, name, NULL, NULL, NULL) )
{
return name;
}
Expand Down
9 changes: 5 additions & 4 deletions gldcore/tzinfo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,15 @@ EST+5EDT,M4.5.0/02:00,M10.5.0/02:00 ; Eastern, DST 2am last Sun/Apr to last Sun/
America/Washington
America/Atlanta
America/Boston
America/New York
America/New_York
America/Baltimore
CST+6CDT,M4.5.0/02:00,M10.5.0/02:00 ; Central, DST 2am last Sun/Apr to last Sun/Oct
US/IL/Chicago
America/Chicago
US/LA/New Orleans
America/New Orleans
America/New_Orleans
US/MO/St Louis
America/St Louis
America/St_Louis
MST+7MDT,M4.5.0/02:00,M10.5.0/02:00 ; Mountain, DST 2am last Sun/Apr to last Sun/Oct
CA/AB/Calgary
US/CO/Denver
Expand All @@ -151,8 +151,9 @@ PST+8PDT,M4.5.0/02:00,M10.5.0/02:00 ; Pacific, DST 2am last Sun/Apr to last Sun/
US/OR/Portland
US/WA/Seattle
America/Los_Angeles
America/San Francisco
America/San_Francisco
America/Seattle
America/Vancouver
AST+9ADT,M4.5.0/02:00,M10.5.0/02:00 ; Alaska DST 2am last Sun/Apr to last Sun/Oct
US/AK/Anchorage
America/Anchorage
Expand Down

0 comments on commit a595ba3

Please sign in to comment.