== Formats for Dates and Times
Several Ruby time-related classes have instance method +strftime+,
which returns a formatted string representing all or part of a date or time:
- Date#strftime.
- DateTime#strftime.
- Time#strftime.
Each of these methods takes optional argument +format+,
which has zero or more embedded _format_ _specifications_ (see below).
Each of these methods returns the string resulting from replacing each
format specification embedded in +format+ with a string form
of one or more parts of the date or time.
A simple example:
Time.now.strftime('%H:%M:%S') # => "14:02:07"
A format specification has the form:
%[flags][width]conversion
It consists of:
- A leading percent character.
- Zero or more _flags_ (each is a character).
- An optional _width_ _specifier_ (an integer).
- A _conversion_ _specifier_ (a character).
Except for the leading percent character,
the only required part is the conversion specifier, so we begin with that.
=== Conversion Specifiers
==== \Date (Year, Month, Day)
- %Y - Year including century, zero-padded:
Time.now.strftime('%Y') # => "2022"
Time.new(-1000).strftime('%Y') # => "-1000" # Before common era.
Time.new(10000).strftime('%Y') # => "10000" # Far future.
Time.new(10).strftime('%Y') # => "0010" # Zero-padded by default.
- %y - Year without century, in range (0.99), zero-padded:
Time.now.strftime('%y') # => "22"
Time.new(1).strftime('%y') # => "01" # Zero-padded by default.
- %C - Century, zero-padded:
Time.now.strftime('%C') # => "20"
Time.new(-1000).strftime('%C') # => "-10" # Before common era.
Time.new(10000).strftime('%C') # => "100" # Far future.
Time.new(100).strftime('%C') # => "01" # Zero-padded by default.
- %m - Month of the year, in range (1..12), zero-padded:
Time.new(2022, 1).strftime('%m') # => "01" # Zero-padded by default.
Time.new(2022, 12).strftime('%m') # => "12"
- %B - Full month name, capitalized:
Time.new(2022, 1).strftime('%B') # => "January"
Time.new(2022, 12).strftime('%B') # => "December"
- %b - Abbreviated month name, capitalized:
Time.new(2022, 1).strftime('%b') # => "Jan"
Time.new(2022, 12).strftime('%h') # => "Dec"
- %h - Same as %b.
- %d - Day of the month, in range (1..31), zero-padded:
Time.new(2002, 1, 1).strftime('%d') # => "01"
Time.new(2002, 1, 31).strftime('%d') # => "31"
- %e - Day of the month, in range (1..31), blank-padded:
Time.new(2002, 1, 1).strftime('%e') # => " 1"
Time.new(2002, 1, 31).strftime('%e') # => "31"
- %j - Day of the year, in range (1..366), zero-padded:
Time.new(2002, 1, 1).strftime('%j') # => "001"
Time.new(2002, 12, 31).strftime('%j') # => "365"
==== \Time (Hour, Minute, Second, Subsecond)
- %H - Hour of the day, in range (0..23), zero-padded:
Time.new(2022, 1, 1, 1).strftime('%H') # => "01"
Time.new(2022, 1, 1, 13).strftime('%H') # => "13"
- %k - Hour of the day, in range (0..23), blank-padded:
Time.new(2022, 1, 1, 1).strftime('%k') # => " 1"
Time.new(2022, 1, 1, 13).strftime('%k') # => "13"
- %I - Hour of the day, in range (1..12), zero-padded:
Time.new(2022, 1, 1, 1).strftime('%I') # => "01"
Time.new(2022, 1, 1, 13).strftime('%I') # => "01"
- %l - Hour of the day, in range (1..12), blank-padded:
Time.new(2022, 1, 1, 1).strftime('%l') # => " 1"
Time.new(2022, 1, 1, 13).strftime('%l') # => " 1"
- %P - Meridian indicator, lowercase:
Time.new(2022, 1, 1, 1).strftime('%P') # => "am"
Time.new(2022, 1, 1, 13).strftime('%P') # => "pm"
- %p - Meridian indicator, uppercase:
Time.new(2022, 1, 1, 1).strftime('%p') # => "AM"
Time.new(2022, 1, 1, 13).strftime('%p') # => "PM"
- %M - Minute of the hour, in range (0..59), zero-padded:
Time.new(2022, 1, 1, 1, 0, 0).strftime('%M') # => "00"
- %S - Second of the minute in range (0..59), zero-padded:
Time.new(2022, 1, 1, 1, 0, 0, 0).strftime('%S') # => "00"
- %L - Millisecond of the second, in range (0..999), zero-padded:
Time.new(2022, 1, 1, 1, 0, 0, 0).strftime('%L') # => "000"
- %N - Fractional seconds, default width is 9 digits (nanoseconds):
t = Time.now # => 2022-06-29 07:10:20.3230914 -0500
t.strftime('%N') # => "323091400" # Default.
Use {width specifiers}[rdoc-ref:strftime_formatting.rdoc@Width+Specifiers]
to adjust units:
t.strftime('%3N') # => "323" # Milliseconds.
t.strftime('%6N') # => "323091" # Microseconds.
t.strftime('%9N') # => "323091400" # Nanoseconds.
t.strftime('%12N') # => "323091400000" # Picoseconds.
t.strftime('%15N') # => "323091400000000" # Femptoseconds.
t.strftime('%18N') # => "323091400000000000" # Attoseconds.
t.strftime('%21N') # => "323091400000000000000" # Zeptoseconds.
t.strftime('%24N') # => "323091400000000000000000" # Yoctoseconds.
- %s - Number of seconds since the epoch:
Time.now.strftime('%s') # => "1656505136"
==== Timezone
- %z - Timezone as hour and minute offset from UTC:
Time.now.strftime('%z') # => "-0500"
- %Z - Timezone name (platform-dependent):
Time.now.strftime('%Z') # => "Central Daylight Time"
==== Weekday
- %A - Full weekday name:
Time.now.strftime('%A') # => "Wednesday"
- %a - Abbreviated weekday name:
Time.now.strftime('%a') # => "Wed"
- %u - Day of the week, in range (1..7), Monday is 1:
t = Time.new(2022, 6, 26) # => 2022-06-26 00:00:00 -0500
t.strftime('%a') # => "Sun"
t.strftime('%u') # => "7"
- %w - Day of the week, in range (0..6), Sunday is 0:
t = Time.new(2022, 6, 26) # => 2022-06-26 00:00:00 -0500
t.strftime('%a') # => "Sun"
t.strftime('%w') # => "0"
==== Week Number
- %U - Week number of the year, in range (0..53), zero-padded,
where each week begins on a Sunday:
t = Time.new(2022, 6, 26) # => 2022-06-26 00:00:00 -0500
t.strftime('%a') # => "Sun"
t.strftime('%U') # => "26"
- %W - Week number of the year, in range (0..53), zero-padded,
where each week begins on a Monday:
t = Time.new(2022, 6, 26) # => 2022-06-26 00:00:00 -0500
t.strftime('%a') # => "Sun"
t.strftime('%W') # => "25"
==== Week Dates
See {ISO 8601 week dates}[https://en.wikipedia.org/wiki/ISO_8601#Week_dates].
t0 = Time.new(2023, 1, 1) # => 2023-01-01 00:00:00 -0600
t1 = Time.new(2024, 1, 1) # => 2024-01-01 00:00:00 -0600
- %G - Week-based year:
t0.strftime('%G') # => "2022"
t1.strftime('%G') # => "2024"
- %g - Week-based year without century, in range (0..99), zero-padded:
t0.strftime('%g') # => "22"
t1.strftime('%g') # => "24"
- %V - Week number of the week-based year, in range (1..53),
zero-padded:
t0.strftime('%V') # => "52"
t1.strftime('%V') # => "01"
==== Literals
- %n - Newline character "\n":
Time.now.strftime('%n') # => "\n"
- %t - Tab character "\t":
Time.now.strftime('%t') # => "\t"
- %% - Percent character '%':
Time.now.strftime('%%') # => "%"
==== Shorthand Conversion Specifiers
Each shorthand specifier here is shown with its corresponding
longhand specifier.
- %c - \Date and time:
Time.now.strftime('%c') # => "Wed Jun 29 08:01:41 2022"
Time.now.strftime('%a %b %e %T %Y') # => "Wed Jun 29 08:02:07 2022"
- %D - \Date:
Time.now.strftime('%D') # => "06/29/22"
Time.now.strftime('%m/%d/%y') # => "06/29/22"
- %F - ISO 8601 date:
Time.now.strftime('%F') # => "2022-06-29"
Time.now.strftime('%Y-%m-%d') # => "2022-06-29"
- %v - VMS date:
Time.now.strftime('%v') # => "29-JUN-2022"
Time.now.strftime('%e-%^b-%4Y') # => "29-JUN-2022"
- %x - Same as %D.
- %X - Same as %T.
- %r - 12-hour time:
Time.new(2022, 1, 1, 1).strftime('%r') # => "01:00:00 AM"
Time.new(2022, 1, 1, 1).strftime('%I:%M:%S %p') # => "01:00:00 AM"
Time.new(2022, 1, 1, 13).strftime('%r') # => "01:00:00 PM"
Time.new(2022, 1, 1, 13).strftime('%I:%M:%S %p') # => "01:00:00 PM"
- %R - 24-hour time:
Time.new(2022, 1, 1, 1).strftime('%R') # => "01:00"
Time.new(2022, 1, 1, 1).strftime('%H:%M') # => "01:00"
Time.new(2022, 1, 1, 13).strftime('%R') # => "13:00"
Time.new(2022, 1, 1, 13).strftime('%H:%M') # => "13:00"
- %T - 24-hour time:
Time.new(2022, 1, 1, 1).strftime('%T') # => "01:00:00"
Time.new(2022, 1, 1, 1).strftime('%H:%M:%S') # => "01:00:00"
Time.new(2022, 1, 1, 13).strftime('%T') # => "13:00:00"
Time.new(2022, 1, 1, 13).strftime('%H:%M:%S') # => "13:00:00"
- %+ (not supported in Time#strftime) - \Date and time:
DateTime.now.strftime('%+')
# => "Wed Jun 29 08:31:53 -05:00 2022"
DateTime.now.strftime('%a %b %e %H:%M:%S %Z %Y')
# => "Wed Jun 29 08:32:18 -05:00 2022"
=== ISO 8601 Format Specifications
This section shows format specifications that are compatible with
{ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601].
Details for various formats may be seen at the links.
Examples in this section assume:
t = Time.now # => 2022-06-29 16:49:25.465246 -0500
==== Dates
See {ISO 8601 dates}[https://en.wikipedia.org/wiki/ISO_8601#Dates].
- {Years}[https://en.wikipedia.org/wiki/ISO_8601#Years]:
- Basic year (+YYYY+):
t.strftime('%Y') # => "2022"
- Expanded year (±YYYYY):
t.strftime('+%5Y') # => "+02022"
t.strftime('-%5Y') # => "-02022"
- {Calendar dates}[https://en.wikipedia.org/wiki/ISO_8601#Calendar_dates]:
- Basic date (+YYYYMMDD+):
t.strftime('%Y%m%d') # => "20220629"
- Extended date (YYYY-MM-DD):
t.strftime('%Y-%m-%d') # => "2022-06-29"
- Reduced extended date (YYYY-MM):
t.strftime('%Y-%m') # => "2022-06"
- {Week dates}[https://en.wikipedia.org/wiki/ISO_8601#Week_dates]:
- Basic date (+YYYYWww+ or +YYYYWwwD+):
t.strftime('%Y%Ww') # => "202226w"
t.strftime('%Y%Ww%u') # => "202226w3"
- Extended date (YYYY-Www or YYYY-Www-D):
t.strftime('%Y-%Ww') # => "2022-26w"
t.strftime('%Y-%Ww-%u') # => "2022-26w-3"
- {Ordinal dates}[https://en.wikipedia.org/wiki/ISO_8601#Ordinal_dates]:
- Basic date (+YYYYDDD+):
t.strftime('%Y%j') # => "2022180"
- Extended date (YYYY-DDD):
t.strftime('%Y-%j') # => "2022-180"
==== Times
See {ISO 8601 times}[https://en.wikipedia.org/wiki/ISO_8601#Times].
- Times:
- Basic time (+Thhmmss.sss+, +Thhmmss+, +Thhmm+, or +Thh+):
t.strftime('T%H%M%S.%L') # => "T164925.465"
t.strftime('T%H%M%S') # => "T164925"
t.strftime('T%H%M') # => "T1649"
t.strftime('T%H') # => "T16"
- Extended time (+Thh:mm:ss.sss+, +Thh:mm:ss+, or +Thh:mm+):
t.strftime('T%H:%M:%S.%L') # => "T16:49:25.465"
t.strftime('T%H:%M:%S') # => "T16:49:25"
t.strftime('T%H:%M') # => "T16:49"
- {Time zone designators}[https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators]:
- Timezone (+time+ represents a valid time,
+hh+ represents a valid 2-digit hour,
and +mm+ represents a valid 2-digit minute):
- Basic timezone (time±hhmm, time±hh, or +timeZ+):
t.strftime('T%H%M%S%z') # => "T164925-0500"
t.strftime('T%H%M%S%z').slice(0..-3) # => "T164925-05"
t.strftime('T%H%M%SZ') # => "T164925Z"
- Extended timezone (time±hh:mm):
t.strftime('T%H:%M:%S%z') # => "T16:49:25-0500"
- See also:
- {Local time (unqualified)}[https://en.wikipedia.org/wiki/ISO_8601#Local_time_(unqualified)].
- {Coordinated Universal Time (UTC)}[https://en.wikipedia.org/wiki/ISO_8601#Coordinated_Universal_Time_(UTC)].
- {Time offsets from UTC}[https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC].
==== Combined \Date and \Time
See {ISO 8601 Combined date and time representations}[https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations].
An ISO 8601 combined date and time representation may be any
ISO 8601 date and any ISO 8601 time,
separated by the letter +T+.
For the relevant +strftime+ formats, see
{Dates}[rdoc-ref:strftime_formatting.rdoc@Dates]
and {Times}[rdoc-ref:strftime_formatting.rdoc@Times] above.
=== Flags
Flags may affect certain formatting specifications.
Multiple flags may be given with a single conversion specified;
order does not matter.
==== Padding Flags
- 0 - Pad with zeroes:
Time.new(10).strftime('%0Y') # => "0010"
- _ - Pad with blanks:
Time.new(10).strftime('%_Y') # => " 10"
- - - Don't pad:
Time.new(10).strftime('%-Y') # => "10"
==== Casing Flags
- ^ - Upcase result:
Time.new(2022, 1).strftime('%B') # => "January" # No casing flag.
Time.new(2022, 1).strftime('%^B') # => "JANUARY"
- # - Swapcase result:
Time.now.strftime('%p') # => "AM"
Time.now.strftime('%^p') # => "AM"
Time.now.strftime('%#p') # => "am"
==== Timezone Flags
- : - Put timezone as colon-separated hours and minutes:
Time.now.strftime('%:z') # => "-05:00"
- :: - Put timezone as colon-separated hours, minutes, and seconds:
Time.now.strftime('%::z') # => "-05:00:00"
=== Width Specifiers
The integer width specifier gives a minimum width for the returned string:
Time.new(2002).strftime('%Y') # => "2002" # No width specifier.
Time.new(2002).strftime('%10Y') # => "0000002002"
Time.new(2002, 12).strftime('%B') # => "December" # No width specifier.
Time.new(2002, 12).strftime('%10B') # => " December"
Time.new(2002, 12).strftime('%3B') # => "December" # Ignored if too small.