Time::Piece(3perl)

okay bestie, welcome to Time::Piece(3perl) 🌸 straight outta the Perl Programmers Reference Guide and honestly?? it's kind of iconic. let's get into it, no skimming, everything's here πŸ’–


🌈 NAME 🌈

Time::Piece β€” Object Oriented time objects πŸ‘‘


✨ SYNOPSIS ✨

use Time::Piece;

my $t = localtime;
print "Time is $t\n";
print "Year is ", $t->year, "\n";

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so real talk: this module rolls up and replaces your standard localtime and gmtime functions with versions that hand you back objects instead of boring scalars. and the way it does it is backwards compatible, bestie β€” so if you call localtime/gmtime the way perlfunc documents it, you still get exactly what you expect. no betrayal. 🫢

it actually implements most of an interface that Larry Wall laid out on the perl5-porters mailing list, right here: <https://www.nntp.perl.org/group/perl.perl5.porters/2000/01/msg5283.html> β€” yes that's a real URL, don't @ me. πŸ’…

so once you import this module, when you use localtime or gmtime in a scalar context, you don't get a plain old scalar string of the date and time anymore β€” you get a Time::Piece object, whose stringification just so happens to produce the same effect as the localtime and gmtime functions. sneaky. flawless. obsessed.

the main character way to make Time::Piece objects is through localtime and gmtime themselves. there's also a new() constructor which is literally the same as localtime(), except when you pass it a Time::Piece object, in which case ✨ surprise ✨ it's a copy constructor.

πŸŽ€ Public Methods πŸŽ€

these are the methods you get on the object:

⏰ Time Components

$t->sec                 # also available as $t->second
$t->min                 # also available as $t->minute
$t->hour                # 24 hour

πŸ“… Date Components

$t->mday                # also available as $t->day_of_month
$t->mon                 # 1 = January
$t->_mon                # 0 = January
$t->year                # based at 0 (year 0 AD is, of course 1 BC)
$t->_year               # year minus 1900
$t->yy                  # 2 digit year

πŸ—“οΈ Day and Month Names

$t->monname             # Feb
$t->month               # same as $t->monname
$t->fullmonth           # February
$t->wday                # 1 = Sunday
$t->_wday               # 0 = Sunday
$t->day_of_week         # 0 = Sunday
$t->wdayname            # Tue
$t->day                 # same as wdayname
$t->fullday             # Tuesday

πŸ’– Formatted Date/Time Output

$t->hms                 # 12:34:56
$t->hms(".")            # 12.34.56
$t->time                # same as $t->hms
$t->ymd                 # 2000-02-29
$t->date                # same as $t->ymd
$t->mdy                 # 02-29-2000
$t->mdy("/")            # 02/29/2000
$t->dmy                 # 29-02-2000
$t->dmy(".")            # 29.02.2000
$t->datetime            # 2000-02-29T12:34:56 (ISO 8601)
$t->cdate               # Tue Feb 29 12:34:56 2000
"$t"                    # same as $t->cdate
$t->strftime(FORMAT)    # same as POSIX::strftime (without the overhead
                        # of the full POSIX extension)
$t->strftime()          # "Tue, 29 Feb 2000 12:34:56 GMT"

🌸 strftime Format Flags 🌸

heads up bestie, the strftime method calls your system's native strftime() implementation, so which format flags work and how they behave is 100% up to your platform. iykyk. πŸ™ˆ

Platform Variability: some format flags act differently or might not even exist on certain platforms. these ones are the known troublemakers with platform-specific issues: %e, %D, %F, %k, %l, %P, %r, %R, %s, %T, %u, %V, %z, and %Z. πŸ’€

to smooth those differences over, Time::Piece has a special translation layer that tries to unify behavior across platforms. for example, %F isn't available on some Microsoft platforms, so it gets automatically converted to "%Y-%m-%d" internally before calling the system's strftime(). slay. πŸ’…

for the complete list of format flags your system supports, go check your platform's strftime(3) manual page (man strftime on Unix-like systems). 🫢

πŸ”₯ Epoch and Calendar Calculations

$t->epoch               # seconds since the epoch
$t->julian_day          # number of days since Julian period began
$t->mjd                 # modified Julian date (JD-2400000.5 days)
$t->week                # week number (ISO 8601)
$t->yday                # also available as $t->day_of_year, 0 = Jan 01

🌍 Timezone and DST

$t->tzoffset            # timezone offset in a Time::Seconds object
$t->isdst               # also available as $t->daylight_savings
$t->to_gmtime           # convert to GMT, preserving the epoch
$t->to_localtime        # convert to local time, preserving the epoch

the isdst method returns:

  • 0 for GMT/UTC times (they never have DST) ⭐
  • 0 or 1 for local times depending on whether DST is active
  • Automatically calculated if unknown

the tzoffset method returns the offset from UTC as a Time::Seconds object. for GMT/UTC times, this always returns 0. for local times, it calculates the actual offset including any DST adjustment. ✨

the to_gmtime and to_localtime methods convert between timezone contexts while preserving the same moment in time (epoch). they always return a new Time::Piece object. no cap.

🧰 Utility Methods

$t->is_leap_year        # true if it's a leap year
$t->month_last_day      # 28-31
$t->add_days            # Add days
$t->add_months          # Add months
$t->add_years           # Add years

πŸ‘‘ Global Configuration

$t->time_separator($s)     # set the default separator (default ":")
$t->date_separator($s)     # set the default separator (default "-")
$t->day_list(@days)        # set the names used by wdayname()
$t->mon_list(@months)      # set the names used by month()
$t->fullday_list(@days)    # set the names used by fullday()
$t->fullmon_list(@months)  # set the names used by fullmonth()

πŸ” Parsing

Time::Piece->strptime(STRING, FORMAT)
                        # see strptime man page. Creates a new
                        # Time::Piece object
Note: localtime and gmtime are not listed above. if you call them as methods on a Time::Piece object, they act as constructors, returning a new Time::Piece object for the current time. in other words: they're not useful as methods. πŸ™ˆ

πŸ’œ Date Calculations πŸ’œ

you can do simple addition and subtraction of objects, bestie. it's giving math but cute:

use Time::Seconds;

my $seconds = $t1 - $t2;
$t1 += ONE_DAY; # add 1 day (constant from Time::Seconds)

these are valid ($t1 and $t2 are Time::Piece objects):

$t1 - $t2; # returns Time::Seconds object
$t1 - 42; # returns Time::Piece object
$t1 + 533; # returns Time::Piece object
$t1->add_days(2); # returns Time::Piece object
Note: all arithmetic uses epoch seconds (UTC). when daylight saving time (DST) changes occur:
  • Adding seconds works on UTC time, so adding 3600 seconds during DST transition from 1:30 AM gives 3:30 AM (not 2:30 AM, which doesn't exist during "spring forward") 😭
  • Subtracting across DST transitions may differ from wall-clock expectations due to skipped or repeated hours

πŸŒ™ Adding Months and Years πŸŒ™

two methods handle calendar arithmetic differently than seconds-based math:

$t = $t->add_months(6);
$t = $t->add_years(5);

important behaviors:

  • these preserve the day-of-month number, which can cause overflow (Jan 31 + 1 month = Mar 3, since "Feb 31" doesn't exist) πŸ’€
  • wall-clock time is preserved across DST transitions
  • order matters: add_months(1) then "+ 86400" gives different results than "+ 86400" then add_months(1)

πŸ¦‹ Truncation πŸ¦‹

calling the truncate method returns a copy of the object but with the time truncated to the start of the supplied unit. so dramatic, so useful.

$t = $t->truncate(to => 'day');

this example will set the time to midnight on the same date which $t had previously. allowed values for the to parameter are: "year", "quarter", "month", "day", "hour", "minute" and "second". ✨


⭐ Date Comparisons ⭐

date comparisons are also possible, using the full suite of <, >, <=, >=, <=>, == and !=. periodt.

all comparisons use epoch seconds, so they work correctly across timezones:

my $t1 = localtime;
my $t2 = gmtime;
if ($t1 > $t2) {  # Compares actual moments in time, not clock values
    # ...
}

Time::Piece objects can also be compared as strings using cmp:

if ($t1 cmp "2024-01-15") {  # Compares against cdate format
    # ...
}

πŸ¦‹ Date Parsing πŸ¦‹

okay bestie, this is the part where Time::Piece gets flexible πŸ’…. It gives you super chill date parsing through the built-in strptime() function (which, fun fact, comes from FreeBSD ✨).

So if you wanna know all the acceptable formats and flags for strptime, just run man strptime on your unix system (iykyk). Orrr you can peek here instead: <http://www.unix.com/man-page/FreeBSD/3/strftime/>


🌸 Basic Usage 🌸

my $t = Time::Piece->strptime("Sunday 3rd Nov, 1943",
                              "%A %drd %b, %Y");

print $t->strftime("%a, %d %b %Y");

and it outputs:

Wed, 03 Nov 1943

The default format string is "%a, %d %b %Y %H:%M:%S %Z", so these two are literally the same thing, no cap:

my $t1 = Time::Piece->strptime($string);
my $t2 = Time::Piece->strptime($string, "%a, %d %b %Y %H:%M:%S %Z");

✨ Supported Format Flags ✨

Time::Piece uses a custom strptime() implementation that supports these format flags, bestie:

  • %% β€” a literal % character
  • %a β€” abbreviated weekday name (Mon, Tue, etc.)
  • %A β€” full weekday name (Monday, Tuesday, etc.)
  • %b β€” abbreviated month name (Jan, Feb, etc.)
  • %B β€” full month name (January, February, etc.)
  • %C β€” century number (00-99)
  • %d β€” day of month (01-31)
  • %D β€” equivalent to %m/%d/%y
  • %e β€” day of month ( 1-31, space-padded)
  • %f β€” fractional seconds as microseconds (up to 6 digits, parsed but ignored πŸ™ˆ)
  • %F β€” equivalent to %Y-%m-%d (ISO 8601 date format)
  • %h β€” abbreviated month name (same as %b)
  • %H β€” hour in 24-hour format (00-23)
  • %I β€” hour in 12-hour format (01-12)
  • %j β€” day of year (001-366)
  • %k β€” hour in 24-hour format ( 0-23, space-padded)
  • %l β€” hour in 12-hour format ( 1-12, space-padded)
  • %m β€” month number (01-12)
  • %M β€” minute (00-59)
  • %n β€” any whitespace
  • %p β€” AM/PM indicator
  • %P β€” alt AM/PM indicator
  • %r β€” time in AM/PM format (%I:%M:%S %p, or %H:%M:%S if the locale has no AM/PM)
  • %R β€” equivalent to %H:%M
  • %s β€” seconds since Unix epoch (1970-01-01 00:00:00 UTC)
  • %S β€” second (00-60, allowing for leap seconds πŸ‘‘)
  • %t β€” any whitespace (same as %n)
  • %T β€” equivalent to %H:%M:%S
  • %u β€” weekday as a number (1-7, Monday = 1)
  • %w β€” weekday as a number (0-6, Sunday = 0)
  • %y β€” year within century (00-99). Values 00-68 are 2000-2068, and 69-99 are 1969-1999
  • %Y β€” year with century (e.g., 2024)
  • %z β€” timezone offset (+HHMM, -HHMM, +HH:MM, or -HH:MM)
  • %Z β€” timezone name (only GMT and UTC recognized; others parsed but ignored)

Unsupported Locale Flags: the format flags %c, %x, and %X are not supported, because they're highly locale-dependent and have inconsistent formats across systems 😭. But you can totally build equivalent formats with the individual flags listed above. Like, %c is typically equivalent to something like:

"%a %b %e %H:%M:%S %Y"   # e.g., "Tue Feb 29 12:34:56 2000"
Note: %U, %V, and %W (the week number formats) are parsed but not fully implemented in the strptime logic, because they need extra date components to actually calculate the real date πŸ’€.
Note: %f (fractional seconds) is only supported in strptime for parsing. It is not available in strftime for output formatting, because Time::Piece uses epoch seconds which do not store subsecond precision, bestie.

🌈 GMT vs Local Time 🌈

By default, strptime returns GMT objects when you call it as a class method:

# Returns GMT (c_islocal = 0)
Time::Piece->strptime($string, $format)

To get local time objects instead, you can do any of these:

# Call as instance method on localtime object
localtime()->strptime($string, $format)

# Use explicit islocal option
Time::Piece->strptime($string, $format, { islocal => 1 })

# Pass a local Time::Piece object as defaults
my $local = localtime();
Time::Piece->strptime($string, $format, { defaults => $local })

The islocal and defaults options were added in version 1.37 ✨ β€” and the instance method can be used for compatibility with previous versions 🫢.


πŸ’œ Timezone Parsing with %z and %Z πŸ’œ

Time::Piece's strptime() function has some limited support for parsing timezone information through two format specifiers: %z and %Z.

Added in version 1.38. Before that, these flags were mostly ignored, fr fr. So consider the current implementation somewhat "alpha" and in need of feedback πŸ’€.

🌊 Numeric Offsets (%z)

The %z specifier parses numeric timezone offsets (format: "[+-]HHMM", "[+-]HH:MM", or "[+-]HH"):

my $t = Time::Piece->strptime("2024-01-15 15:30:00 +0500",
                              "%Y-%m-%d %H:%M:%S %z");
print $t->hour;  # prints 10 (converted to UTC: 15:30 - 5:00)

Key behaviors, bestie:

  • Offsets are applied to convert to UTC (+0500 means "5 hours ahead of UTC")
  • Valid range: -1200 to +1400 with minutes less than 60
  • For local objects (islocal == 1), the result is converted to system timezone

Times parsed with timezone information default to GMT. To convert to local time:

# Parse and convert to local timezone
my $t = Time::Piece->strptime("2024-01-15 15:30:00 +0500",
                              "%Y-%m-%d %H:%M:%S %z",
                              { islocal => 1 });
# Result: 10:30 UTC converted to your local timezone

πŸŽ€ Timezone Names (%Z)

The %Z specifier currently only recognizes "GMT" and "UTC" (case-sensitive πŸ‘€). Other timezone names are parsed but ignored:

# GMT/UTC recognized and handled
my $t1 = Time::Piece->strptime("2024-01-15 10:30:00 GMT",
                               "%Y-%m-%d %H:%M:%S %Z");
print $t1->hour;  # prints 10 (no adjustment)

# Other timezones parsed but ignored
my $t2 = Time::Piece->strptime("2024-01-15 10:30:00 PST",
                               "%Y-%m-%d %H:%M:%S %Z");
print $t2->hour;  # prints 10 (PST ignored - no adjustment)

# Parse and convert to local timezone
my $t3 = Time::Piece->strptime("2024-01-15 15:30:00 UTC",
                              "%Y-%m-%d %H:%M:%S %Z",
                              { islocal => 1 });
print $t3->hour;  # prints 10:30 UTC converted to your local timezone
Note: full timezone name support is not currently implemented πŸ₯². For reliable timezone handling beyond GMT/UTC, consider using the DateTime module.

πŸ“ Handling Partial Dates πŸ“

When you're parsing incomplete date strings, you can provide defaults for the missing components in several ways:

Array Reference β€” standard time components (as returned by localtime):

my @defaults = localtime();
my $t = Time::Piece->strptime("15 Mar", "%d %b",
                              { defaults => \@defaults });

Hash Reference β€” specify only the components you actually need:

my $t = Time::Piece->strptime("15 Mar", "%d %b",
                              { defaults => {
                                  year => 2023,
                                  hour => 14,
                                  min  => 30
                              } });

Valid keys: "sec", "min", "hour", "mday", "mon", "year", "wday", "yday", "isdst"

Note: for the "year" parameter, numbers less than 1000 are treated as an offset from 1900. Whereas numbers larger than 1000 are treated as the actual year πŸ‘‘

Time::Piece Object β€” uses all components from the object:

my $base = localtime();
my $t = Time::Piece->strptime("15 Mar", "%d %b",
                              { defaults => $base });
Note: in all cases, parsed values always override defaults. Only missing components use default values πŸ’….

🧚 Locale Considerations 🧚

By default, strptime only parses English day and month names, while strftime uses your system locale. So yeah, that combo can cause parsing failures for non-English dates 😭.

To parse localized dates, call Time::Piece->use_locale() to build up a list of your locale's day and month names:

# Enable locale-aware parsing (global setting)
Time::Piece->use_locale();

# Now strptime can parse names in your system locale
my $t = Time::Piece->strptime("15 Marzo 2024", "%d %B %Y");
Note: this is a global change affecting all Time::Piece instances πŸ‘€.

You can also override the day/month names manually:

# Abbreviated day names
my @days = qw( Dom Lun Mar Mie Jue Vie Sab );
my $spanish_day = localtime->day(@days);

# Full day names
my @fulldays = qw( Domingo Lunes Martes Miercoles Jueves Viernes Sabado );
my $spanish_fullday = localtime->fullday(@fulldays);

# Abbreviated month names
my @months = qw( Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic );
print localtime->month(@months);

# Full month names
my @fullmonths = qw( Enero Febrero Marzo Abril Mayo Junio
                     Julio Agosto Septiembre Octubre Noviembre Diciembre );
print localtime->fullmonth(@fullmonths);

And set them globally with:

Time::Piece::day_list(@days);
Time::Piece::mon_list(@months);
Time::Piece::fullday_list(@fulldays);
Time::Piece::fullmon_list(@fullmonths);

πŸ‘‘ Global Overriding πŸ‘‘

To override localtime and gmtime everywhere:

use Time::Piece ':override';

This replaces Perl's built-in functions with Time::Piece versions globally, no take-backs πŸ”₯.


πŸ’€ CAVEATS πŸ’€

🫠 Setting $ENV{TZ} in Threads on Win32

So here's the tea: when you're using perl in the default build configuration on Win32 (specifically, when perl is built with PERL_IMPLICIT_SYS), each perl interpreter keeps its own copy of the environment, and only the main interpreter will update the process environment that strftime actually sees.

So if you change $ENV{TZ} from inside a thread that isn't the main thread, those changes will not be seen by strftime if you then call it with the %Z formatting code 😭. You have to change $ENV{TZ} in the main thread for it to work here (and you also have to call _tzset() in the main thread to register the environment change).

Also, remember that this caveat applies to fork() too, which is emulated by threads on Win32. Yeah. It's a lot, bestie πŸ₯².

⏳ Use of epoch seconds

This module internally uses the epoch seconds system that's provided via the perl time() function and supported by gmtime() and localtime().

If your perl does not support times larger than 2^31 seconds (that's Perl versions < 5.12) then this module is likely to fail at processing dates beyond the year 2038 πŸ’€. If that's not an option for you, use the DateTime module, which has support for years well into the future and past ✨.


🌈 AUTHOR 🌈

Matt Sergeant, matt@sergeant.org Jarkko Hietaniemi, jhi@iki.fi (while creating Time::Piece for core perl)


Copyright 2001, Larry Wall.

This module is free software, you may distribute it under the same terms as Perl πŸ’–.


⭐ SEE ALSO ⭐

The excellent Calendar FAQ at <http://www.tondering.dk/claus/calendar.html> πŸ—“οΈβœ¨

πŸ› BUGS πŸ›

ok so nobody's perfect and this module is lowkey keeping it real about it πŸ’– here's the tea:

  • The test harness leaves much to be desired. Patches welcome.

fr fr, the test harness is giving "we tried" 😭 like it does the thing but it's not the moment it could be. and bestie, the way to fix that is literally patches β€” if you got the skills, send them in, it's giving community service but make it cute πŸ’…

  • Proper UTF8 support

no cap, proper UTF8 support is on the wishlist and it is not there yet. so keep that in your notes 🫢


and that's the whole page bestie, you made it to the end, iconic behavior πŸ’– terminally online perl girlie signing off, this was perl v5.44.0, dated 2026-10-17, so if you're reading this from the future, hi, love you, go write a patch for that test harness πŸ’…βœ¨πŸŒ·

πŸ“¦ where this page comes from
  • arch rolling: perl 5.44.0-1 Β· Artistic-1.0-Perl AND GPL-1.0-or-later
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms