Thank you Mike and Andrew for the swift responses. Mik'e neat trick of setting the TZ environment variable in an external program works well for my needs
(and on windows even!), but Andrew's suggestion to use java did not work because my Octave does not find java, even though it is installed. I have Changed Mike's function slightly (see below) to make it equivalent to the localtime() function, i.e. it produces a time structure compatible with localtime apart from exceptions noted in the help text.
Cheers... Ian
%% -*- texinfo -*-
%% @deftypefn {} {} localtime2 (@var{Seconds}, @var{TimeZone})
%% Given a time value specified in Seconds Since Epoch (1/1/1900), and a timezone specified
%% return a time structure corresponding to the local time zone of the specified timezone.
%% When called with an empty string for the timezone produces the same results as localtime.
%% Example:
%%
%% @example
%% @group
%% localtime2(lstat('filename').mtime, 'CST6CDT,M3.2.0/02:00:00,M11.1.0') ;
%% => {
%% usec = 0
%% sec = 32
%% min = 29
%% hour = 6
%% mday = 4
%% mon = 0
%% year = 119
%% wday = 5
%% yday = 3
%% isdst = 0
%% gmtoff = 0
%% zone = EST
%% }
%% @end group
%% @end example
%% @noindent
%% will return a time structure corresponding to the time
%% (in the specified local time zone) of the time the file was modified.
%%
%% NOTE. Does not currently implement the gmtoff or isdst fields of the time structure.
%% Does not support fractional seconds.
%% @seealso{localtime}
%% @end deftypefn
%% Author: IMM
function lt = localtime2 (seconds, tz)
cmd = sprintf ("env TZ='%s' date address@hidden +'%s'", tz, seconds, "%N %S %M %H %d %m %Y %w %j %Z");
[status, output] = system (cmd);
values = str2double (strsplit (output));
lt.usec = values(1)*1000;
lt.sec = values(2);
lt.min = values(3);
lt.hour = values(4);
lt.mday = values(5);
lt.mon = values(6)-1;
lt.year = values(7)-1900;
lt.wday = values(8);
lt.yday = values(9)-1;
lt.isdst = NaN;
lt.gmtoff = NaN;
lt.zone = strsplit(output){10};
endfunction