Convert epoch to date/time within JSON.mapping

class Test

  ::DB.mapping({

    first_time: {type: Int32?},

    })

  ::JSON.mapping({

    first_time: {type: Int32?},

    })
end

get "/" do |env|

  tested = ::DB.db.query_all("SELECT first_time FROM test_table", as: Test)

  tested.to_json
  #tested.map(&.inspect).join("\n")

end

First_time from test_table is an integer which contains an epoch. Is there anyway I can convert the value into a date/time format within the JSON.mapping? If not which other options do I have to convert before the to_json action?

https://crystal-lang.org/api/0.29.0/Time/EpochConverter.html

Tried this

require "json"
require "kemal"
require "./db"

class Device

  ::DB.mapping({

    first_time: {type: Time, converter: Time::EpochConverter},

    })

  ::JSON.mapping({

   first_time: {type: Time, converter: Time::EpochConverter},

    })
end

get "/" do |env|

  devices = Devices::DB.db.query_all("SELECT first_time FROM devices", as: Device)

  devices.to_json
  #devices.map(&.inspect).join("\n")

end

Kemal.run

Got this in return on compiling

Error in src/devices.cr:22: instantiating 'DB::Database#query_all(String)'

  devices = Devices::DB.db.query_all("SELECT first_time FROM devices", as: Device)
                           ^~~~~~~~~

in lib/db/src/db/query_methods.cr:241: instantiating 'DB::ResultSet+#read(Device.class)'

        rs.read(type)
           ^~~~

in lib/db/src/db/result_set.cr:74: instantiating 'Device.class#new(DB::ResultSet+)'

      type.new(self)
           ^~~

in src/devices.cr:7: expanding macro

  ::DB.mapping({
  ^

in macro 'mapping' /root/crystal/hello-world/lib/db/src/db/mapping.cr:60, line 41:

   1.     include ::DB::Mappable
   2. 
   3.     
   4.       
   5.     
   6. 
   7.     
   8.       
   9. 
  10.       
  11.     
  12. 
  13.     
  14.       @first_time : Time 
  15. 
  16.       def first_time=(_first_time : Time )
  17.         @first_time = _first_time
  18.       end
  19. 
  20.       def first_time
  21.         @first_time
  22.       end
  23.     
  24. 
  25.     def self.from_rs(__temp_52 : ::DB::ResultSet)
  26.       __temp_53 = Array(self).new
  27.       __temp_52.each do
  28.         __temp_53 << self.new(__temp_52)
  29.       end
  30.       __temp_53
  31.     ensure
  32.       __temp_52.close
  33.     end
  34. 
  35.     def initialize(__temp_52 : ::DB::ResultSet)
  36.       
  37.         __temp_54 = nil
  38.         __temp_55 = false
  39.       
  40. 
> 41.       __temp_52.each_column do |col_name|
  42.         case col_name
  43.           
  44.             when "first_time"
  45.               __temp_55 = true
  46.               __temp_54 =
  47.                 
  48.                   Time::EpochConverter.from_rs(__temp_52)
  49.                 
  50.           
  51.           else
  52.             
  53.               raise ::DB::MappingException.new("unknown result set attribute: #{col_name}")
  54.             
  55.         end
  56.       end
  57. 
  58.       
  59.         
  60.           if __temp_54.is_a?(Nil) && !__temp_55
  61.             raise ::DB::MappingException.new("missing result set attribute: first_time")
  62.           end
  63.         
  64.       
  65. 
  66.       
  67.         
  68.           @first_time = __temp_54.as(Time)
  69.         
  70.       
  71.     end
  72.   

instantiating 'DB::ResultSet+#each_column()'
in src/devices.cr:7: expanding macro

  ::DB.mapping({
  ^

in macro 'mapping' /root/crystal/hello-world/lib/db/src/db/mapping.cr:60, line 41:

   1.     include ::DB::Mappable
   2. 
   3.     
   4.       
   5.     
   6. 
   7.     
   8.       
   9. 
  10.       
  11.     
  12. 
  13.     
  14.       @first_time : Time 
  15. 
  16.       def first_time=(_first_time : Time )
  17.         @first_time = _first_time
  18.       end
  19. 
  20.       def first_time
  21.         @first_time
  22.       end
  23.     
  24. 
  25.     def self.from_rs(__temp_52 : ::DB::ResultSet)
  26.       __temp_53 = Array(self).new
  27.       __temp_52.each do
  28.         __temp_53 << self.new(__temp_52)
  29.       end
  30.       __temp_53
  31.     ensure
  32.       __temp_52.close
  33.     end
  34. 
  35.     def initialize(__temp_52 : ::DB::ResultSet)
  36.       
  37.         __temp_54 = nil
  38.         __temp_55 = false
  39.       
  40. 
> 41.       __temp_52.each_column do |col_name|
  42.         case col_name
  43.           
  44.             when "first_time"
  45.               __temp_55 = true
  46.               __temp_54 =
  47.                 
  48.                   Time::EpochConverter.from_rs(__temp_52)
  49.                 
  50.           
  51.           else
  52.             
  53.               raise ::DB::MappingException.new("unknown result set attribute: #{col_name}")
  54.             
  55.         end
  56.       end
  57. 
  58.       
  59.         
  60.           if __temp_54.is_a?(Nil) && !__temp_55
  61.             raise ::DB::MappingException.new("missing result set attribute: first_time")
  62.           end
  63.         
  64.       
  65. 
  66.       
  67.         
  68.           @first_time = __temp_54.as(Time)
  69.         
  70.       
  71.     end
  72.   

instantiating 'DB::ResultSet+#each_column()'
in src/devices.cr:7: expanding macro

  ::DB.mapping({
  ^

in macro 'mapping' /root/crystal/hello-world/lib/db/src/db/mapping.cr:60, line 48:

   1.     include ::DB::Mappable
   2. 
   3.     
   4.       
   5.     
   6. 
   7.     
   8.       
   9. 
  10.       
  11.     
  12. 
  13.     
  14.       @first_time : Time 
  15. 
  16.       def first_time=(_first_time : Time )
  17.         @first_time = _first_time
  18.       end
  19. 
  20.       def first_time
  21.         @first_time
  22.       end
  23.     
  24. 
  25.     def self.from_rs(__temp_52 : ::DB::ResultSet)
  26.       __temp_53 = Array(self).new
  27.       __temp_52.each do
  28.         __temp_53 << self.new(__temp_52)
  29.       end
  30.       __temp_53
  31.     ensure
  32.       __temp_52.close
  33.     end
  34. 
  35.     def initialize(__temp_52 : ::DB::ResultSet)
  36.       
  37.         __temp_54 = nil
  38.         __temp_55 = false
  39.       
  40. 
  41.       __temp_52.each_column do |col_name|
  42.         case col_name
  43.           
  44.             when "first_time"
  45.               __temp_55 = true
  46.               __temp_54 =
  47.                 
> 48.                   Time::EpochConverter.from_rs(__temp_52)
  49.                 
  50.           
  51.           else
  52.             
  53.               raise ::DB::MappingException.new("unknown result set attribute: #{col_name}")
  54.             
  55.         end
  56.       end
  57. 
  58.       
  59.         
  60.           if __temp_54.is_a?(Nil) && !__temp_55
  61.             raise ::DB::MappingException.new("missing result set attribute: first_time")
  62.           end
  63.         
  64.       
  65. 
  66.       
  67.         
  68.           @first_time = __temp_54.as(Time)
  69.         
  70.       
  71.     end
  72.   

undefined method 'from_rs' for Time::EpochConverter:Module

Where do I go wrong?

It’s only for JSON/YAML.mapping.

I’d maybe look into using an ORM that would handle this stuff for you.

EDIT: It looks like DB.mapping also supports converters. Should be able to add a from_rs method to the Time::EpochConverter module so it would also work for DB.mapping. Probably something like

module Time::EpochConverter
  def self.from_rs(result_set : DB::ResultSet) : Time
    Time.unix result_set.read(Int32)
  end
end

Something like that maybe? @miessos

Will do, thank you for your assistance.

Which database driver are you using? Maybe such a converter should be added to the driver. Or maybe it’s already there?