# frozen_string_literal: true module FmRest module V1 module Dates FM_DATE_FORMAT = "%m/%d/%Y" FM_DATETIME_FORMAT = "#{FM_DATE_FORMAT} %H:%M:%S" FM_DATETIME_FORMAT_MATCHER = /MM|mm|dd|HH|ss|yyyy/.freeze FM_DATE_TO_STRPTIME_SUBSTITUTIONS = { "MM" => "%m", "dd" => "%d", "yyyy" => "%Y", "HH" => "%H", "mm" => "%M", "ss" => "%S" }.freeze FM_DATE_TO_REGEXP_SUBSTITUTIONS = { "MM" => '(?:0[1-9]|1[012])', "dd" => '(?:0[1-9]|[12][0-9]|3[01])', "yyyy" => '\d{4}', "HH" => '(?:[01]\d|2[0123])', "mm" => '[0-5]\d', "ss" => '[0-5]\d' }.freeze def self.extended(mod) mod.instance_eval do @date_strptime = {} @date_regexp = {} end end # Converts a FM date-time format to `DateTime.strptime` format # # @param fm_format [String] The FileMaker date-time format # @return [String] The `DateTime.strpdate` equivalent of the given FM # date-time format def fm_date_to_strptime_format(fm_format) @date_strptime[fm_format] ||= fm_format.gsub(FM_DATETIME_FORMAT_MATCHER, FM_DATE_TO_STRPTIME_SUBSTITUTIONS).freeze end # Converts a FM date-time format to a Regexp. This is mostly used a # quicker way of checking whether a FM field is a date field than # Date|DateTime.strptime # # @param fm_format [String] The FileMaker date-time format # @return [Regexp] A reegular expression matching strings in the given FM # date-time format def fm_date_to_regexp(fm_format) @date_regexp[fm_format] ||= Regexp.new('\A' + fm_format.gsub(FM_DATETIME_FORMAT_MATCHER, FM_DATE_TO_REGEXP_SUBSTITUTIONS) + '\Z').freeze end # Takes a DateTime dt, and returns the correct local offset for that dt, # daylight savings included, in fraction of a day. # # By default, if ActiveSupport's `Time.zone` is set it will be used # instead of the system timezone. # # @param dt [DateTime] The DateTime to get the offset for # @param zone [nil, String, TimeZone] The timezone to use to calculate # the offset (defaults to system timezone, or ActiveSupport's Time.zone # if set) # @return [Rational] The offset in fraction of a day def local_offset_for_datetime(dt, zone = nil) dt = dt.new_offset(0) time = ::Time.utc(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec) # Do we have ActiveSupport's TimeZone? time = if time.respond_to?(:in_time_zone) time.in_time_zone(zone || ::Time.zone) else time.localtime end Rational(time.utc_offset, 86400) # seconds in one day (24*60*60) end # Returns a list of all datetime classes recognized by fmrest-ruby, # including `FmRest::StringDateTime` if defined. Useful for using in a # `case .. when` statement. # def datetime_classes [DateTime, Time, defined?(FmRest::StringDateTime) && FmRest::StringDateTime].compact.freeze end # Returns a list of all date classes recognized by fmrest-ruby, including # `FmRest::StringDate` if defined. Useful for using in a `case .. when` # statement. # def date_classes [Date, defined?(FmRest::StringDate) && FmRest::StringDate].compact.freeze end # Converts the given DateTime dt to the specified timezone setting offset, # leaving everything else intact. # # @param dt [DateTime] The datetime to convert # @param timezone [nil, Symbol, String] Accepted values are `:utc`, # `:local`, or `nil` (in which case it leaves the given datetime intact) # @return [DateTime] A new datetime with converted timezone def convert_datetime_timezone(dt, timezone) case timezone when :utc, "utc" dt.new_offset(0) when :local, "local" dt.new_offset(FmRest::V1.local_offset_for_datetime(dt)) when nil dt end end end end end