# Time::Piece [![CI Tests](https://github.com/Dual-Life/Time-Piece/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Dual-Life/Time-Piece/actions) A Perl module that replaces the standard `localtime` and `gmtime` functions with implementations that return objects. ## Documentation # 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 This module replaces the standard `localtime` and `gmtime` functions with implementations that return objects. It does so in a backwards compatible manner, so that using localtime/gmtime in the way documented in perlfunc will still return what you expect. The module actually implements most of an interface described by Larry Wall on the perl5-porters mailing list here: [https://www.nntp.perl.org/group/perl.perl5.porters/2000/01/msg5283.html](https://www.nntp.perl.org/group/perl.perl5.porters/2000/01/msg5283.html) After importing this module, when you use `localtime` or `gmtime` in a scalar context, rather than getting an ordinary scalar string representing the date and time, you get a `Time::Piece` object, whose stringification happens to produce the same effect as the `localtime` and `gmtime` functions. The primary way to create Time::Piece objects is through the `localtime` and `gmtime` functions. There is also a `new()` constructor which is the same as `localtime()`, except when passed a Time::Piece object, in which case it's a copy constructor. # Public Methods The following methods are available 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" ## 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 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. ## Utility Methods $t->is_leap_year # true if it's a leap year $t->month_last_day # 28-31 ## 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 default weekdays $t->mon_list(@days) # set the default months ## 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 called 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 It's possible to use simple addition and subtraction of objects: use Time::Seconds; my $seconds = $t1 - $t2; $t1 += ONE_DAY; # add 1 day (constant from Time::Seconds) The following 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 **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. $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 "!=". 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 Time::Piece provides flexible date parsing via the built-in `strptime()` function (from FreeBSD). For more information on acceptible formats and flags for `strptime` see "man strptime" on unix systems. Alternatively look here: [http://www.unix.com/man-page/FreeBSD/3/strftime/](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"); Outputs: Wed, 03 Nov 1943 The default format string is `"%a, %d %b %Y %H:%M:%S %Z"`, so these are equivalent: my $t1 = Time::Piece->strptime($string); my $t2 = Time::Piece->strptime($string, "%a, %d %b %Y %H:%M:%S %Z"); ## Handling Partial Dates When parsing incomplete date strings, you can provide defaults for 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 needed components: 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. ## GMT vs Local Time By default, `strptime` returns GMT objects when called as a class method: # Returns GMT (c_islocal = 0) Time::Piece->strptime($string, $format) To get local time objects, you can: # 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 }) ### Locale Considerations By default, `strptime` only parses English day and month names, while `strftime` uses your system locale. This can cause parsing failures for non-English dates. To parse localized dates, call `Time::Piece->use_locale()` to build 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: my @days = qw( Domingo Lunes Martes Miercoles Jueves Viernes Sabado ); my $spanish_day = localtime->day(@days); my @months = qw( Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre Noviembre Diciembre ); print localtime->month(@months); Set globally with: Time::Piece::day_list(@days); Time::Piece::mon_list(@months); ## 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. Prior to that, these flags were mostly ignored. Consider the current implementation somewhat "alpha" and in need of feedback. ### Numeric Offsets (%z) The `%z` specifier parses numeric timezone offsets (format: `+HHMM` or `-HHMM`): 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: - 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) **Note:** Full timezone name support is not currently implemented. For reliable timezone handling beyond GMT/UTC, consider using the [DateTime](https://metacpan.org/pod/DateTime) module. # Global Overriding To override localtime and gmtime everywhere: use Time::Piece ':override'; This replaces Perl's built-in functions with Time::Piece versions globally. # CAVEATS ## Setting $ENV{TZ} in Threads on Win32 Note that when using perl in the default build configuration on Win32 (specifically, when perl is built with PERL\_IMPLICIT\_SYS), each perl interpreter maintains its own copy of the environment and only the main interpreter will update the process environment seen by strftime. Therefore, if you make changes to $ENV{TZ} from inside a thread other than the main thread then those changes will not be seen by `strftime` if you subsequently call that with the %Z formatting code. You must change $ENV{TZ} in the main thread to have the desired effect in this case (and you must also call `_tzset()` in the main thread to register the environment change). Furthermore, remember that this caveat also applies to fork(), which is emulated by threads on Win32. ## Use of epoch seconds This module internally uses the epoch seconds system that is provided via the perl `time()` function and supported by `gmtime()` and `localtime()`. If your perl does not support times larger than `2^31` seconds (Perl versions < 5.12) then this module is likely to fail at processing dates beyond the year 2038. If that is not an option, use the [DateTime](https://metacpan.org/pod/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 AND LICENSE 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](http://www.tondering.dk/claus/calendar.html) # BUGS - The test harness leaves much to be desired. Patches welcome. - Proper UTF8 support ## Development Instructions The module uses GitHub Actions for continuous integration testing across multiple platforms. ### Using Feature Branches For development and testing: 1. **Setup Git Hooks**: ```bash ./setup-hooks.sh ``` 2. **Create a feature branch**: ```bash git checkout -b feature/windows-fix ``` You can create any descriptive name after the `feature/` prefix: ```bash git checkout -b feature/memory-optimization git checkout -b feature/strawberry-perl-compatibility ``` 3. **Make your changes and commit them**: ```bash # Make changes to your code git add . git commit -m "Implement Windows-specific fixes" ``` 4. **Push to GitHub to trigger automatic tests**: ```bash git push origin feature/windows-fix ``` 5. **Delete once merge done**: ```bash # Delete locally git branch -D feature/windows-fix # Delete from GitHub git push origin --delete feature/windows-fix ``` ## Installation From CPAN: ``` cpan Time::Piece ``` From source: ``` perl Makefile.PL make make test make install ```