Skip to content

Commit 8b198ee

Browse files
authored
[flang] Update the date_and_time intrinsic for AIX (#104849)
Currently, strftime is called to get the timezone for the ZONE argument. On AIX, this routine requires an environment variable set in order to return the required format. This patch is to add the time difference computation from UTC for the platform.
1 parent 74d1960 commit 8b198ee

File tree

1 file changed

+69
-3
lines changed

1 file changed

+69
-3
lines changed

flang/runtime/time-intrinsic.cpp

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,61 @@ static void DateAndTimeUnavailable(Fortran::runtime::Terminator &terminator,
247247
}
248248

249249
#ifndef _WIN32
250+
#ifdef _AIX
251+
// Compute the time difference from GMT/UTC to get around the behavior of
252+
// strfname on AIX that requires setting an environment variable for numeric
253+
// value for ZONE.
254+
// The ZONE and the VALUES(4) arguments of the DATE_AND_TIME intrinsic has
255+
// the resolution to the minute.
256+
static int computeUTCDiff(const tm &localTime, bool *err) {
257+
tm utcTime;
258+
const time_t timer{mktime(const_cast<tm *>(&localTime))};
259+
if (timer < 0) {
260+
*err = true;
261+
return 0;
262+
}
263+
264+
// Get the GMT/UTC time
265+
if (gmtime_r(&timer, &utcTime) == nullptr) {
266+
*err = true;
267+
return 0;
268+
}
269+
270+
// Adjust for day difference
271+
auto dayDiff{localTime.tm_mday - utcTime.tm_mday};
272+
auto localHr{localTime.tm_hour};
273+
if (dayDiff > 0) {
274+
if (dayDiff == 1) {
275+
localHr += 24;
276+
} else {
277+
utcTime.tm_hour += 24;
278+
}
279+
} else if (dayDiff < 0) {
280+
if (dayDiff == -1) {
281+
utcTime.tm_hour += 24;
282+
} else {
283+
localHr += 24;
284+
}
285+
}
286+
return (localHr * 60 + localTime.tm_min) -
287+
(utcTime.tm_hour * 60 + utcTime.tm_min);
288+
}
289+
#endif
290+
291+
static std::size_t getUTCOffsetToBuffer(
292+
char *buffer, const std::size_t &buffSize, tm *localTime) {
293+
#ifdef _AIX
294+
// format: +HHMM or -HHMM
295+
bool err{false};
296+
auto utcOffset{computeUTCDiff(*localTime, &err)};
297+
auto hour{utcOffset / 60};
298+
auto hrMin{hour * 100 + (utcOffset - hour * 60)};
299+
auto n{sprintf(buffer, "%+05d", hrMin)};
300+
return err ? 0 : n + 1;
301+
#else
302+
return std::strftime(buffer, buffSize, "%z", localTime);
303+
#endif
304+
}
250305

251306
// SFINAE helper to return the struct tm.tm_gmtoff which is not a POSIX standard
252307
// field.
@@ -263,8 +318,19 @@ GetGmtOffset(const TM &tm, fallback_implementation) {
263318
// tm.tm_gmtoff is not available, there may be platform dependent alternatives
264319
// (such as using timezone from <time.h> when available), but so far just
265320
// return -HUGE to report that this information is not available.
266-
return -std::numeric_limits<Fortran::runtime::CppTypeFor<
267-
Fortran::common::TypeCategory::Integer, KIND>>::max();
321+
const auto negHuge{-std::numeric_limits<Fortran::runtime::CppTypeFor<
322+
Fortran::common::TypeCategory::Integer, KIND>>::max()};
323+
#ifdef _AIX
324+
bool err{false};
325+
auto diff{computeUTCDiff(tm, &err)};
326+
if (err) {
327+
return negHuge;
328+
} else {
329+
return diff;
330+
}
331+
#else
332+
return negHuge;
333+
#endif
268334
}
269335
template <typename TM = struct tm> struct GmtOffsetHelper {
270336
template <int KIND> struct StoreGmtOffset {
@@ -317,7 +383,7 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
317383
// Note: this may leave the buffer empty on many platforms. Classic flang
318384
// has a much more complex way of doing this (see __io_timezone in classic
319385
// flang).
320-
auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
386+
auto len{getUTCOffsetToBuffer(buffer, buffSize, &localTime)};
321387
copyBufferAndPad(zone, zoneChars, len);
322388
}
323389
if (values) {

0 commit comments

Comments
 (0)