Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bae5812
Replace Date and DateTime classes from C to Ruby.
jinroq Feb 11, 2026
0cdfd42
Put `# encoding: US-ASCII` at the beginning.
jinroq Feb 15, 2026
802e0c2
Optimized methods
jinroq Feb 15, 2026
d4aa1aa
Optimized some methods.
jinroq Feb 22, 2026
8909ef5
Rewrite Date and DateTime from C extension to pure Ruby
jinroq Feb 25, 2026
d276c80
Auto-generate lib/date/zonetab.rb from ext/date/zonetab.list
jinroq Mar 1, 2026
2b938aa
Remove redundant .encode(Encoding::US_ASCII).freeze from constants.rb
jinroq Mar 1, 2026
fb37439
Optimize Date.new by delegating to Date.civil directly
jinroq Mar 2, 2026
ea0355e
Merge branch 'master' into replace_c_to_ruby
jinroq Mar 2, 2026
a65aca6
Refactor getbyte to StringScanner, and optimize strptime
jinroq Mar 3, 2026
7e80db9
Remove inlined civil_to_jd and other duplicated method bodies
jinroq Mar 4, 2026
a9255c0
Replace Rational() method calls with rational literals for performance
jinroq Mar 7, 2026
aa536c6
Merge branch 'master' into replace_c_to_ruby
jinroq Jun 6, 2026
1b3b1ef
Address review feedback: stricter arg checks, drop debug-only methods…
jinroq Jun 6, 2026
fd03c6f
Inline idiv helper and remove unused Date#initialize
jinroq Jun 6, 2026
997de16
Delegate comparison to Comparable, simplify deconstruct_keys, drop yd…
jinroq Jun 6, 2026
e2698ea
Access day fraction via #day_fraction instead of @df in Date
jinroq Jun 6, 2026
a539110
Rename @sg/@df ivars to @start/@day_fraction
jinroq Jun 6, 2026
c211307
Fix Date#+/#- Integer path to preserve shape and day fraction
jinroq Jun 6, 2026
e67f196
Normalize Date::Infinity start to Float in marshal_load
jinroq Jun 6, 2026
5bcb504
Remove unnecessary `strip!` on offset expression in zonetab generator
jinroq Jun 16, 2026
d80a88b
Simplify zonetab sort to Hash#sort
jinroq Jun 16, 2026
382bb9a
Use printf with %p in zonetab generator key formatting
jinroq Jun 17, 2026
a6abfc1
Use File.foreach chomp option in zonetab generator
jinroq Jun 17, 2026
7c04350
Simplify section tracking in zonetab generator
jinroq Jun 17, 2026
9e9d45a
Accept input/output paths as arguments in zonetab generator
jinroq Jun 17, 2026
e880620
Build abbr 3-key tables with shared compute_3key helper
jinroq Jun 17, 2026
20198e3
Remove unused lib/date/patterns.rb
jinroq Jun 17, 2026
d0b25b2
Move file-local flag constants out of constants.rb
jinroq Jun 17, 2026
c9b2175
Drop the C extension; pure Ruby implementation only (Ruby >= 3.3)
jinroq Jun 17, 2026
f6a2947
Fix calendar-reform validation in civil/ordinal/commercial
jinroq Jun 20, 2026
3773d98
Return Float for Date#start to match the C extension
jinroq Jun 20, 2026
6786840
Remove unused require and dead constant
jinroq Jun 20, 2026
4e11ebb
Implement DateTime#inspect to match the C extension
jinroq Jun 20, 2026
4a2bf55
Fix DateTime month/year arithmetic, new_offset and negative-year %y
jinroq Jun 20, 2026
9131c63
Use String#getbyte in compute_3key to avoid array allocation
jinroq Jun 30, 2026
8795e36
Merge branch 'master' into replace_c_to_ruby
jinroq Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
echo "diff=true" >> $GITHUB_OUTPUT
- name: Commit
run: |
git commit --message="Update zonetab.h at $(date +%F)" ext/date
git commit --message="Update zonetab at $(date +%F)" ext/date lib/date/zonetab.rb
git pull --ff-only origin ${GITHUB_REF#refs/heads/}
git push origin ${GITHUB_REF#refs/heads/}
env:
Expand Down
61 changes: 39 additions & 22 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
require "bundler/gem_tasks"
require "rake/testtask"
require "shellwords"
require "rake/extensiontask"

extask = Rake::ExtensionTask.new("date") do |ext|
ext.name = "date_core"
ext.lib_dir.sub!(%r[(?=/|\z)], "/#{RUBY_VERSION}/#{ext.platform}")
end
if RUBY_VERSION >= "3.3"
# Pure Ruby — no compilation needed
Rake::TestTask.new(:test) do |t|
t.libs << "lib"
t.libs << "test/lib"
t.ruby_opts << "-rhelper"
t.test_files = FileList['test/**/test_*.rb']
end

Rake::TestTask.new(:test) do |t|
t.libs << extask.lib_dir
t.libs << "test/lib"
t.ruby_opts << "-rhelper"
t.test_files = FileList['test/**/test_*.rb']
end
task :compile # no-op

else
# C extension for Ruby < 3.3
require "shellwords"
require "rake/extensiontask"

extask = Rake::ExtensionTask.new("date") do |ext|
ext.name = "date_core"
ext.lib_dir.sub!(%r[(?=/|\z)], "/#{RUBY_VERSION}/#{ext.platform}")
end

Rake::TestTask.new(:test) do |t|
t.libs << extask.lib_dir
t.libs << "test/lib"
t.ruby_opts << "-rhelper"
t.test_files = FileList['test/**/test_*.rb']
end

task test: :compile

task compile: "ext/date/zonetab.h"
file "ext/date/zonetab.h" => "ext/date/zonetab.list" do |t|
dir, hdr = File.split(t.name)
make_program_name =
ENV['MAKE'] || ENV['make'] ||
RbConfig::CONFIG['configure_args'][/with-make-prog\=\K\w+/] ||
(/mswin/ =~ RUBY_PLATFORM ? 'nmake' : 'make')
make_program = Shellwords.split(make_program_name)
sh(*make_program, "-f", "prereq.mk", "top_srcdir=.."+"/.."*dir.count("/"),
hdr, chdir: dir)
task compile: "ext/date/zonetab.h"
file "ext/date/zonetab.h" => "ext/date/zonetab.list" do |t|
dir, hdr = File.split(t.name)
make_program_name =
ENV['MAKE'] || ENV['make'] ||
RbConfig::CONFIG['configure_args'][/with-make-prog\=\K\w+/] ||
(/mswin/ =~ RUBY_PLATFORM ? 'nmake' : 'make')
make_program = Shellwords.split(make_program_name)
sh(*make_program, "-f", "prereq.mk", "top_srcdir=.."+"/.."*dir.count("/"),
hdr, chdir: dir)
end
end

task :default => [:compile, :test]
24 changes: 7 additions & 17 deletions date.gemspec
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
# frozen_string_literal: true

version = File.foreach(File.expand_path("../lib/date.rb", __FILE__)).find do |line|
/^\s*VERSION\s*=\s*["'](.*)["']/ =~ line and break $1
end
require_relative "lib/date/version"

Gem::Specification.new do |s|
s.name = "date"
s.version = version
s.version = Date::VERSION
s.summary = "The official date library for Ruby."
s.description = "The official date library for Ruby."

if Gem::Platform === s.platform and s.platform =~ 'java' or RUBY_ENGINE == 'jruby'
s.platform = 'java'
# No files shipped, no require path, no-op for now on JRuby
else
s.require_path = %w{lib}
s.require_path = %w{lib}

s.files = [
"README.md", "COPYING", "BSDL",
"lib/date.rb", "ext/date/date_core.c", "ext/date/date_parse.c", "ext/date/date_strftime.c",
"ext/date/date_strptime.c", "ext/date/date_tmx.h", "ext/date/extconf.rb", "ext/date/prereq.mk",
"ext/date/zonetab.h", "ext/date/zonetab.list"
]
s.extensions = "ext/date/extconf.rb"
end
s.files = Dir["README.md", "COPYING", "BSDL", "lib/**/*.rb",
"ext/date/*.c", "ext/date/*.h", "ext/date/extconf.rb",
"ext/date/prereq.mk", "ext/date/zonetab.list"]
s.extensions = ["ext/date/extconf.rb"]

s.required_ruby_version = ">= 2.6.0"

Expand Down
21 changes: 12 additions & 9 deletions ext/date/extconf.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# frozen_string_literal: true
require 'mkmf'

config_string("strict_warnflags") {|w| $warnflags += " #{w}"}

append_cflags("-Wno-compound-token-split-by-macro") if RUBY_VERSION < "2.7."
have_func("rb_category_warn")
with_werror("", {:werror => true}) do |opt, |
have_var("timezone", "time.h", opt)
have_var("altzone", "time.h", opt)
if RUBY_VERSION >= "3.3"
# Pure Ruby implementation; skip C extension build
File.write("Makefile", dummy_makefile($srcdir).join(""))
else
config_string("strict_warnflags") {|w| $warnflags += " #{w}"}
append_cflags("-Wno-compound-token-split-by-macro") if RUBY_VERSION < "2.7."
have_func("rb_category_warn")
with_werror("", {:werror => true}) do |opt, |
have_var("timezone", "time.h", opt)
have_var("altzone", "time.h", opt)
end
create_makefile('date_core')
end

create_makefile('date_core')
59 changes: 59 additions & 0 deletions ext/date/generate-zonetab-rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- mode: ruby -*-
# Generate lib/date/zonetab.rb from ext/date/zonetab.list
#
# Usage: ruby -C ext/date generate-zonetab-rb

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Usage: ruby -C ext/date generate-zonetab-rb
# Usage: ruby ext/date/generate-zonetab-rb ext/date/zonetab.list lib/date/zonetab.rb

#
# This script reads zonetab.list (the same source used to generate zonetab.h
# via gperf) and produces the equivalent Ruby hash table.

list_path = File.join(__dir__, 'zonetab.list')
output_path = File.join(__dir__, '..', '..', 'lib', 'date', 'zonetab.rb')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
list_path = File.join(__dir__, 'zonetab.list')
output_path = File.join(__dir__, '..', '..', 'lib', 'date', 'zonetab.rb')
list_path, output_path = ARGV


entries = {}
in_entries = false

File.foreach(list_path) do |line|
line.chomp!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
File.foreach(list_path) do |line|
line.chomp!
File.foreach(list_path, chomp: true) do |line|

if line == '%%'
if in_entries
break
else
in_entries = true
next
end
end
next unless in_entries

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
in_entries = false
File.foreach(list_path) do |line|
line.chomp!
if line == '%%'
if in_entries
break
else
in_entries = true
next
end
end
next unless in_entries
sections = 0
File.foreach(list_path) do |line|
line.chomp!
break if line == '%%' and (sections += 1) > 1
next if sections < 1


abbr, offset_expr = line.split(',', 2)
next unless offset_expr

abbr.strip!
offset_expr.strip!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to strip, as eval skips leading/trailing spaces.

Suggested change
offset_expr.strip!


# Evaluate offset expression (e.g., "0*3600", "-5*3600", "-(1*3600+1800)", "16200")
offset = eval(offset_expr)

entries[abbr] = offset
end

sorted = entries.sort_by { |k, _| k }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sorted = entries.sort_by { |k, _| k }
sorted = entries.sort


max_key_len = sorted.map { |k, _| k.length }.max

File.open(output_path, 'w') do |f|
f.puts '# frozen_string_literal: true'
f.puts
f.puts '# Timezone name => UTC offset (seconds) mapping table.'
f.puts '# Auto-generated from ext/date/zonetab.list by ext/date/generate-zonetab-rb.'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
f.puts '# Auto-generated from ext/date/zonetab.list by ext/date/generate-zonetab-rb.'
f.puts "# Auto-generated from #{list_path} by ext/date/generate-zonetab-rb."

f.puts '# Do not edit manually.'
f.puts 'class Date'
f.puts ' ZONE_TABLE = {'
sorted.each do |abbr, offset|
key = abbr.include?(' ') ? %Q("#{abbr}") : %Q("#{abbr}")
f.puts " %-#{max_key_len + 3}s => %d," % [key, offset]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both sides look the same expression.

Suggested change
key = abbr.include?(' ') ? %Q("#{abbr}") : %Q("#{abbr}")
f.puts " %-#{max_key_len + 3}s => %d," % [key, offset]
f.printf " %-*p => %d,\n", max_key_len + 3, abbr, offset

end
f.puts ' }.freeze'
f.puts 'end'
end

puts "Generated #{output_path} with #{entries.size} entries."
1 change: 1 addition & 0 deletions ext/date/prereq.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ zonetab.h: zonetab.list
.PHONY: update-zonetab
update-zonetab:
$(RUBY) -C $(srcdir) update-abbr
$(RUBY) -C $(srcdir) generate-zonetab-rb

.PHONY: update-nothing
update-nothing:
Expand Down
21 changes: 16 additions & 5 deletions lib/date.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
# frozen_string_literal: true
# date.rb: Written by Tadayoshi Funaba 1998-2011

require 'date_core'
require 'timeout'
require 'strscan'

class Date
VERSION = "3.5.1" # :nodoc:
if RUBY_VERSION >= "3.3"
require_relative "date/version"
require_relative "date/constants"
require_relative "date/shared"
require_relative "date/core"
require_relative "date/strftime"
require_relative "date/parse"
require_relative "date/strptime"
require_relative "date/time"
require_relative "date/datetime"
else
require 'date_core'
end

class Date
# call-seq:
# infinite? -> false
#
Expand Down Expand Up @@ -64,7 +77,5 @@ def to_f
-Float::INFINITY
end
end

end

end
Loading