The localtime and gmtime built-in functions are interfaces to the POSIX functions for turning a Unix epoch timestamp into date-time components in either UTC or the system local time. When you want to go the other way, there's Time::Local.
The localtime and gmtime functions have two quirks as compared to the date-time components humans might expect. The value it returns for the month is 0-indexed, ostensibly so that it can be used directly in a 0-indexed array of 12 month names, and the value it returns for the year is the number of years since the year 1900.
Historically, Time::Local's timelocal and timegm functions have not used this interpretation for the year value; they use a heuristic to attempt to handle the common two- and four-digit human representations of years, in addition to the localtime year value format. Unfortunately, this means that quite often, the year value as returned by localtime or similar functions will not be interpreted correctly. Of note, starting in 2021, the value of
70 returned for the year of the Unix epoch
1970-01-01 will start to be interpreted as
Recent versions of Time::Local (1.27+, or the version that comes with Perl 5.30+) introduce
timegm_modern variants with a more consistent interpretation. While it is a reliable interpretation unlike the legacy functions, it interprets it as a full year value, rather than the years since 1900 as returned by localtime and similar functions. This means you unfortunately still cannot use it directly as the reverse of localtime/gmtime.
There is hope however. If you have access to the new modern functions, you can simply add 1900 to the year value and it will always be interpreted correctly. If you don't, adding 1900 to the year value if it is zero or positive will always avoid the two-digit heuristic and thus make the interpretation consistent.
# modern functions my @localtime = localtime($epoch); $localtime += 1900; my $epoch = timelocal_modern(@localtime); # legacy functions my @gmtime = gmtime($epoch); $gmtime += 1900 if $gmtime >= 0; my $epoch = timegm(@gmtime);