Time

localtime#

Convert an epoch time to a broken-down calendar time in the local time zone.

localtime takes a Unix epoch value — seconds since 1970-01-01 UTC, the shape returned by time — and decomposes it into year, month, day, hour, minute, second, weekday, yearday, and a daylight-saving flag, all expressed in the local time zone. The time zone is read from the TZ environment variable if set, otherwise from the system default (typically /etc/localtime). For the UTC companion that ignores TZ, see gmtime.

Synopsis#

localtime EXPR
localtime

What you get back#

The return shape depends on context, and localtime is one of the handful of built-ins where the same expression in list context and scalar context produces completely different answers.

List context — a 9-element list, all numeric, taken straight out of the C struct tm:

#     0     1     2      3      4     5      6      7      8
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
                                                   localtime(time);

Index

Name

Range

Notes

0

$sec

0..60

60 is possible for leap seconds

1

$min

0..59

2

$hour

0..23

3

$mday

1..31

1-based day of the month

4

$mon

0..11

0-based: 0 = January, 11 = December

5

$year

years - 1900

e.g. 129 for 2029

6

$wday

0..6

0 = Sunday, 3 = Wednesday

7

$yday

0..365

0-based day of the year; 365 in leap year

8

$isdst

boolean

true when DST is in effect for the local TZ

The 0-based $mon and 1900-based $year are long-standing C inheritances; most bugs written against localtime come from forgetting exactly one of them. The fix for $year is += 1900:

my @t = localtime;
my $full_year = $t[5] + 1900;

Scalar context — the familiar English ctime(3) string, always 24 characters, always in English regardless of locale:

my $now_string = localtime;      # "Thu Oct 13 04:54:34 1994"

For a locale-aware or custom-formatted string, do not try to pick apart the scalar form. Go through the list form and POSIX::strftime:

use POSIX qw(strftime);
my $now_string = strftime "%a %b %e %H:%M:%S %Y", localtime;

Global state it touches#

  • TZ in %ENV — selects the local time zone. A program that wants to interpret a timestamp in a specific zone sets $ENV{TZ} and calls POSIX::tzset before localtime (see Edge cases for the caveat).

  • System zoneinfo (/etc/localtime, /usr/share/zoneinfo/...) when TZ is unset — read lazily by the C library, not by Perl directly.

  • %ENV changes to TZ do not always take effect on the next call; see Edge cases.

localtime does not touch $_ or any output-related globals.

Examples#

Current wall-clock date in a human-readable string:

my $now = localtime;             # "Wed Apr 23 14:07:12 2025"

Current date as a 9-element list — note the two offsets:

my ($s,$m,$h,$md,$mo,$y,$wd,$yd,$dst) = localtime;
printf "%04d-%02d-%02d %02d:%02d:%02d\n",
       $y+1900, $mo+1, $md, $h, $m, $s;    # "2025-04-23 14:07:12"

Month name from $mon using a lookup table — this is the canonical way, because $mon is 0-based exactly so it indexes an array:

my @abbr = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my ($mday, $mon) = (localtime)[3,4];
print "$abbr[$mon] $mday";       # e.g. "Apr 23"

Formatting with POSIX::strftime — the right answer whenever you need anything other than the bare English ctime string:

use POSIX qw(strftime);
my @now = localtime;
my $stamp = strftime "%Y-%m-%d %H:%M:%S", @now;   # "2025-04-23 14:07:12"

Interpreting a stored epoch in a specific zone. Set TZ, call POSIX::tzset, then localtime:

use POSIX qw(tzset);
local $ENV{TZ} = "Europe/Berlin";
tzset();
my $wall = localtime(1_700_000_000);   # "Tue Nov 14 23:13:20 2023"

Context trap — localtime inside print LIST is in list context, so you get nine numbers concatenated with no spaces. Use scalar to force the string form:

print localtime, "\n";           # e.g. "12724232312505230"
print scalar localtime, "\n";    # "Wed Apr 23 14:07:12 2025"

Edge cases#

  • No argument: localtime with no argument uses time — the current wall-clock moment.

  • Negative epoch: dates before 1970-01-01 UTC are accepted on systems with a signed time_t. Portability of very distant past or future times depends on the platform’s C library.

  • $year pitfall: $year + 1900 gives the full year. Writing "19$year" was a Y2K bug and is still wrong; write 1900 + $year.

  • $mon pitfall: $mon is 0-based. localtime output feeding anything 1-based (including POSIX::mktime) does not need adjustment — mktime expects the same 0-based convention. External systems (databases, ISO-8601 strings) usually want $mon + 1.

  • $isdst is advisory, not directly comparable across zones. 1 means “the local zone currently applies its DST offset”; 0 means “standard time”; -1 occasionally appears when the zone rules cannot decide (e.g. the hour that does not exist on a spring-forward day).

  • TZ reload caveat: most C libraries (glibc included) cache the parsed zone rules on the first lookup. A later assignment to $ENV{TZ} is not picked up automatically — call POSIX::tzset after every change, or the old zone will keep being used.

  • Scalar string is always English and is not affected by LC_TIME or any other locale setting. For a localized string, go through POSIX::strftime with LC_TIME set to the desired locale.

  • Thread safety: on systems where the C localtime(3) call is not reentrant, concurrent threads mutating TZ can race. In practice pperl and modern glibc use the reentrant localtime_r internally, but TZ is still a process-wide global.

  • Leap seconds: $sec can legally be 60 on systems with leap second support. Most systems smear or ignore them and you will never see it; code that must not break on 60 handles it already.

  • %a / %b width under POSIX::strftime: the abbreviated weekday and month names are not guaranteed three characters in every locale. Code that assumes a fixed column width will misalign in German, French, and many others.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • gmtime — same decomposition in UTC; does not consult TZ and is the right choice when you want a time zone–independent value

  • time — the epoch source that localtime most often receives as its argument

  • POSIX::strftime — format a localtime list into any string layout you need, locale-aware

  • POSIX::mktime — inverse direction: take a 9-element list back to an epoch value

  • POSIX::tzset — re-read TZ after changing it, mandatory if you want the change to take effect

  • $ENV{TZ} — the environment variable localtime reads to pick the local zone