Skip to content

Ruby Date and Time — Time DateTime Date Parsing and Formatting Explained

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about Ruby Date and Time. We cover key concepts, practical examples, and best practices to help you master this topic.

Ruby Date and Time classes provide temporal data handling with Time for system timestamps, Date for calendar dates, and DateTime for combined date-time operations with Parsing, formatting, and arithmetic.

What You'll Learn

  • Using Time for timestamps and system time
  • Working with Date for calendar operations
  • Parsing and formatting dates and times
  • Date arithmetic and time zones

Why It Matters

Date and time handling is universal. Durga Antivirus Pro timestamps every scan with precise time, calculates expiry dates for license keys, and schedules periodic scans. Doda Browser records browsing history with timestamps, manages cookie expiry, and displays relative times ("2 hours ago"). Getting dates right prevents bugs that only appear at midnight.

Real-World Use

Log timestamps, session expiry, scheduled tasks, reporting periods, event scheduling, timezone conversion — every application deals with temporal data.

flowchart LR
    A["Date/Time"] --> B["Time"]
    B --> C["Date"]
    C --> D["DateTime"]
    D --> E["Parsing"]
    E --> F["Arithmetic"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style F fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Requiring Date Support

require "date"
require "time"

The Time Class

Time represents system timestamps with nanosecond precision:

# Current time
now = Time.now
puts now  # 2026-06-28 12:30:45.123456789 +0000

# Specific time
t = Time.new(2026, 6, 28, 12, 30, 0, "+00:00")
puts t  # 2026-06-28 12:30:00 +0000

# UTC time
utc = Time.now.utc
puts utc  # 2026-06-28 12:30:45 UTC

# Epoch time
epoch = Time.now.to_i
puts epoch  # 1780123445 (seconds since 1970-01-01)

Time Components

t = Time.now
puts t.year       # 2026
puts t.month      # 6
puts t.day        # 28
puts t.hour       # 12
puts t.min        # 30
puts t.sec        # 45
puts t.nsec       # 123456789 (nanoseconds)
puts t.wday       # 0 (Sunday = 0)
puts t.yday       # 179 (day of year)
puts t.zone       # UTC

Time Formatting

t = Time.now

puts t.strftime("%Y-%m-%d")           # 2026-06-28
puts t.strftime("%B %d, %Y")          # June 28, 2026
puts t.strftime("%I:%M %p")           # 12:30 PM
puts t.strftime("%Y-%m-%d %H:%M:%S")  # 2026-06-28 12:30:45
puts t.strftime("%A, %B %d")          # Sunday, June 28
puts t.iso8601                        # 2026-06-28T12:30:45+00:00
puts t.rfc2822                        # Sun, 28 Jun 2026 12:30:45 +0000

Time Arithmetic

t = Time.now

# Seconds-based arithmetic
future = t + 3600  # +1 hour
puts future - t    # 3600.0 (seconds)

past = t - 86400   # -1 day
puts past - t      # -86400.0

# Compare times
puts t < future    # true
puts past < t      # true

The Date Class

Date handles calendar dates without time components:

require "date"

# Current date
today = Date.today
puts today  # 2026-06-28

# Specific date
d = Date.new(2026, 6, 28)
puts d  # 2026-06-28

# Julian day number
puts d.jd  # 2464810

Date Components

d = Date.today
puts d.year       # 2026
puts d.month      # 6
puts d.day        # 28
puts d.wday       # 0 (Sunday)
puts d.cwday      # 7 (ISO week day)
puts d.cweek      # 26 (ISO week number)
puts d.leap?      # false (2026 is not a leap year)

Date Arithmetic

require "date"

today = Date.today

# Day-based arithmetic
tomorrow = today + 1
yesterday = today - 1
next_week = today + 7
next_month = today.next_month
next_year = today.next_year

puts tomorrow   # 2026-06-29
puts next_month # 2026-07-28

# Difference in days
puts next_week - today  # 7

# Next/previous specific day
puts today.next_day(:monday)   # Next Monday
puts today.prev_day(:friday)   # Previous Friday

Date Predicates

d = Date.new(2026, 6, 28)
puts d.monday?    # false
puts d.tuesday?   # false
puts d.wednesday? # false
puts d.thursday?  # false
puts d.friday?    # false
puts d.saturday?  # false
puts d.sunday?    # true
puts d.julian?    # false (Gregorian calendar)

The DateTime Class

DateTime combines Date and Time capabilities:

require "date"

dt = DateTime.new(2026, 6, 28, 12, 30, 45, "+00:00")
puts dt  # 2026-06-28T12:30:45+00:00

# Components
puts dt.year    # 2026
puts dt.hour    # 12
puts dt.min     # 30
puts dt.sec     # 45
puts dt.offset  # 0/1 (UTC offset as rational)
puts dt.zone    # +00:00

# DateTime arithmetic
dt2 = dt + 1  # +1 day
puts dt2  # 2026-06-29T12:30:45+00:00

Parsing Dates and Times

require "date"
require "time"

# Time.parse
require "time"
t = Time.parse("2026-06-28 12:30:00 UTC")
puts t  # 2026-06-28 12:30:00 UTC

# Date.parse
d = Date.parse("2026-06-28")
puts d  # 2026-06-28

# DateTime.parse
dt = DateTime.parse("2026-06-28T12:30:45+00:00")
puts dt  # 2026-06-28T12:30:45+00:00

# Custom format parsing with strptime
d = Date.strptime("06/28/2026", "%m/%d/%Y")
puts d  # 2026-06-28

t = Time.strptime("28-06-2026 12:30", "%d-%m-%Y %H:%M")
puts t  # 2026-06-28 12:30:00 +0000

Common Date Formats

require "date"

formats = [
  "2026-06-28",
  "06/28/2026",
  "June 28, 2026",
  "28 Jun 2026",
  "20260628",
  "2026-06-28 12:30:45",
  "2026-06-28T12:30:45Z"
]

formats.each do |fmt|
  begin
    d = Date.parse(fmt)
    puts "#{fmt.ljust(30)} => #{d}"
  rescue
    puts "#{fmt.ljust(30)} => Parse error"
  end
end

Time Zones

require "time"

# Create time in specific zone
t1 = Time.new(2026, 6, 28, 12, 0, 0, "+05:30")
puts t1  # 2026-06-28 12:00:00 +0530

# Convert to UTC
puts t1.getutc  # 2026-06-28 06:30:00 UTC

# Convert to local
puts t1.getlocal  # Local time equivalent

# Check DST
puts t1.dst?  # false

# UTC offset
puts t1.utc_offset  # 19800 (seconds, = 5.5 hours)

# Timezone name
puts t1.zone  # +05:30

Working with Time Zones Using tzinfo

require "tzinfo"

# Get timezone
tz = TZInfo::Timezone.get("America/New_York")

# Convert UTC time to timezone
utc_time = Time.now.utc
local_time = tz.utc_to_local(utc_time)
puts local_time  # 2026-06-28 08:30:00 -0400 (for example)

# Current local time
puts tz.now

Measuring Elapsed Time

start = Process.clock_gettime(Process::CLOCK_MONOTONIC)

# Simulate work
sleep(0.1)

elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
puts "Elapsed: #{elapsed.round(4)} seconds"  # Elapsed: ~0.1002 seconds

# Benchmark alternative
require "benchmark"
time = Benchmark.measure do
  sleep(0.1)
end
puts time.real  # ~0.1002

Common Mistakes

1. Ignoring Time Zones in Parsing

# Ambiguous — assumes local time
t = Time.parse("2026-06-28 12:30:00")

# Explicit — always specify
t = Time.parse("2026-06-28 12:30:00 UTC")

2. Using Time.now for Performance Measurement

# Wrong — system clock can jump
start = Time.now
work()
puts Time.now - start

# Correct — monotonic clock
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
work()
puts Process.clock_gettime(Process::CLOCK_MONOTONIC) - start

3. Forgetting to require "date" or "time"

# NoMethodError — Date not loaded
# Date.today

require "date"
Date.today  # Works

4. Assuming Time is Immutable

t = Time.now
t2 = t + 10  # Returns new Time, doesn't modify t
puts t == t2  # false

5. Comparing Across Time Zones Incorrectly

t1 = Time.new(2026, 6, 28, 12, 0, 0, "+00:00")
t2 = Time.new(2026, 6, 28, 15, 0, 0, "+03:00")
puts t1 == t2  # true — same instant in time!

Practice Questions

1. What's the difference between Time and DateTime?

Time represents system timestamps with nanosecond precision. DateTime provides calendar date + time with better date arithmetic. Time is faster; DateTime is more feature-rich for date manipulation.

2. How do you format a date as "2026-06-28"?

Use strftime("%Y-%m-%d") or iso8601 for ISO 8601 format.

3. Why use Process::CLOCK_MONOTONIC for timing?

It measures real elapsed time regardless of system clock adjustments (NTP, DST, user changes). Time.now can jump forward or backward.

4. How do you handle timezone conversion?

Use Time#getutc for UTC, Time#getlocal for local time, or the tzinfo gem for named timezones like "America/New_York".

Challenge: Write a method that takes an array of date strings in various formats and returns them sorted chronologically, then grouped by month.

Solution
require "date"

def process_dates(date_strings)
  dates = date_strings.map { |s| Date.parse(s) }
  sorted = dates.sort
  grouped = sorted.group_by { |d| [d.year, d.month] }

  {
    sorted: sorted.map(&:to_s),
    total: sorted.size,
    by_month: grouped.transform_keys { |y, m| "#{y}-#{format('%02d', m)}" }
      .transform_values { |dates| dates.map(&:to_s) },
    range: "#{sorted.first} to #{sorted.last}"
  }
end

dates = ["2026-06-01", "01/15/2026", "March 3, 2026", "2026-12-25", "07/04/2026"]
result = process_dates(dates)
puts result[:sorted].inspect
puts result[:range]
puts result[:by_month].keys.inspect

Expected output:

["2026-01-15", "2026-03-03", "2026-06-01", "2026-07-04", "2026-12-25"]
2026-01-15 to 2026-12-25
["2026-01", "2026-03", "2026-06", "2026-07", "2026-12"]

FAQ

{{< faq question="Should I use Time or DateTime?" >}} Use Time for timestamps, logging, and performance measurement. Use Date for calendar dates without time. Use DateTime when you need both date and time with rich date arithmetic. In modern Ruby, Time covers most use cases. {{< /faq >}}

{{< faq question="How do I get the current timestamp in milliseconds?" >}} (Time.now.to_f * 1000).to_i or use Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond). {{< /faq >}}

{{< faq question="What time format is best for APIs?" >}} ISO 8601, produced by Time.now.utc.iso8601 — "2026-06-28T12:30:45Z". It's unambiguous, sortable, and widely supported. {{< /faq >}}

{{< faq question="How do I handle user-input dates in different formats?" >}} Use Date.parse for flexible parsing, or Date.strptime with an explicit format string. For web apps, standardize on ISO 8601 and validate with regex. {{< /faq >}}

{{< faq question="Is it safe to do date arithmetic across DST boundaries?" >}} Time can be ambiguous during DST transitions. Date and DateTime avoid this by working with calendar days. For Time, prefer UTC for arithmetic and convert for display. {{< /faq >}}

Try It Yourself

# date_time_demo.rb

require "date"

def format_relative(date)
  days = (Date.today - date).to_i
  case days
  when 0 then "Today"
  when 1 then "Yesterday"
  when 2..7 then "#{days} days ago"
  when 8..30 then "#{(days / 7).floor} weeks ago"
  when 31..365 then "#{(days / 30).floor} months ago"
  else "#{(days / 365).floor} years ago"
  end
end

dates = [
  Date.today,
  Date.today - 1,
  Date.today - 5,
  Date.today - 30,
  Date.today - 365,
  Date.new(2020, 1, 1)
]

dates.each do |d|
  puts "#{d} => #{format_relative(d)}"
end

# Event scheduling
event = DateTime.new(2026, 7, 4, 18, 0, 0, "+00:00")
now = DateTime.now
puts "Event: #{event}"
puts "Now: #{now}"
puts "Countdown: #{(event - now).to_f * 24 * 60} minutes"

What's Next

Now that you understand date and time handling, learn about Marshal Serialization for saving Ruby objects to disk.

Topic Description Link
Ruby Marshal Serialization Object persistence {{< ref "23-marshal-serialization" >}}
Ruby Logging Debug output, log levels {{< ref "24-logging" >}}
Python datetime Compare Python's datetime module Python

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro