Return JES2 job timestamps as ISO 8601 UTC - #151
Merged
Conversation
The JSON carried two timestamps per job that came from different sources and
disagreed. start_stamp is the epoch httpjes2.c computes explicitly:
tzadjust = httpd->tzoffset * -1; /* convert local time to GMT */
start_time = j->start_time + tzadjust;
which is correct now that tzoffset defaults to the system offset (#150).
start_display then ran that same instant through ctime64(), which converts
through crt->crttzoff -- the *task's* timezone, taken from CVTTZ -- shifting
it a second time by an unrelated offset. On the reference system a job that
started at 17:25:18 UTC was reported as "Fri Aug 7 05:25:18 2026": neither
UTC, nor the system's local time, nor the caller's.
Beyond the double shift, a local time is the wrong thing for an API to
return. The server cannot know the caller's timezone, so any local rendering
is unlabelled and the client cannot tell which zone it got. Returning an
unambiguous instant and letting the client localize is what zowe and every
browser already do.
That is also the convention mvsMF settled on elsewhere: ussapi.c formats
mtime with mgmtime64() and a literal "Z", and docs/endpoints/uss/list.md and
console/hardcopy-log.md both specify ISO 8601 UTC. Those timestamps never had
this class of bug because they never touch localtime.
So both display fields now render through gmtime64_r() as
"2026-08-07T17:25:18Z", via one shared jestime() rather than two open-coded
ctime64() calls that had already drifted apart. A zero timestamp still yields
"...", as before.
start_stamp is unchanged -- it is the machine-readable epoch and is correct.
Note for the reader: /jes/status is served by httpjes2.c, not jesst.c. Both
define main(), so only httpjes2.o can be in the HTTPJES2 module and jesst.o is
never autocalled; jesst.c looks like dead code. It is changed here anyway so
the two do not drift again if it is ever wired up, but it is a candidate for
deletion alongside the rest of the deprecated JES CGI.
Refs #145
Real z/OSMF emits the exec-* fields as
"exec-started":"2018-11-03T09:05:18.010Z"
so jestime() now renders ".000Z" rather than "Z". JES2 gives second
resolution here (start_time64 is a time64_t), so the fraction is always zero
-- which is what z/OSMF itself reports for exec-submitted anyway.
httpjes2 is the reference mvsMF's own implementation gets compared against,
so the two formats should be identical rather than merely similar.
The "Z" on these fields is a promise, and TZOFFSET could break it. The epoch
was computed as
tzadjust = httpd->tzoffset * -1;
so an operator who set TZOFFSET to their own zone rather than the machine's
skewed every timestamp the API reports. On the reference system, TZOFFSET
+02:00 against a CVTTZ of -5h put a job that ran at 17:25:18 UTC at
10:25:18 UTC -- seven hours out, and labelled Z.
Converting JES2's stored local timestamps to UTC needs the offset the machine
actually runs at, which is a fact rather than a setting. __tzget() returns
crt->crttzoff for the calling task, which cgistart's tzset() filled in from TZ
or the system's CVTTZ. That is immune to what the Parmlib says, which is the
point: the field is UTC by contract.
httpd->tzoffset keeps its job -- httpd's own local-time rendering, DISPLAY
TIME and the SMF records. The Date: header was never affected; it goes
through gmtime64() on a time64() value, and measuring it against a UTC clock
gives the same second.
Leaves jesst.c alone. It applies no offset at all, so it is a third behaviour
again -- but it defines its own main(), is therefore never autocalled into
HTTPJES2, and looks like dead code. Deleting it is the right fix, not
adjusting it.
Refs #145
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #150. The JES API carried two timestamps per job from different sources, and they disagreed.
What was wrong
start_stampis the epochhttpjes2.ccomputes explicitly, and the intent was already right:Correct now that
tzoffsetdefaults to the system offset (#150).start_displaythen ran that same instant throughctime64(), which converts throughcrt->crttzoff— the task's timezone, taken from CVTTZ — shifting it a second time by an unrelated offset.For a job that started at 17:25:18 UTC on the reference system:
start_stampstart_displayFri Aug 7 05:25:18 2026Neither UTC, nor the system's local time (12:25:18), nor the caller's (19:25:18 for CET summer). Two timezone sources in adjacent lines of the same JSON object.
Why UTC and not local
A local time is the wrong thing for an API to return at all. The server cannot know the caller's timezone, so any local rendering is unlabelled — the client cannot tell which zone it received. Return an unambiguous instant and let the client localize; that is what
zoweand every browser already do.It is also the convention mvsMF settled on elsewhere.
mvsmf/src/ussapi.cformatsmtimewithmgmtime64()and a literalZ, and bothdocs/endpoints/uss/list.mdanddocs/endpoints/console/hardcopy-log.mdspecify ISO 8601 UTC (2026-06-30T02:00:00Z). Those timestamps never had this class of bug because they never touch localtime — usinggmtime64removes the dependency on per-task timezone state instead of trying to keep two copies of it in step.The change
New
jestime()(src/jestime.c,include/jestime.h) renders atime64_tthroughgmtime64_r()as2026-08-07T17:25:18Z. Both display fields inhttpjes2.cand both injesst.cnow call it, replacing four open-codedctime64()calls that had already drifted into two spellings.A zero timestamp (a job that has not started or ended) still yields
"...", exactly as before, so clients that special-case it are unaffected.start_stampis untouched — it is the machine-readable epoch and it is correct.Verification
makeclean under-Wall -Werror, 6 modules link,make test-host63 assertions pass. Deployed toIBMUSER.HTTPD.V4R0M0D.LINKLIB.Not yet verified live — the STC still runs the previous build. After it is re-bestückt,
/jes/statusshould showstart_displayequal tostart_stampinterpreted as UTC:For that to be the true time as well,
TZOFFSETmust be out of the Parmlib (or set to the system's actual offset) — withTZOFFSET +02:00on a UTC−5 system the epoch itself is still 7 h early, which is #145 part (b), not this PR.Noticed, not fixed
/jes/statusis served byhttpjes2.c, notjesst.c— thenodeandjesinfofields in the response only exist in the former. Both definemain(), so onlyhttpjes2.ocan be in the HTTPJES2 module andjesst.ois never autocalled:jesst.clooks like dead code. It is changed here anyway so the two cannot drift again, but it is a candidate for deletion along with the rest of the deprecated JES CGI.The
ctime64()calls inhttpcons.candhttpdsrv.care left alone: those are operator- and dump-facing displays of internal times, where local time is arguably what is wanted. Only the API contract is changed here.