// bdlt_fixutil.h -*-C++-*- #ifndef INCLUDED_BDLT_FIXUTIL #define INCLUDED_BDLT_FIXUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide conversions between date/time objects and FIX strings. // //@CLASSES: // bdlt::FixUtil: namespace for FIX date/time conversion functions // //@SEE_ALSO: bdlt_fixutilconfiguration // //@DESCRIPTION: This component provides a namespace, 'bdlt::FixUtil', // containing functions that convert 'bdlt' date, time, and datetime objects to // and from ("generate" and "parse", respectively) corresponding string // representations that are compliant with the FIX standard. The version of // the FIX standard that is the basis for this component can be found at: //.. // http://www.fixtradingcommunity.org/FIXimate/FIXimate3.0/latestEP/en/ // FIX.5.0SP2_EP208/fix_datatypes.html //.. // In general terms, 'FixUtil' functions support what FIX refers to as // *complete* *representations* in *extended* *format*. We first present a // brief overview before delving into the details of the FIX representations // that are supported for each of the relevant 'bdlt' vocabulary types. // // Each function that *generates* FIX strings (named 'generate' and // 'generateRaw') takes a 'bdlt' object and a 'char *' buffer, 'bsl::string', // or 'bsl::ostream', and writes a FIX representation of the object to the // buffer, string, or stream. The "raw" functions are distinguished from their // non-"raw" counterparts in three respects: // //: o The length of the 'char *' buffer is not supplied to the 'generateRaw' //: functions. //: //: o The 'generateRaw' functions do not output a null terminator. //: //: o The 'generate' functions that provide an 'int bufferLength' parameter //: truncate the generated output to 'bufferLength' characters. (Neither the //: 'generateRaw' functions nor the 'generate' functions taking 'bsl::string' //: or 'bsl::ostream' do any truncation of their generated output.) // // Since the generate functions always succeed, no status value is returned. // Instead, either the number of characters output to the 'char *' buffer or // string, or a reference to the stream, is returned. (Note that the // generating functions also take an optional 'bdlt::FixUtilConfiguration' // object, which is discussed shortly.) // // Each function that *parses* FIX strings (named 'parse') take the address of // a target 'bdlt' object and a 'const char *' (paired with a 'length' // argument) or 'bsl::string_view', and loads the object with the result of // parsing the character string. Since parsing can fail, the parse functions // return an 'int' status value (0 for success and a non-zero value for // failure). Note that, besides elementary syntactical considerations, the // validity of parsed strings are subject to the semantic constraints imposed // by the various 'isValid*' class methods (i.e., 'Date::isValidYearMonthDay', // 'Time::isValid', etc.). // ///Terminology ///----------- // As this component concerns FIX, some terms from that specification are used // liberally in what follows. Two FIX terms of particular note are *timezone* // *offset* and *fractional* *second*. // // A FIX *timezone* *offset* corresponds to what other 'bdlt' components // commonly refer to as a timezone offset (or simply as an offset; e.g., see // 'bdlt_datetimetz'). For example, the FIX string '20020317-15:46:00+04:00' // has a timezone offset of '+04:00', indicating a timezone 4 hours ahead of // UTC. // // A FIX *fractional* *second* corresponds to, for example, combined // 'millisecond' and 'microsecond' attributes of a 'bdlt::Datetime' or // 'bdlt::Time' object. For example, the 'Time' value (and FIX string) // '15:46:09.330' has a 'millisecond' attribute value of 330; i.e., a // fractional second of .33. // ///FIX String Generation ///--------------------- // Strings produced by the 'generate' and 'generateRaw' functions are a // straightforward transposition of the attributes of the source 'bdlt' value // into an appropriate FIX format, and are best illustrated by a few examples. // Note that for 'Datetime', 'DatetimeTz', and 'Time', the fractional second is // generated with the precision specified in the configuration. Also note that // for 'TimeTz', no fractional second is generated (as per the FIX // specification for "TZTimeOnly"). //.. // +--------------------------------------+---------------------------------+ // | Object Value | Generated FIX String | // | | (using default configuration) | // +======================================+=================================+ // | Date(2002, 03, 17) | 20020317 | // +--------------------------------------+---------------------------------+ // | Time(15, 46, 09, 330) | 15:46:09.330 | // +--------------------------------------+---------------------------------+ // | Datetime(Date(2002, 03, 17) | | // | Time(15, 46, 09, 330)) | 20020317-15:46:09.330 | // +--------------------------------------+---------------------------------+ // | DateTz(Date(2002, 03, 17), -120) | 20020317-02:00 | // +--------------------------------------+---------------------------------+ // | TimeTz(Time(15, 46, 09, 330), 270) | 15:46:09+04:30 | // +--------------------------------------+---------------------------------+ // | DatetimeTz(Datetime( | | // | Date(2002, 03, 17), | | // | Time(15, 46, 09, 330)), | | // | 0) | 20020317-15:46:09.330+00:00 | // +--------------------------------------+---------------------------------+ //.. // Note that the FIX specification does not have an equivalent to // 'bdlt::DateTz'. // ///Configuration ///- - - - - - - // The 'generate' and 'generateRaw' functions provide an optional configuration // parameter. This optional parameter, of type 'FixUtilConfiguration', enables // configuration of two aspects of FIX string generation: // //: o The precision of the fractional seconds. //: //: o Whether 'Z' is output for the timezone offset instead of '+00:00' (UTC). // // 'FixUtilConfiguration' has two attributes that directly correspond to these // aspects. In addition, for generate methods that are not supplied with a // configuration argument, a process-wide configuration takes effect. See // 'bdlt_fixutilconfiguration' for details. // ///FIX String Parsing ///------------------ // The parse functions accept *all* strings that are produced by the generate // functions. In addition, the parse functions accept some variation in the // generated strings, the details of which are discussed next. Note that the // parse methods are not configurable like the generate methods (i.e., via an // optional 'FixUtilConfiguration' argument). Moreover, the process-wide // configuration has no effect on parsing either. Instead, the parse methods // automatically treat '+00:00' and 'Z' as equivalent timezone offsets (both // denoting UTC). Finally, the parsing allows seconds to be optionally // specified in all types, which is in contradiction to some of the types in // the referenced FIX protocol specification. // ///Timezone Offsets /// - - - - - - - - // The timezone offset is optional, and can be present when parsing for *any* // type, i.e., even for 'Date', 'Time', and 'Datetime'. If a timezone offset // is parsed for a 'Date', it must be valid, so it can affect the status value // that is returned in that case, but it is otherwise ignored. For 'Time' and // 'Datetime', any timezone offset present in the parsed string will affect the // resulting object value (unless the timezone offset denotes UTC) because the // result is converted to UTC. If the timezone offset is absent, it is treated // as if '+00:00' were specified: //.. // +------------------------------------+-----------------------------------+ // | Parsed FIX String | Result Object Value | // +====================================+===================================+ // | 20020317-02:00 | Date(2002, 03, 17) | // | | # timezone offset ignored | // +------------------------------------+-----------------------------------+ // | 20020317-02:65 | Date: parsing fails | // | | # invalid timezone offset | // +------------------------------------+-----------------------------------+ // | 15:46:09.330+04:30 | Time(11, 16, 09, 330) | // | | # converted to UTC | // +------------------------------------+-----------------------------------+ // | 15:46:09.330+04:30 | TimeTz(Time(15, 46, 09, 330), | // | | 270) | // +------------------------------------+-----------------------------------+ // | 15:46:09.330 | TimeTz(Time(15, 46, 09, 330), | // | | 0) | // | | # implied '+00:00' | // +------------------------------------+-----------------------------------+ // | 20020317-23:46:09.222-05:00 | Datetime(Date(2002, 03, 18), | // | | Time(04, 46, 09, 222)) | // | | # carry into 'day' attribute | // | | # when converted to UTC | // +------------------------------------+-----------------------------------+ //.. // In the last example above, the conversion to UTC incurs a carry into the // 'day' attribute of the 'Date' component of the resulting 'Datetime' value. // Note that if such a carry causes an underflow or overflow at the extreme // ends of the valid range of dates (0001/01/01 and 9999/12/31), then parsing // for 'Datetime' fails. // ///Fractional Seconds /// - - - - - - - - - // The fractional second is optional. When the fractional second is absent, it // is treated as if '.0' were specified. When the fractional second is // present, it can have one or more digits (in divergence with the referenced // FIX protocol document, which indicates the fractional second may be // unspecified or have a positive multiple of three digits). Although FIX has // provision for picosecond (or finer) time resolution, be aware that 'bdlt' is // limited to microsecond resolution. If more than six digits are included in // the fractional second, values are rounded to a full microsecond; i.e., // values greater than or equal to .5 microseconds are rounded up. These // roundings may incur a carry of one second into the 'second' attribute: //.. // +--------------------------------------+---------------------------------+ // | Parsed FIX String | Result Object Value | // +======================================+=================================+ // | 15:46:09.1 | Time(15, 46, 09, 100) | // +--------------------------------------+---------------------------------+ // | 15:46:09-05:00 | TimeTz(Time(15, 46, 09), -300) | // | | # implied '.0' | // +--------------------------------------+---------------------------------+ // | 15:46:09.99999949 | Time(15, 46, 09, 999, 999) | // | | # truncate last two digits | // +--------------------------------------+---------------------------------+ // | 15:46:09.9999995 | Time(15, 46, 10, 000) | // | | # round up and carry | // +--------------------------------------+---------------------------------+ //.. // Note that, for 'Datetime' and 'DatetimeTz', if a carry due to rounding of // the fractional second causes an overflow at the extreme upper end of the // valid range of dates (i.e., 9999/12/31), then parsing fails. // ///Leap Seconds /// - - - - - - // Leap seconds are not representable by 'bdlt::Time' or 'bdlt::Datetime'. // Hence, they are not produced by any of the 'FixUtil' generate functions. // However, positive leap seconds *are* supported by the parse functions. A // leap second is recognized when the value parsed for the 'second' attribute // of a 'Time' is 60 -- regardless of the values parsed for the 'hour', // 'minute', and 'millisecond' attributes. Note that this behavior is more // generous than that afforded by the FIX specification (which indicates that a // positive leap second can only be represented as "23:59:60Z"). // // When a leap second is detected during parsing of a FIX string, the 'second' // attribute is taken to be 59, so that the value of the 'Time' object can be // validly set; then an additional second is added to the object. Note that // the possible carry incurred by a leap second (i.e., when loading the result // of parsing into a 'Datetime' or 'DatetimeTz' object) has the same potential // for overflow as may occur with fractional seconds that are rounded up // (although in admittedly pathological cases). // ///The Time 24:00 /// - - - - - - - // Although 24:00 is *representable* by 'bdlt', i.e., as the default value for // 'bdlt::Time', "24:00:00.000" is *not* a valid string in the FIX protocol. // As per other methods acting upon 24:00 within 'bdlt', an 'hour' attribute // value of 24 is mapped to 0 by the generate functions provided by this // component: //.. // +------------------------------------+-----------------------------------+ // | Source Object Value | Generated FIX String | // +====================================+===================================+ // | Time(24, 0, 0, 0) | 00:00:00.000 | // +------------------------------------+-----------------------------------+ // | Datetime(Date(2002, 03, 17), | 20020317-00:00:00.000 | // | Time(24, 0, 0, 0)) | | // +------------------------------------+-----------------------------------+ //.. // Finally, a string representing 24:00 is rejected by the 'bdlt::FixUtil' // parse methods. // ///Summary of Supported FIX Representations ///- - - - - - - - - - - - - - - - - - - - // The syntax description below summarizes the FIX string representations // supported by this component. Although not quoted (for readability), // '[+-:.Z]' are literal characters that can occur in FIX strings. The // characters '[YMDhms]' each denote a decimal digit, '{}' brackets optional // elements, '()' is used for grouping, and '|' separates alternatives: //.. // <Generated Date> ::= <DATE> // // <Parsed Date> ::= <Parsed DateTz> // // <Generated DateTz> ::= <DATE><ZONE> // // <Parsed DateTz> ::= <DATE>{<ZONE>} // // <Generated Time> ::= <TIME FLEXIBLE> // // <Parsed Time> ::= <Parsed TimeTz> // // <Generated TimeTz> ::= <TIME FIXED><ZONE> // // <Parsed TimeTz> ::= <TIME FLEXIBLE>{<ZONE>} // // <Generated Datetime> ::= <DATE>-<TIME FLEXIBLE> // // <Parsed Datetime> ::= <Parsed DatetimeTz> // // <Generated DatetimeTz> ::= <DATE>-<TIME FLEXIBLE><ZONE> // // <Parsed DatetimeTz> ::= <DATE>-<TIME FLEXIBLE>{<ZONE>} // // <DATE> ::= YYYYMMDD // // <TIME FIXED> ::= hh:mm:ss // // <TIME FLEXIBLE> ::= hh:mm{:ss{.s+}} // // <ZONE> ::= ((+|-)hh{:mm})|Z # timezone offset, the colon // # and minute attribute are // # optional during parsing //.. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Basic 'bdlt::FixUtil' Usage /// - - - - - - - - - - - - - - - - - - - // This example demonstrates basic use of one 'generate' function and two // 'parse' functions. // // First, we construct a few objects that are prerequisites for this and the // following example: //.. // const bdlt::Date date(2005, 1, 31); // 2005/01/31 // const bdlt::Time time(8, 59, 59, 123); // 08:59:59.123 // const int tzOffset = 240; // +04:00 (four hours west of UTC) //.. // Then, we construct a 'bdlt::DatetimeTz' object for which a corresponding // FIX-compliant string will be generated shortly: //.. // const bdlt::DatetimeTz sourceDatetimeTz(bdlt::Datetime(date, time), // tzOffset); //.. // For comparison with the FIX string generated below, note that streaming the // value of 'sourceDatetimeTz' to 'stdout': //.. // bsl::cout << sourceDatetimeTz << bsl::endl; //.. // produces: //.. // 31JAN2005_08:59:59.123000+0400 //.. // Next, we use a 'generate' function to produce a FIX-compliant string for // 'sourceDatetimeTz', writing the output to a 'bsl::ostringstream', and assert // that both the return value and the string that is produced are as expected: //.. // bsl::ostringstream oss; // const bsl::ostream& ret = bdlt::FixUtil::generate(oss, sourceDatetimeTz); // assert(&oss == &ret); // // const bsl::string fix = oss.str(); // assert(fix == "20050131-08:59:59.123+04:00"); //.. // For comparison, see the output that was produced by the streaming operator // above. // // Now, we parse the string that was just produced, loading the result of the // parse into a second 'bdlt::DatetimeTz' object, and assert that the parse was // successful and that the target object has the same value as that of the // original (i.e., 'sourceDatetimeTz'): //.. // bdlt::DatetimeTz targetDatetimeTz; // // int rc = bdlt::FixUtil::parse(&targetDatetimeTz, // fix.c_str(), // static_cast<int>(fix.length())); // assert( 0 == rc); // assert(sourceDatetimeTz == targetDatetimeTz); //.. // Finally, we parse the 'fix' string a second time, this time loading the // result into a 'bdlt::Datetime' object (instead of a 'bdlt::DatetimeTz'): //.. // bdlt::Datetime targetDatetime; // // rc = bdlt::FixUtil::parse(&targetDatetime, // fix.c_str(), // static_cast<int>(fix.length())); // assert( 0 == rc); // assert(sourceDatetimeTz.utcDatetime() == targetDatetime); //.. // Note that this time the value of the target object has been converted to // UTC. // ///Example 2: Configuring FIX String Generation ///- - - - - - - - - - - - - - - - - - - - - - // This example demonstrates use of a 'bdlt::FixUtilConfiguration' object to // influence the format of the FIX strings that are generated by this component // by passing that configuration object to 'generate'. We also take this // opportunity to illustrate the flavor of the 'generate' functions that // outputs to a 'char *' buffer of a specified length. // // First, we construct the 'bdlt::FixUtilConfiguration' object that indicates // how we would like to affect the generated output FIX string. In this case, // we want to have microsecond precision displayed: //.. // bdlt::FixUtilConfiguration configuration; // // configuration.setFractionalSecondPrecision(6); //.. // Then, we define the 'char *' buffer that will be used to stored the // generated string. A buffer of size 'bdlt::FixUtil::k_DATETIMETZ_STRLEN + 1' // is large enough to hold any string generated by this component for a // 'bdlt::DatetimeTz' object, including a null terminator: //.. // const int BUFLEN = bdlt::FixUtil::k_DATETIMETZ_STRLEN + 1; // char buffer[BUFLEN]; //.. // Next, we use a 'generate' function that accepts our 'configuration' to // produce a FIX-compliant string for 'sourceDatetimeTz', this time writing the // output to a 'char *' buffer, and assert that both the return value and the // string that is produced are as expected. Note that in comparing the return // value against 'BUFLEN - 1' we account for the fact that, although a null // terminator was generated, it is not included in the character count returned // by 'generate'. Also note that we use 'bsl::strcmp' to compare the resulting // string knowing that we supplied a buffer having sufficient capacity to // accommodate a null terminator: //.. // rc = bdlt::FixUtil::generate(buffer, // BUFLEN, // sourceDatetimeTz, // configuration); // assert(BUFLEN - 1 == rc); // assert( 0 == bsl::strcmp(buffer, // "20050131-08:59:59.123000+04:00")); //.. // For comparison, see the output that was produced by the streaming operator // above. // // Next, we parse the string that was just produced, loading the result of the // parse into a second 'bdlt::DatetimeTz' object, and assert that the parse was // successful and that the target object has the same value as that of the // original (i.e., 'sourceDatetimeTz'). Note that 'BUFLEN - 1' is passed and // *not* 'BUFLEN' because the former indicates the correct number of characters // in 'buffer' that we wish to parse: //.. // rc = bdlt::FixUtil::parse(&targetDatetimeTz, buffer, BUFLEN - 1); // // assert( 0 == rc); // assert(sourceDatetimeTz == targetDatetimeTz); //.. // Then, we parse the string in 'buffer' a second time, this time loading the // result into a 'bdlt::Datetime' object (instead of a 'bdlt::DatetimeTz'): //.. // rc = bdlt::FixUtil::parse(&targetDatetime, buffer, BUFLEN - 1); // // assert( 0 == rc); // assert(sourceDatetimeTz.utcDatetime() == targetDatetime); //.. // Note that this time the value of the target object has been converted to // UTC. // // Finally, we modify the 'configuration' to display the 'bdlt::DatetimeTz' // without fractional seconds: //.. // configuration.setFractionalSecondPrecision(0); // rc = bdlt::FixUtil::generate(buffer, // BUFLEN, // sourceDatetimeTz, // configuration); // assert(BUFLEN - 8 == rc); // assert( 0 == bsl::strcmp(buffer, "20050131-08:59:59+04:00")); //.. #include <bdlscm_version.h> #include <bdlt_fixutilconfiguration.h> #include <bslmf_assert.h> #include <bslmf_issame.h> #include <bsls_assert.h> #include <bsls_libraryfeatures.h> #include <bsls_review.h> #include <bsl_ostream.h> #include <bsl_string.h> namespace BloombergLP { namespace bdlt { class Date; class DateTz; class Datetime; class DatetimeTz; class Time; class TimeTz; class FixUtilConfiguration; // ============== // struct FixUtil // ============== struct FixUtil { // This 'struct' provides a namespace for a suite of pure functions that // perform conversions between objects of 'bdlt' vocabulary type and their // FIX representations. Each 'generate' and 'generateRaw' method takes a // 'bdlt' object (of type 'Date', 'DateTz', 'Time', 'TimeTz', 'Datetime', // or 'DatetimeTz') and outputs its corresponding FIX representation to a // user-supplied character buffer or 'bsl::ostream'. The 'parse' methods // effect the opposite conversion in that they populate a 'bdlt' object // from the result of parsing a FIX representation. // TYPES enum { // This enumeration defines fixed lengths for the FIX representations // of date, time, and datetime values. Note that these constants do // *not* account for the null terminator that may be produced by the // 'generate' functions taking a 'bufferLength' argument. k_DATE_STRLEN = 8, // 'bdlt::Date' k_DATETZ_STRLEN = 14, // 'bdlt::DateTz' k_TIME_STRLEN = 15, // 'bdlt::Time' k_TIMETZ_STRLEN = 14, // 'bdlt::TimeTz' k_DATETIME_STRLEN = 24, // 'bdlt::Datetime' k_DATETIMETZ_STRLEN = 30, // 'bdlt::DatetimeTz' k_MAX_STRLEN = k_DATETIMETZ_STRLEN }; // CLASS METHODS static int generate(char *buffer, int bufferLength, const Date& object); static int generate(char *buffer, int bufferLength, const Date& object, const FixUtilConfiguration& configuration); static int generate(char *buffer, int bufferLength, const Time& object); static int generate(char *buffer, int bufferLength, const Time& object, const FixUtilConfiguration& configuration); static int generate(char *buffer, int bufferLength, const Datetime& object); static int generate(char *buffer, int bufferLength, const Datetime& object, const FixUtilConfiguration& configuration); static int generate(char *buffer, int bufferLength, const DateTz& object); static int generate(char *buffer, int bufferLength, const DateTz& object, const FixUtilConfiguration& configuration); static int generate(char *buffer, int bufferLength, const TimeTz& object); static int generate(char *buffer, int bufferLength, const TimeTz& object, const FixUtilConfiguration& configuration); static int generate(char *buffer, int bufferLength, const DatetimeTz& object); static int generate(char *buffer, int bufferLength, const DatetimeTz& object, const FixUtilConfiguration& configuration); // Write the FIX representation of the specified 'object' to the // specified 'buffer' of the specified 'bufferLength' (in bytes), // truncating (if necessary) to 'bufferLength'. Optionally specify a // 'configuration' to affect the format of the generated string. If // 'configuration' is not supplied, the process-wide default value // 'FixUtilConfiguration::defaultConfiguration()' is used. Return the // number of characters in the formatted string before truncation (not // counting a null terminator). If 'bufferLength' indicates sufficient // capacity, 'buffer' is null terminated. The behavior is undefined // unless '0 <= bufferLength'. Note that a buffer of size // 'k_MAX_STRLEN + 1' is large enough to hold any string generated by // this component (counting a null terminator, if any). static int generate(bsl::string *string, const Date& object); static int generate(bsl::string *string, const Date& object, const FixUtilConfiguration& configuration); static int generate(bsl::string *string, const Time& object); static int generate(bsl::string *string, const Time& object, const FixUtilConfiguration& configuration); static int generate(bsl::string *string, const Datetime& object); static int generate(bsl::string *string, const Datetime& object, const FixUtilConfiguration& configuration); static int generate(bsl::string *string, const DateTz& object); static int generate(bsl::string *string, const DateTz& object, const FixUtilConfiguration& configuration); static int generate(bsl::string *string, const TimeTz& object); static int generate(bsl::string *string, const TimeTz& object, const FixUtilConfiguration& configuration); static int generate(bsl::string *string, const DatetimeTz& object); static int generate(bsl::string *string, const DatetimeTz& object, const FixUtilConfiguration& configuration); static int generate(std::string *string, const Date& object); static int generate(std::string *string, const Date& object, const FixUtilConfiguration& configuration); static int generate(std::string *string, const Time& object); static int generate(std::string *string, const Time& object, const FixUtilConfiguration& configuration); static int generate(std::string *string, const Datetime& object); static int generate(std::string *string, const Datetime& object, const FixUtilConfiguration& configuration); static int generate(std::string *string, const DateTz& object); static int generate(std::string *string, const DateTz& object, const FixUtilConfiguration& configuration); static int generate(std::string *string, const TimeTz& object); static int generate(std::string *string, const TimeTz& object, const FixUtilConfiguration& configuration); static int generate(std::string *string, const DatetimeTz& object); static int generate(std::string *string, const DatetimeTz& object, const FixUtilConfiguration& configuration); #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR static int generate(std::pmr::string *string, const Date& object); static int generate(std::pmr::string *string, const Date& object, const FixUtilConfiguration& configuration); static int generate(std::pmr::string *string, const Time& object); static int generate(std::pmr::string *string, const Time& object, const FixUtilConfiguration& configuration); static int generate(std::pmr::string *string, const Datetime& object); static int generate(std::pmr::string *string, const Datetime& object, const FixUtilConfiguration& configuration); static int generate(std::pmr::string *string, const DateTz& object); static int generate(std::pmr::string *string, const DateTz& object, const FixUtilConfiguration& configuration); static int generate(std::pmr::string *string, const TimeTz& object); static int generate(std::pmr::string *string, const TimeTz& object, const FixUtilConfiguration& configuration); static int generate(std::pmr::string *string, const DatetimeTz& object); static int generate(std::pmr::string *string, const DatetimeTz& object, const FixUtilConfiguration& configuration); #endif // Load the FIX representation of the specified 'object' into the // specified 'string'. Optionally specify a 'configuration' to affect // the format of the generated string. If 'configuration' is not // supplied, the process-wide default value // 'FixUtilConfiguration::defaultConfiguration()' is used. Return the // number of characters in the formatted string. The previous contents // of 'string' (if any) are discarded. static bsl::ostream& generate(bsl::ostream& stream, const Date& object); static bsl::ostream& generate(bsl::ostream& stream, const Date& object, const FixUtilConfiguration& configuration); static bsl::ostream& generate(bsl::ostream& stream, const Time& object); static bsl::ostream& generate(bsl::ostream& stream, const Time& object, const FixUtilConfiguration& configuration); static bsl::ostream& generate(bsl::ostream& stream, const Datetime& object); static bsl::ostream& generate(bsl::ostream& stream, const Datetime& object, const FixUtilConfiguration& configuration); static bsl::ostream& generate(bsl::ostream& stream, const DateTz& object); static bsl::ostream& generate(bsl::ostream& stream, const DateTz& object, const FixUtilConfiguration& configuration); static bsl::ostream& generate(bsl::ostream& stream, const TimeTz& object); static bsl::ostream& generate(bsl::ostream& stream, const TimeTz& object, const FixUtilConfiguration& configuration); static bsl::ostream& generate(bsl::ostream& stream, const DatetimeTz& object); static bsl::ostream& generate(bsl::ostream& stream, const DatetimeTz& object, const FixUtilConfiguration& configuration); // Write the FIX representation of the specified 'object' to the // specified 'stream'. Optionally specify a 'configuration' to affect // the format of the generated string. If 'configuration' is not // supplied, the process-wide default value // 'FixUtilConfiguration::defaultConfiguration()' is used. Return a // reference to 'stream'. Note that 'stream' is not null terminated. static int generateRaw(char *buffer, const Date& object); static int generateRaw(char *buffer, const Date& object, const FixUtilConfiguration& configuration); static int generateRaw(char *buffer, const Time& object); static int generateRaw(char *buffer, const Time& object, const FixUtilConfiguration& configuration); static int generateRaw(char *buffer, const Datetime& object); static int generateRaw(char *buffer, const Datetime& object, const FixUtilConfiguration& configuration); static int generateRaw(char *buffer, const DateTz& object); static int generateRaw(char *buffer, const DateTz& object, const FixUtilConfiguration& configuration); static int generateRaw(char *buffer, const TimeTz& object); static int generateRaw(char *buffer, const TimeTz& object, const FixUtilConfiguration& configuration); static int generateRaw(char *buffer, const DatetimeTz& object); static int generateRaw(char *buffer, const DatetimeTz& object, const FixUtilConfiguration& configuration); // Write the FIX representation of the specified 'object' to the // specified 'buffer'. Optionally specify a 'configuration' to affect // the format of the generated string. If 'configuration' is not // supplied, the process-wide default value // 'FixUtilConfiguration::defaultConfiguration()' is used. Return the // number of characters in the formatted string. 'buffer' is not null // terminated. The behavior is undefined unless 'buffer' has // sufficient capacity. Note that a buffer of size 'k_MAX_STRLEN + 1' // is large enough to hold any string generated by this component // (counting a null terminator, if any). static int parse(Date *result, const char *string, int length); // Parse the specified initial 'length' characters of the specified FIX // 'string' as a 'Date' value, and load the value into the specified // 'result'. Return 0 on success, and a non-zero value (with no // effect) otherwise. 'string' is assumed to be of the form: //.. // YYYYMMDD{(+|-)hh{:mm}|Z} //.. // *Exactly* 'length' characters are parsed; parsing will fail if a // proper prefix of 'string' matches the expected format, but the // entire 'length' characters do not. If the optional timezone offset // is present in 'string', it is parsed but ignored. The behavior is // undefined unless '0 <= length'. static int parse(Time *result, const char *string, int length); // Parse the specified initial 'length' characters of the specified FIX // 'string' as a 'Time' value, and load the value into the specified // 'result'. Return 0 on success, and a non-zero value (with no // effect) otherwise. 'string' is assumed to be of the form: //.. // hh:mm:ss{.s+}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'length' characters are parsed; parsing will fail if a // proper prefix of 'string' matches the expected format, but the // entire 'length' characters do not. If an optional fractional second // having more than six digits is present in 'string', it is rounded // to the nearest value in microseconds. If the optional timezone // offset is present in 'string', the resulting 'Time' value is // converted to the equivalent UTC time; if the timezone offset is // absent, UTC is assumed. If a leap second is detected (i.e., the // parsed value of the 'second' attribute is 60; see {Leap Seconds}), // the 'second' attribute is taken to be 59, then an additional second // is added to 'result' at the end. The behavior is undefined unless // '0 <= length'. static int parse(Datetime *result, const char *string, int length); // Parse the specified initial 'length' characters of the specified FIX // 'string' as a 'Datetime' value, and load the value into the // specified 'result'. Return 0 on success, and a non-zero value (with // no effect) otherwise. 'string' is assumed to be of the form: //.. // YYYYMMDD-hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'length' characters are parsed; parsing will fail if a // proper prefix of 'string' matches the expected format, but the // entire 'length' characters do not. If an optional fractional second // having more than six digits is present in 'string', it is rounded to // the nearest value in microseconds. If the optional timezone offset // is present in 'string', the resulting 'Datetime' value is converted // to the equivalent UTC value; if the timezone offset is absent, UTC // is assumed. If a leap second is detected (i.e., the parsed value of // the 'second' attribute is 60; see {Leap Seconds}), the 'second' // attribute is taken to be 59, then an additional second is added to // 'result' at the end. The behavior is undefined unless // '0 <= length'. static int parse(DateTz *result, const char *string, int length); // Parse the specified initial 'length' characters of the specified FIX // 'string' as a 'DateTz' value, and load the value into the specified // 'result'. Return 0 on success, and a non-zero value (with no // effect) otherwise. 'string' is assumed to be of the form: //.. // YYYYMMDD{(+|-)hh{:mm}|Z} //.. // *Exactly* 'length' characters are parsed; parsing will fail if a // proper prefix of 'string' matches the expected format, but the // entire 'length' characters do not. If the optional timezone offset // is not present in 'string', UTC is assumed. The behavior is // undefined unless '0 <= length'. static int parse(TimeTz *result, const char *string, int length); // Parse the specified initial 'length' characters of the specified FIX // 'string' as a 'TimeTz' value, and load the value into the specified // 'result'. Return 0 on success, and a non-zero value (with no // effect) otherwise. 'string' is assumed to be of the form: //.. // hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'length' characters are parsed; parsing will fail if a // proper prefix of 'string' matches the expected format, but the // entire 'length' characters do not. If an optional fractional second // having more than six digits is present in 'string', it is rounded // to the nearest value in microseconds. If the optional timezone // offset is not present in 'string', UTC is assumed. If a leap second // is detected (i.e., the parsed value of the 'second' attribute is 60; // see {Leap Seconds}), the 'second' attribute is taken to be 59, then // an additional second is added to 'result' at the end. The behavior // is undefined unless '0 <= length'. static int parse(DatetimeTz *result, const char *string, int length); // Parse the specified initial 'length' characters of the specified FIX // 'string' as a 'DatetimeTz' value, and load the value into the // specified 'result'. Return 0 on success, and a non-zero value (with // no effect) otherwise. 'string' is assumed to be of the form: //.. // YYYYMMDD-hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'length' characters are parsed; parsing will fail if a // proper prefix of 'string' matches the expected format, but the // entire 'length' characters do not. If an optional fractional second // having more than six digits is present in 'string', it is rounded to // the nearest value in microseconds. If the optional timezone offset // is not present in 'string', UTC is assumed. If a leap second is // detected (i.e., the parsed value of the 'second' attribute is 60; // see {Leap Seconds}), the 'second' attribute is taken to be 59, then // an additional second is added to 'result' at the end. The behavior // is undefined unless '0 <= length'. static int parse(Date *result, const bsl::string_view& string); // Parse the specified FIX 'string' as a 'Date' value, and load the // value into the specified 'result'. Return 0 on success, and a // non-zero value (with no effect) otherwise. 'string' is assumed to // be of the form: //.. // YYYYMMDD{(+|-)hh{:mm}|Z} //.. // *Exactly* 'string.length()' characters are parsed; parsing will fail // if a proper prefix of 'string' matches the expected format, but the // entire 'string.length()' characters do not. If the optional // timezone offset is present in 'string', it is parsed but ignored. // The behavior is undefined unless 'string.data()' is non-null. static int parse(Time *result, const bsl::string_view& string); // Parse the specified FIX 'string' as a 'Time' value, and load the // value into the specified 'result'. Return 0 on success, and a // non-zero value (with no effect) otherwise. 'string' is assumed to // be of the form: //.. // hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'string.length()' characters are parsed; parsing will fail // if a proper prefix of 'string' matches the expected format, but the // entire 'string.length()' characters do not. If an optional // fractional second having more than six digits is present in // 'string', it is rounded to the nearest value in microseconds. If // the optional timezone offset is present in 'string', the resulting // 'Time' value is converted to the equivalent UTC time; if the // timezone offset is absent, UTC is assumed. If a leap second is // detected (i.e., the parsed value of the 'second' attribute is 60; // see {Leap Seconds}), the 'second' attribute is taken to be 59, then // an additional second is added to 'result' at the end. The behavior // is undefined unless 'string.data()' is non-null. static int parse(Datetime *result, const bsl::string_view& string); // Parse the specified FIX 'string' as a 'Datetime' value, and load the // value into the specified 'result'. Return 0 on success, and a // non-zero value (with no effect) otherwise. 'string' is assumed to // be of the form: //.. // YYYYMMDD-hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'string.length()' characters are parsed; parsing will fail // if a proper prefix of 'string' matches the expected format, but the // entire 'string.length()' characters do not. If an optional // fractional second having more than six digits is present in // 'string', it is rounded to the nearest value in microseconds. If // the optional timezone offset is present in 'string', the resulting // 'Datetime' value is converted to the equivalent UTC value; if the // timezone offset is absent, UTC is assumed. If a leap second is // detected (i.e., the parsed value of the 'second' attribute is 60; // see {Leap Seconds}), the 'second' attribute is taken to be 59, then // an additional second is added to 'result' at the end. The behavior // is undefined unless 'string.data()' is non-null. static int parse(DateTz *result, const bsl::string_view& string); // Parse the specified FIX 'string' as a 'DateTz' value, and load the // value into the specified 'result'. Return 0 on success, and a // non-zero value (with no effect) otherwise. 'string' is assumed to // be of the form: //.. // YYYYMMDD{(+|-)hh{:mm}|Z} //.. // *Exactly* 'string.length()' characters are parsed; parsing will fail // if a proper prefix of 'string' matches the expected format, but the // entire 'string.length()' characters do not. If the optional // timezone offset is not present in 'string', UTC is assumed. The // behavior is undefined unless 'string.data()' is non-null. static int parse(TimeTz *result, const bsl::string_view& string); // Parse the specified FIX 'string' as a 'TimeTz' value, and load the // value into the specified 'result'. Return 0 on success, and a // non-zero value (with no effect) otherwise. 'string' is assumed to // be of the form: //.. // hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'string.length()' characters are parsed; parsing will fail // if a proper prefix of 'string' matches the expected format, but the // entire 'string.length()' characters do not. If an optional // fractional second having more than six digits is present in // 'string', it is rounded to the nearest value in microseconds. If // the optional timezone offset is not present in 'string', UTC is // assumed. If a leap second is detected (i.e., the parsed value of // the 'second' attribute is 60; see {Leap Seconds}), the 'second' // attribute is taken to be 59, then an additional second is added to // 'result' at the end. The behavior is undefined unless // 'string.data()' is non-null. static int parse(DatetimeTz *result, const bsl::string_view& string); // Parse the specified FIX 'string' as a 'DatetimeTz' value, and load // the value into the specified 'result'. Return 0 on success, and a // non-zero value (with no effect) otherwise. 'string' is assumed to // be of the form: //.. // YYYYMMDD-hh:mm{:ss{.s+}}{(+|-)hh{:mm}|Z} //.. // *Exactly* 'string.length()' characters are parsed; parsing will fail // if a proper prefix of 'string' matches the expected format, but the // entire 'string.length()' characters do not. If an optional // fractional second having more than six digits is present in // 'string', it is rounded to the nearest value in microseconds. If // the optional timezone offset is not present in 'string', UTC is // assumed. If a leap second is detected (i.e., the parsed value of // the 'second' attribute is 60; see {Leap Seconds}), the 'second' // attribute is taken to be 59, then an additional second is added to // 'result' at the end. The behavior is undefined unless // 'string.data()' is non-null. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // -------------- // struct FixUtil // -------------- // CLASS METHODS inline int FixUtil::generate(char *buffer, int bufferLength, const Date& object) { BSLS_ASSERT(buffer); BSLS_ASSERT(0 <= bufferLength); return generate(buffer, bufferLength, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(char *buffer, int bufferLength, const Time& object) { BSLS_ASSERT(buffer); BSLS_ASSERT(0 <= bufferLength); return generate(buffer, bufferLength, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(char *buffer, int bufferLength, const Datetime& object) { BSLS_ASSERT(buffer); BSLS_ASSERT(0 <= bufferLength); return generate(buffer, bufferLength, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(char *buffer, int bufferLength, const DateTz& object) { BSLS_ASSERT(buffer); BSLS_ASSERT(0 <= bufferLength); return generate(buffer, bufferLength, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(char *buffer, int bufferLength, const TimeTz& object) { BSLS_ASSERT(buffer); BSLS_ASSERT(0 <= bufferLength); return generate(buffer, bufferLength, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(char *buffer, int bufferLength, const DatetimeTz& object) { BSLS_ASSERT(buffer); BSLS_ASSERT(0 <= bufferLength); return generate(buffer, bufferLength, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(bsl::string *string, const Date& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(bsl::string *string, const Time& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(bsl::string *string, const Datetime& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(bsl::string *string, const DateTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(bsl::string *string, const TimeTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(bsl::string *string, const DatetimeTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::string *string, const Date& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::string *string, const Time& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::string *string, const Datetime& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::string *string, const DateTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::string *string, const TimeTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::string *string, const DatetimeTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR inline int FixUtil::generate(std::pmr::string *string, const Date& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::pmr::string *string, const Time& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::pmr::string *string, const Datetime& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::pmr::string *string, const DateTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::pmr::string *string, const TimeTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generate(std::pmr::string *string, const DatetimeTz& object) { return generate(string, object, FixUtilConfiguration::defaultConfiguration()); } #endif inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const Date& object) { return generate(stream, object, FixUtilConfiguration::defaultConfiguration()); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const Date& object, const FixUtilConfiguration& configuration) { char buffer[k_DATE_STRLEN + 1]; const int len = generate(buffer, k_DATE_STRLEN, object, configuration); BSLS_ASSERT(k_DATE_STRLEN >= len); return stream.write(buffer, len); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const Time& object) { return generate(stream, object, FixUtilConfiguration::defaultConfiguration()); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const Time& object, const FixUtilConfiguration& configuration) { char buffer[k_TIME_STRLEN + 1]; const int len = generate(buffer, k_TIME_STRLEN, object, configuration); BSLS_ASSERT(k_TIME_STRLEN >= len); return stream.write(buffer, len); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const Datetime& object) { return generate(stream, object, FixUtilConfiguration::defaultConfiguration()); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const Datetime& object, const FixUtilConfiguration& configuration) { char buffer[k_DATETIME_STRLEN + 1]; const int len = generate(buffer, k_DATETIME_STRLEN, object, configuration); BSLS_ASSERT(k_DATETIME_STRLEN >= len); return stream.write(buffer, len); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const DateTz& object) { return generate(stream, object, FixUtilConfiguration::defaultConfiguration()); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const DateTz& object, const FixUtilConfiguration& configuration) { char buffer[k_DATETZ_STRLEN + 1]; const int len = generate(buffer, k_DATETZ_STRLEN, object, configuration); BSLS_ASSERT(k_DATETZ_STRLEN >= len); return stream.write(buffer, len); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const TimeTz& object) { return generate(stream, object, FixUtilConfiguration::defaultConfiguration()); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const TimeTz& object, const FixUtilConfiguration& configuration) { char buffer[k_TIMETZ_STRLEN + 1]; const int len = generate(buffer, k_TIMETZ_STRLEN, object, configuration); BSLS_ASSERT(k_TIMETZ_STRLEN >= len); return stream.write(buffer, len); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const DatetimeTz& object) { return generate(stream, object, FixUtilConfiguration::defaultConfiguration()); } inline bsl::ostream& FixUtil::generate(bsl::ostream& stream, const DatetimeTz& object, const FixUtilConfiguration& configuration) { char buffer[k_DATETIMETZ_STRLEN + 1]; const int len = generate(buffer, k_DATETIMETZ_STRLEN, object, configuration); BSLS_ASSERT(k_DATETIMETZ_STRLEN >= len); return stream.write(buffer, len); } inline int FixUtil::generateRaw(char *buffer, const Date& object) { BSLS_ASSERT(buffer); return generateRaw(buffer, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generateRaw(char *buffer, const Time& object) { BSLS_ASSERT(buffer); return generateRaw(buffer, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generateRaw(char *buffer, const Datetime& object) { BSLS_ASSERT(buffer); return generateRaw(buffer, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generateRaw(char *buffer, const DateTz& object) { BSLS_ASSERT(buffer); return generateRaw(buffer, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generateRaw(char *buffer, const TimeTz& object) { BSLS_ASSERT(buffer); return generateRaw(buffer, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::generateRaw(char *buffer, const DatetimeTz& object) { BSLS_ASSERT(buffer); return generateRaw(buffer, object, FixUtilConfiguration::defaultConfiguration()); } inline int FixUtil::parse(Date *result, const bsl::string_view& string) { BSLS_ASSERT(string.data()); return parse(result, string.data(), static_cast<int>(string.length())); } inline int FixUtil::parse(Time *result, const bsl::string_view& string) { BSLS_ASSERT(string.data()); return parse(result, string.data(), static_cast<int>(string.length())); } inline int FixUtil::parse(Datetime *result, const bsl::string_view& string) { BSLS_ASSERT(string.data()); return parse(result, string.data(), static_cast<int>(string.length())); } inline int FixUtil::parse(DateTz *result, const bsl::string_view& string) { BSLS_ASSERT(string.data()); return parse(result, string.data(), static_cast<int>(string.length())); } inline int FixUtil::parse(TimeTz *result, const bsl::string_view& string) { BSLS_ASSERT(string.data()); return parse(result, string.data(), static_cast<int>(string.length())); } inline int FixUtil::parse(DatetimeTz *result, const bsl::string_view& string) { BSLS_ASSERT(string.data()); return parse(result, string.data(), static_cast<int>(string.length())); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2017 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------