diff --git a/currency.currency.inc b/currency.currency.inc
index eb1c80a..026697b 100644
--- a/currency.currency.inc
+++ b/currency.currency.inc
@@ -247,6 +247,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'EUR',
+      'minor_unit' => 2,
       'sign' => '€',
       'title' => t('Euro'),
     )),
@@ -771,6 +772,7 @@ function currency_currency_info() {
     )),
     new Currency(array(
       'code' => 'UAH',
+      'minor_unit' => 2,
       'sign' => 'грн.',
       'title' => t('Ukraine Hryvnia'),
     )),
diff --git a/currency.info b/currency.info
index 9f6abf8..a98aedf 100755
--- a/currency.info
+++ b/currency.info
@@ -5,4 +5,6 @@ configure = admin/config/regional/currency
 dependencies[] = ctools
 files[] = includes/Currency.inc
 files[] = includes/CurrencyBaseAbstract.inc
+files[] = includes/CurrencyFormat.inc
 files[] = tests/CurrencyCRUDWebTestCase.test
+files[] = tests/CurrencyFormatWebTestCase.test
diff --git a/includes/Currency.inc b/includes/Currency.inc
index 83ef5b3..83ddf6d 100644
--- a/includes/Currency.inc
+++ b/includes/Currency.inc
@@ -38,7 +38,7 @@ class Currency extends CurrencyBaseAbstract {
    *
    * @var integer
    */
-  public $minor_unit = NULL;
+  public $minor_unit = 0;
 
   /**
    * ISO 4217 currency number.
@@ -60,4 +60,26 @@ class Currency extends CurrencyBaseAbstract {
    * @var string
    */
   public $title = '';
+
+  /**
+   * Gets an amounts major unit.
+   *
+   * @param integer|string $amount
+   *
+   * @return integer
+   */
+  function amountMajorUnit($amount) {
+    return (int) substr($amount, 0, -$this->minor_unit);
+  }
+
+  /**
+   * Gets an amounts minor unit.
+   *
+   * @param integer|string $amount
+   *
+   * @return integer
+   */
+  function amountMinorUnit($amount) {
+    return (int) substr($amount, -$this->minor_unit);
+  }
 }
diff --git a/includes/CurrencyFormat.inc b/includes/CurrencyFormat.inc
new file mode 100644
index 0000000..8414303
--- /dev/null
+++ b/includes/CurrencyFormat.inc
@@ -0,0 +1,230 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyFormat.
+ */
+
+/**
+ * Describes a currency format.
+ */
+class CurrencyFormat {
+
+  /**
+   * The pattern for a positive amount.
+   *
+   * @var integer
+   */
+  const POSITIVE = 0;
+
+  /**
+   * The pattern for a negative amount.
+   *
+   * @var integer
+   */
+  const NEGATIVE = 1;
+
+  /**
+   * The pattern for an amount's major unit.
+   *
+   * @var integer
+   */
+  const MAJOR = 0;
+
+  /**
+   * The pattern for an amount's minor unit.
+   *
+   * @var integer
+   */
+  const MINOR = 1;
+
+  /**
+   * A Unicode CLDR currency number pattern, without short number support.
+   *
+   * @see http://cldr.unicode.org/translation/number-patterns
+   *
+   * @var string
+   *   See the Unicode documentation for details. Additionally, the following
+   *   replacements will be made:
+   *   - [XXX] will be replaced by the ISO 4217 currency code.
+   *   - [999] will be replaced by the ISO 4217 currency number.
+   */
+  public $pattern = '';
+
+  /**
+   * The character(s) used as decimal separator.
+   *
+   * @var string
+   */
+  public $decimal_separator = '';
+
+  /**
+   * The character(s) used as grouping (aka thousands) separator.
+   *
+   * @var string
+   */
+  public $grouping_separator = '';
+
+  /**
+   * Implements __construct().
+   */
+  function __construct($pattern, $decimal_separator, $grouping_separator) {
+    self::validatePattern($pattern);
+    $this->pattern = $pattern;
+    $this->decimal_separator = $decimal_separator;
+    $this->grouping_separator = $grouping_separator;
+  }
+
+  /**
+   * Splits a string in a unicode-safe way.
+   *
+   * @param string
+   *
+   * @return array
+   */
+  function str_split($string, $split_length = 1) {
+    preg_match_all('/.{' . $split_length . '}/u', $string, $matches);
+
+    return $matches[0];
+  }
+
+  /**
+   * Reverses a string in a unicode-safe way.
+   *
+   * @param string
+   *
+   * @return string
+   */
+  function strrev($string) {
+    return implode(array_reverse($this->str_split($string)));
+  }
+
+  /**
+   * Split the pattern into its positive and negative fragments.
+   *
+   * @return array
+   */
+  function patternSplitPositiveNegative() {
+    return preg_split("#(?<!');(?!')#", $this->pattern);
+  }
+
+  /**
+   * Split the pattern into its major and minor fragments.
+   *
+   * @param string $pattern
+   *
+   * @return array
+   */
+  static function patternSplitMajorMinor($pattern) {
+    return preg_split("#(?<!')\.(?!')#", $pattern);
+  }
+
+  /**
+   * Validate a format.
+   *
+   * @throws Exception
+   *
+   * @param string $pattern
+   */
+  static function validatePattern($pattern) {
+    $fragments = preg_split("#(?<!');(?!')#", $pattern);
+    if (count($fragments) != 2) {
+      throw new Exception(t('The pattern <code>@pattern/code> should contain parts for positive and negative amounts, separated by a single semicolon (<code>;</code>).', array(
+        '@pattern' => $pattern,
+      )));
+    }
+    foreach ($fragments as $fragment) {
+      $position = strpos($fragment, '.');
+      if ($position === FALSE) {
+        throw new Exception(t('The pattern <code>@pattern/code> does not contain a period (<code>.</code>) as a decimal separator.', array(
+        '@pattern' => $pattern,
+      )));
+      }
+      if (strpos($fragment, '00', $position) == FALSE) {
+        throw new Exception(t('The pattern <code>@pattern/code> does not contain a double zero (<code>00</code>) as a placeholder for decimal digits.', array(
+        '@pattern' => $pattern,
+      )));
+      }
+    }
+  }
+
+  /**
+   * Get a fragment of a pattern.
+   *
+   * @param integer $sign
+   *   self::POSITIVE or self::NEGATIVE.
+   * @param integer $majorminor
+   *   self::MAJOR or self::MINOR.
+   *
+   * @return string
+   */
+  function getPatternFragment($sign, $majorminor) {
+    $fragments = &drupal_static('CurrencyFormat' . $this->pattern);
+
+    if (is_null($fragments)) {
+      self::validatePattern($this->pattern);
+      foreach ($this->patternSplitPositiveNegative() as $sign => $pattern_sign) {
+        $fragments[$sign] = self::patternSplitMajorMinor($pattern_sign);
+      }
+    }
+
+    return $fragments[$sign][$majorminor];
+  }
+
+  /**
+   * Formats an amount.
+   *
+   * @throws Exception
+   *
+   * @param Currency $currency
+   * @param integer $amount
+   *
+   * @return string
+   */
+  function format(Currency $currency, $amount) {
+    $sign = (int) $amount < 0;
+
+    $output = array(
+      self::MAJOR => '',
+      self::MINOR => '',
+    );
+
+    // Render the minor unit.
+    $output[self::MINOR] = str_replace('00', $currency->amountMinorUnit($amount), $this->getPatternFragment($sign, self::MINOR));
+
+    // Render the major unit.
+    // Split the format into its characters in a unicode-safe way.
+    $characters = array_reverse(preg_split('//u', $this->getPatternFragment($sign, self::MAJOR)));
+    // We keep track of the first replaceable character in the format, so if
+    // the amount has more digits than there are replaceable characters, we
+    // replace the first character by all remaining digits.
+    $digits = array_reverse(str_split($currency->amountMajorUnit($amount)));
+    foreach ($characters as $position => $character) {
+      // Replace replaceable characters, but only if we still have digits left
+      // to replace them with.
+      if ($digits && ($character == '#' || $character ==='0')) {
+        $output[self::MAJOR] .= array_shift($digits);
+      }
+      else {
+        $output[self::MAJOR] .= $character;
+      }
+    }
+    // Remove remaining hashes and whatever is between them and the last
+    // digit.
+    $output[self::MAJOR] = preg_replace('/(\D*)#/', '', $output[self::MAJOR]);
+    // Put the major unit back in its original order.
+    $output[self::MAJOR] = $this->strrev($output[self::MAJOR]);
+
+    foreach ($output as &$output_sign) {
+      // Insert the grouping separator, currency sign, code, and number.
+      $output_sign = str_replace(',', $this->grouping_separator, $output_sign);
+      $output_sign = str_replace('¤', $currency->sign, $output_sign);
+      $output_sign = str_replace('[XXX]', $currency->code, $output_sign);
+      $output_sign = str_replace('[999]', $currency->number, $output_sign);
+      // Unescape escaped characters.
+      $output_sign = preg_replace("#'(.)'#", "\\1", $output_sign);
+    }
+
+    return $output[self::MAJOR] . $this->decimal_separator . $output[self::MINOR];
+  }
+}
diff --git a/tests/CurrencyFormatWebTestCase.test b/tests/CurrencyFormatWebTestCase.test
new file mode 100644
index 0000000..0b35fb3
--- /dev/null
+++ b/tests/CurrencyFormatWebTestCase.test
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains class CurrencyFormatWebTestCase.
+ */
+
+/**
+ * Tests Currency's functionality.
+ */
+class CurrencyFormatWebTestCase extends DrupalWebTestCase {
+
+  static function getInfo() {
+    return array(
+      'name' => 'Currency formatting',
+      'group' => 'Currency',
+    );
+  }
+
+  function setUp(array $modules = array()) {
+    parent::setUp($modules + array('currency'));
+  }
+
+  /**
+   * Test amount formatting.
+   */
+  function testFormat() {
+    ctools_include('export');
+    $amounts = array(123456789, -123456789);
+    // Use different currencies to test multibyte (some currency signs).
+    $currency_codes = array('EUR', 'UAH');
+    foreach ($currency_codes as $currency_code) {
+      $currency = ctools_export_crud_load('currency', $currency_code);
+      $patterns = array(
+        array(
+          'format' => new CurrencyFormat('¤###,###,###,##0.00;¤-###,###,###,##0.00', ',', '.'),
+          'results' => array($currency->sign . '1.234.567,89', $currency->sign . '-1.234.567,89'),
+        ),
+        array(
+          'format' => new CurrencyFormat('[XXX] ##0.00;[XXX] ##0.00', ',', '.'),
+          'results' => array($currency_code . ' 567,89', $currency_code . ' 567,89'),
+        ),
+        // Test some unusual character combinations and positions.
+        array(
+          'format' => new CurrencyFormat("¤####000/@##0.span style=\"text-transform: uppercase';'\">00[XXX]</span>--;-¤####000/@##0.span style=\"text-transform: uppercase';'\">00[XXX]</span>--", '<', '.'),
+          'results' => array($currency->sign . '1234/@567<span style="text-transform: uppercase;">89' . $currency_code . '</span>--', '-' . $currency->sign . '1234/@567<span style="text-transform: uppercase;">89' . $currency_code . '</span>--'),
+        ),
+      );
+      foreach ($patterns as $pattern_info) {
+        foreach ($amounts as $i => $amount) {
+          $format = $pattern_info['format'];
+          $result = $pattern_info['results'][$i];
+          $this->assertEqual($format->format($currency, $amount), $result, '<code>Currency::format()</code> formats amount <code>' . $currency_code . $amount . '</code> as <code>' . $result . '</code> using pattern <code>' . $format->pattern . '</code>.');
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
