1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.core.BasicClass;
    18  import org.as2lib.env.except.IllegalArgumentException;
    19  
    20  /**
    21   * {@code DateFormatter} formats a given date with a specified pattern.
    22   * 
    23   * <p>Use the declared constants as placeholders for specific parts of the date-time.
    24   *
    25   * <p>All characters from 'A' to 'Z' and from 'a' to 'z' are reserved, although not
    26   * all of these characters are interpreted right now. If you want to include plain
    27   * text in the pattern put it into quotes (') to avoid interpretation. If you want
    28   * a quote in the formatted date-time, put two quotes directly after one another.
    29   * For example: {@code "hh 'o''clock'"}.
    30   * 
    31   * <p>Example:
    32   * <code>
    33   *   var formatter:DateFormatter = new DateFormatter("dd.mm.yyyy HH:nn:ss S");
    34   *   trace(formatter.format(new Date(2005, 2, 29, 18, 14, 3, 58)));
    35   * </code>
    36   *
    37   * <p>Output:
    38   * <pre>
    39   *   29.03.2005 18:14:03 58
    40   * </pre>
    41   *
    42   * @author Simon Wacker
    43   */
    44  class org.as2lib.util.DateFormatter extends BasicClass {
    45  	
    46  	/** The default date format pattern. */
    47  	public static var DEFAULT_DATE_FORMAT:String = "dd.mm.yyyy HH:nn:ss";
    48  	
    49  	/** Placeholder for year in date format. */
    50  	public static var YEAR:String = "y";
    51  	
    52  	/** Placeholder for month in year as number in date format. */
    53  	public static var MONTH_AS_NUMBER:String = "m";
    54  	
    55  	/** Placeholder for month in year as text in date format. */
    56  	public static var MONTH_AS_TEXT:String = "M";
    57  	
    58  	/** Placeholder for day in month as number in date format. */
    59  	public static var DAY_AS_NUMBER:String = "d";
    60  	
    61  	/** Placeholder for day in week as text in date format. */
    62  	public static var DAY_AS_TEXT:String = "D";
    63  	
    64  	/** Placeholder for hour in am/pm (1 - 12) in date format. */
    65  	public static var HOUR_IN_AM_PM:String = "h";
    66  	
    67  	/** Placeholder for hour in day (0 - 23) in date format. */
    68  	public static var HOUR_IN_DAY:String = "H";
    69  	
    70  	/** Placeholder for minute in hour in date format. */
    71  	public static var MINUTE:String = "n";
    72  	
    73  	/** Placeholder for second in minute in date format. */
    74  	public static var SECOND:String = "s";
    75  	
    76  	/** Placeholder for millisecond in date format. */
    77  	public static var MILLISECOND:String = "S";
    78  	
    79  	/** Quotation beginning and ending token. */
    80  	public static var QUOTE:String = "'";
    81  	
    82  	/** Fully written out string for january. */
    83  	public static var JANUARY:String = "January";
    84  	
    85  	/** Fully written out string for february. */
    86  	public static var FEBRUARY:String = "February";
    87  	
    88  	/** Fully written out string for march. */
    89  	public static var MARCH:String = "March";
    90  	
    91  	/** Fully written out string for april. */
    92  	public static var APRIL:String = "April";
    93  	
    94  	/** Fully written out string for may. */
    95  	public static var MAY:String = "May";
    96  	
    97  	/** Fully written out string for june. */
    98  	public static var JUNE:String = "June";
    99  	
   100  	/** Fully written out string for july. */
   101  	public static var JULY:String = "July";
   102  	
   103  	/** Fully written out string for august. */
   104  	public static var AUGUST:String = "August";
   105  	
   106  	/** Fully written out string for september. */
   107  	public static var SEPTEMBER:String = "September";
   108  	
   109  	/** Fully written out string for october. */
   110  	public static var OCTOBER:String = "October";
   111  	
   112  	/** Fully written out string for november. */
   113  	public static var NOVEMBER:String = "November";
   114  	
   115  	/** Fully written out string for december. */
   116  	public static var DECEMBER:String = "December";
   117  	
   118  	/** Fully written out string for monday. */
   119  	public static var MONDAY:String = "Monday";
   120  	
   121  	/** Fully written out string for tuesday. */
   122  	public static var TUESDAY:String = "Tuesday";
   123  	
   124  	/** Fully written out string for wednesday. */
   125  	public static var WEDNESDAY:String = "Wednesday";
   126  	
   127  	/** Fully written out string for thursday. */
   128  	public static var THURSDAY:String = "Thursday";
   129  	
   130  	/** Fully written out string for friday. */
   131  	public static var FRIDAY:String = "Friday";
   132  	
   133  	/** Fully written out string for saturday. */
   134  	public static var SATURDAY:String = "Saturday";
   135  	
   136  	/** Fully written out string for sunday. */
   137  	public static var SUNDAY:String = "Sunday";
   138  	
   139  	/** The pattern to format the date with. */
   140  	private var dateFormat:String;
   141  	
   142  	/**
   143  	 * Constructs a new {@code DateFormatter} instance.
   144  	 *
   145  	 * <p>If you do not pass-in a {@code dateFormat} or if the passed-in one is
   146  	 * {@code null} or {@code undefined} the {@code DEFAULT_DATE_FORMAT} is used.
   147  	 * 
   148  	 * @param dateFormat (optional) the pattern describing the date and time format
   149  	 */
   150  	public function DateFormatter(dateFormat:String) {
   151  		this.dateFormat = dateFormat == null ? DEFAULT_DATE_FORMAT : dateFormat;
   152  	}
   153  	
   154  	/**
   155  	 * Formats the passed-in {@code date} with the specified date format pattern into a
   156  	 * date-time string and returns the resulting string.
   157  	 * 
   158  	 * <p>If the passed-in {@code date} is {@code null} or {@code undefined}, the current
   159  	 * date-time will be used instead.
   160  	 *
   161  	 * @param date the date-time value to format into a date-time string
   162  	 * @return the formatted date-time string
   163  	 */
   164  	public function format(date:Date):String {
   165  		if (!date) date = new Date();
   166  		var result:String = "";
   167  		for (var i:Number = 0; i < dateFormat.length; i++) {
   168  			if (dateFormat.substr(i, 1) == YEAR) {
   169  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   170  				result += formatYear(date.getFullYear(), tokenCount);
   171  				i += tokenCount - 1;
   172  				continue;
   173  			}
   174  			if (dateFormat.substr(i, 1) == MONTH_AS_NUMBER) {
   175  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   176  				result += formatMonthAsNumber(date.getMonth(), tokenCount);
   177  				i += tokenCount - 1;
   178  				continue;
   179  			}
   180  			if (dateFormat.substr(i, 1) == MONTH_AS_TEXT) {
   181  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   182  				result += formatMonthAsText(date.getMonth(), tokenCount);
   183  				i += tokenCount - 1;
   184  				continue;
   185  			}
   186  			if (dateFormat.substr(i, 1) == DAY_AS_NUMBER) {
   187  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   188  				result += formatDayAsNumber(date.getDate(), tokenCount);
   189  				i += tokenCount - 1;
   190  				continue;
   191  			}
   192  			if (dateFormat.substr(i, 1) == DAY_AS_TEXT) {
   193  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   194  				result += formatDayAsText(date.getDay(), tokenCount);
   195  				i += tokenCount - 1;
   196  				continue;
   197  			}
   198  			if (dateFormat.substr(i, 1) == HOUR_IN_AM_PM) {
   199  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   200  				result += formatHourInAmPm(date.getHours(), tokenCount);
   201  				i += tokenCount - 1;
   202  				continue;
   203  			}
   204  			if (dateFormat.substr(i, 1) == HOUR_IN_DAY) {
   205  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   206  				result += formatHourInDay(date.getHours(), tokenCount);
   207  				i += tokenCount - 1;
   208  				continue;
   209  			}
   210  			if (dateFormat.substr(i, 1) == MINUTE) {
   211  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   212  				result += formatMinute(date.getMinutes(), tokenCount);
   213  				i += tokenCount - 1;
   214  				continue;
   215  			}
   216  			if (dateFormat.substr(i, 1) == SECOND) {
   217  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   218  				result += formatSecond(date.getSeconds(), tokenCount);
   219  				i += tokenCount - 1;
   220  				continue;
   221  			}
   222  			if (dateFormat.substr(i, 1) == MILLISECOND) {
   223  				var tokenCount:Number = getTokenCount(dateFormat.substr(i));
   224  				result += formatMillisecond(date.getMilliseconds(), tokenCount);
   225  				i += tokenCount - 1;
   226  				continue;
   227  			}
   228  			if (dateFormat.substr(i, 1) == QUOTE) {
   229  				if (dateFormat.substr(i + 1, 1) == QUOTE) {
   230  					result += "'";
   231  					i++;
   232  					continue;
   233  				}
   234  				var nextQuote:Number = i;
   235  				var oldQuote:Number;
   236  				while (true) {
   237  					oldQuote = nextQuote;
   238  					nextQuote = dateFormat.indexOf("'", nextQuote + 1);
   239  					if (dateFormat.substr(nextQuote + 1, 1) != QUOTE) {
   240  						break;
   241  					}
   242  					result += dateFormat.substring(oldQuote + 1, nextQuote + 1);
   243  					nextQuote++;
   244  				}
   245  				result += dateFormat.substring(oldQuote + 1, nextQuote);
   246  				i = nextQuote;
   247  				continue;
   248  			}
   249  			result += dateFormat.substr(i, 1);
   250  		}
   251  		return result;
   252  	}
   253  	
   254  	/**
   255  	 * Returns the number of tokens that occur in a succession from the beginning of the
   256  	 * passed-in {@code string}.
   257  	 * 
   258  	 * <p>If the passed-in {@code string} is {@code null}, {@code undefined} or empty,
   259  	 * 0 is returned.
   260  	 *
   261  	 * @param string the string to search through
   262  	 * @return the number of tokens that occur in a succession
   263  	 */
   264  	private function getTokenCount(string:String):Number {
   265  		if (!string) return 0;
   266  		var result:Number = 0;
   267  		var token:String = string.substr(0, 1);
   268  		while (string.substr(result, 1) == token) {
   269  			result++;
   270  		}
   271  		return result;
   272  	}
   273  	
   274  	/**
   275  	 * Returns a string that contains the specified number of 0s.
   276  	 *
   277  	 * <p>A {@code count} less or equal than 0 or a {@code count} of value {@code null}
   278  	 * or {@code undefined} results in en empty string.
   279  	 * 
   280  	 * @param count the number of 0s
   281  	 * @return the specified number of 0s
   282  	 */
   283  	private function getZeros(count:Number):String {
   284  		if (count < 1 || count == null) return "";
   285  		if (count < 2) return "0";
   286  		var result:String = "00";
   287  		count -= 2;
   288  		while (count) {
   289  			result += "0";
   290  			count--;
   291  		}
   292  		return result;
   293  	}
   294  	
   295  	/**
   296  	 * Formats the passed-in {@code year} into a year string with the specified
   297  	 * {@code digitCount}.
   298  	 * 
   299  	 * <p>A {@code digitCount} less or equal than three results in a year string with
   300  	 * two digits. A {@code digitCount} greater or equal than four results in a year
   301  	 * string with four digits plus preceding 0s if the {@code digitCount} is greater
   302  	 * than four.
   303  	 *
   304  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   305  	 * is used instead.
   306  	 *
   307  	 * @param year the year to format to a string
   308  	 * @param digitCount the number of favored digits
   309  	 * @return the string representation of the year
   310  	 * @throws IllegalArgumentException if the passed-in {@code year} is
   311  	 * {@code null} or {@code undefined}
   312  	 */
   313  	private function formatYear(year:Number, digitCount:Number):String {
   314  		if (year == null) {
   315  			throw new IllegalArgumentException("Argument 'year' [" + year + "] must not be 'null' nor 'undefined'.", this, arguments);
   316  		}
   317  		if (digitCount == null) digitCount = 0;
   318  		if (digitCount < 4) {
   319  			return year.toString().substr(2);
   320  		}
   321  		return (getZeros(digitCount - 4) + year.toString());
   322  	}
   323  	
   324  	/**
   325  	 * Formats the passed-in {@code month} into a month as number string with the
   326  	 * specified {@code digitCount}.
   327  	 * 
   328  	 * <p>A {@code digitCount} less or equal than one results in a month with one digit,
   329  	 * if the month is less or equal than nine. Otherwise the month is represented by a
   330  	 * two digit number. A {@code digitCount} greater or equal than two results in a
   331  	 * month with preceding 0s.
   332  	 *
   333  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0 
   334  	 * is used instead.
   335  	 * 
   336  	 * @param month the month to format to a number string
   337  	 * @param digitCount the number of favored digits
   338  	 * @return the number representation of the month
   339  	 * @throws IllegalArgumentException if the passed-in {@code month} is
   340  	 * less than 0 or greater than 11 or {@code null} or {@code undefined}
   341  	 */
   342  	private function formatMonthAsNumber(month:Number, digitCount:Number):String {
   343  		if (month < 0 || month > 11 || month == null) {
   344  			throw new IllegalArgumentException("Argument 'month' [" + month + "] must not be less than 0 nor greater than 11 nor 'null' nor 'undefined'.", this, arguments);
   345  		}
   346  		if (digitCount == null) digitCount = 0;
   347  		var string:String = (month + 1).toString();
   348  		return (getZeros(digitCount - string.length) + string);
   349  	}
   350  	
   351  	/**
   352  	 * Formats the passed-in {@code month} into a string with the specified 
   353  	 * {@code tokenCount}.
   354  	 * 
   355  	 * <p>A {@code tokenCount} less or equal than three results in a month with three
   356  	 * tokens. A {@code tokenCount} greater or equal than four results in a fully written
   357  	 * out month.
   358  	 *
   359  	 * <p>If the passed-in {@code tokenCount} is {@code null} or {@code undefined}, 0
   360  	 * is used instead.
   361  	 *
   362  	 * @param month the month to format to a string
   363  	 * @param tokenCount the number of favored tokens
   364  	 * @return the string representation of the month
   365  	 * @throws IllegalArgumentException if the passed-in {@code month} is
   366  	 * less than 0 or greater than 11 or {@code null} or {@code undefined}
   367  	 */
   368  	private function formatMonthAsText(month:Number, tokenCount:Number):String {
   369  		if (month < 0 || month > 11 || month == null) {
   370  			throw new IllegalArgumentException("Argument 'month' [" + month + "] must not be less than 0 nor greater than 11 nor 'null' nor 'undefined'.", this, arguments);
   371  		}
   372  		if (tokenCount == null) tokenCount = 0;
   373  		var result:String;
   374  		switch (month) {
   375  			case 0:
   376  				result = JANUARY;
   377  				break;
   378  			case 1:
   379  				result = FEBRUARY;
   380  				break;
   381  			case 2:
   382  				result = MARCH;
   383  				break;
   384  			case 3:
   385  				result = APRIL;
   386  				break;
   387  			case 4:
   388  				result = MAY;
   389  				break;
   390  			case 5:
   391  				result = JUNE;
   392  				break;
   393  			case 6:
   394  				result = JULY;
   395  				break;
   396  			case 7:
   397  				result = AUGUST;
   398  				break;
   399  			case 8:
   400  				result = SEPTEMBER;
   401  				break;
   402  			case 9:
   403  				result = OCTOBER;
   404  				break;
   405  			case 10:
   406  				result = NOVEMBER;
   407  				break;
   408  			case 11:
   409  				result = DECEMBER;
   410  				break;
   411  		}
   412  		if (tokenCount < 4) {
   413  			return result.substr(0, 3);
   414  		}
   415  		return result;
   416  	}
   417  	
   418  	/**
   419  	 * Formats the passed-in {@code day} into a day as number string with the specified 
   420  	 * {@code digitCount}.
   421  	 * 
   422  	 * <p>A {@code digitCount} less or equal than one results in a day with one digit,
   423  	 * if the day is less or equal than nine. Otherwise the day is represented by a two
   424  	 * digit number. A {@code digitCount} greater or equal than two results in a day with
   425  	 * preceding 0s.
   426  	 *
   427  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   428  	 * is used instead.
   429  	 *
   430  	 * @param day the day of month to format to a number string
   431  	 * @param digitCount the number of digits
   432  	 * @return the number representation of the day
   433  	 * @throws IllegalArgumentException if the passed-in {@code day} is less
   434  	 * than 1 or greater than 31 or {@code null} or {@code undefined}
   435  	 */
   436  	private function formatDayAsNumber(day:Number, digitCount:Number):String {
   437  		if (day < 1 || day > 31 || day == null) {
   438  			throw new IllegalArgumentException("Argument 'day' [" + day + "] must not be less than 1 nor greater than 31 nor 'null' nor 'undefined'.", this, arguments);
   439  		}
   440  		if (digitCount == null) digitCount = 0;
   441  		var string:String = day.toString();
   442  		return (getZeros(digitCount - string.length) + string);
   443  	}
   444  	
   445  	/**
   446  	 * Formats the passed-in {@code day} into a string with the specified 
   447  	 * {@code tokenCount}.
   448  	 * 
   449  	 * <p>A {@code tokenCount} less or equal than three results in a day with two 
   450  	 * tokens. A {@code tokenCount} greater or equal than four results in a fully written
   451  	 * out day.
   452  	 *
   453  	 * <p>If the passed-in {@code tokenCount} is {@code null} or {@code undefined}, 0 
   454  	 * is used instead.
   455  	 *
   456  	 * @param day the day to format to a string
   457  	 * @param tokenCount the number of favored tokens
   458  	 * @return the string representation of the day
   459  	 * @throws IllegalArgumentException if the passed-in {@code day} is less
   460  	 * than 0 or greater than 6 or {@code null} or {@code undefined}
   461  	 */
   462  	private function formatDayAsText(day:Number, tokenCount:Number):String {
   463  		if (day < 0 || day > 6 || day == null) {
   464  			throw new IllegalArgumentException("Argument 'day' [" + day + "] must not be less than 0 nor greater than 6 nor 'null' nor 'undefined'.", this, arguments);
   465  		}
   466  		if (tokenCount == null) tokenCount = 0;
   467  		var result:String;
   468  		switch (day) {
   469  			case 0:
   470  				result = SUNDAY;
   471  				break;
   472  			case 1:
   473  				result = MONDAY;
   474  				break;
   475  			case 2:
   476  				result = TUESDAY;
   477  				break;
   478  			case 3:
   479  				result = WEDNESDAY;
   480  				break;
   481  			case 4:
   482  				result = THURSDAY;
   483  				break;
   484  			case 5:
   485  				result = FRIDAY;
   486  				break;
   487  			case 6:
   488  				result = SATURDAY;
   489  				break;
   490  		}
   491  		if (tokenCount < 4) {
   492  			return result.substr(0, 2);
   493  		}
   494  		return result;
   495  	}
   496  	
   497  	/**
   498  	 * Formats the passed-in {@code hour} into a number string from range 1 to 12.
   499  	 * 
   500  	 * <p>The resulting string contains only the specified {@code digitCount} if
   501  	 * possible. This means if the hour is 3 and the {@code digitCount} 1 the resulting
   502  	 * string contains one digit. But this is not possible with the hour 12. So in this
   503  	 * case the resulting string contains 2 digits. If {@code digitCount} is greater
   504  	 * than the actual number of digits, preceding 0s are added.
   505  	 *
   506  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   507  	 * is used instead.
   508  	 *
   509  	 * @param hour the hour to format
   510  	 * @param digitCount the number of favored digits
   511  	 * @return the string representation of {@code hour}
   512  	 * @throws IllegalArgumentException if the passed-in {@code hour} is less
   513  	 * than 0 or greater than 23 or {@code null} or {@code undefined}
   514  	 */
   515  	private function formatHourInAmPm(hour:Number, digitCount:Number):String {
   516  		if (hour < 0 || hour > 23 || hour == null) {
   517  			throw new IllegalArgumentException("Argument 'hour' [" + hour + "] must not be less than 0 nor greater than 23 nor 'null' nor 'undefined'.", this, arguments);
   518  		}
   519  		if (digitCount == null) digitCount = 0;
   520  		var string:String;
   521  		if (hour == 0) {
   522  			// 12.toString() causes a compiler error
   523  			string = (12).toString();
   524  		} else if (hour > 12) {
   525  			string = (hour - 12).toString();
   526  		} else {
   527  			string = hour.toString();
   528  		}
   529  		return (getZeros(digitCount - string.length) + string);
   530  	}
   531  	
   532  	/**
   533  	 * Formats the passed-in {@code hour} into a number string from range 0 to 23.
   534  	 * 
   535  	 * <p>The resulting string contains only the specified {@code digitCount} if 
   536  	 * possible. This means if the hour is 3 and the {@code digitCount} 1 the resulting
   537  	 * string contains one digit. But this is not possible with the hour 18. So in this
   538  	 * case the resulting string contains 2 digits. If {@code digitCount} is greater
   539  	 * than the actual number of digits, preceding 0s are added.
   540  	 *
   541  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   542  	 * is used instead.
   543  	 *
   544  	 * @param hour the hour to format
   545  	 * @param digitCount the number of favored digits
   546  	 * @return the string representation of {@code hour}
   547  	 * @throws IllegalArgumentException if the passed-in {@code hour} is less
   548  	 * than 0 or greater than 23 or {@code null} or {@code undefined}
   549  	 */
   550  	private function formatHourInDay(hour:Number, digitCount:Number):String {
   551  		if (hour < 0 || hour > 23 || hour == null) {
   552  			throw new IllegalArgumentException("Argument 'hour' [" + hour + "] must not be less than 0 nor greater than 23 nor 'null' nor 'undefined'.", this, arguments);
   553  		}
   554  		if (digitCount == null) digitCount = 0;
   555  		var string:String = hour.toString();
   556  		return (getZeros(digitCount - string.length) + string);
   557  	}
   558  	
   559  	/**
   560  	 * Formats the passed-in {@code minute} into a number string with range 0 to 59.
   561  	 * 
   562  	 * <p>The resulting string contains only the specified {@code digitCount} if
   563  	 * possible. This means if the minute is 3 and the {@code digitCount} 1, the
   564  	 * resulting string contains only one digit. But this is not possible with the
   565  	 * minute 46. So in this case the resulting string contains 2 digits. If
   566  	 * {@code digitCount} is greater than the actual number of digits, preceding 0s are
   567  	 * added.
   568  	 *
   569  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   570  	 * is used instead.
   571  	 *
   572  	 * @param minute the minute to format
   573  	 * @param digitCount the number of favored digits
   574  	 * @return the string representation of the {@code minute}
   575  	 * @throws IllegalArgumentException if the passed-in {@code minute} is
   576  	 * less than 0 or greater than 59 or {@code null} or {@code undefined}
   577  	 */
   578  	private function formatMinute(minute:Number, digitCount:Number):String {
   579  		if (minute < 0 || minute > 59 || minute == null) {
   580  			throw new IllegalArgumentException("Argument 'minute' [" + minute + "] must not be less than 0 nor greater than 59 nor 'null' nor 'undefined'.", this, arguments);
   581  		}
   582  		if (digitCount == null) digitCount = 0;
   583  		var string:String = minute.toString();
   584  		return (getZeros(digitCount - string.length) + string);
   585  	}
   586  	
   587  	/**
   588  	 * Formats the passed-in {@code second} into a number string with range 0 to 59.
   589  	 * 
   590  	 * <p>The resulting string contains only the specified {@code digitCount} if
   591  	 * possible. This means if the second is 3 and the {@code digitCount} 1, the
   592  	 * resulting string contains only one digit. But this is not possible with the
   593  	 * second 46. So in this case the resulting string contains 2 digits. If
   594  	 * {@code digitCount} is greater than the actual number of digits, preceding 0s are
   595  	 * added.
   596  	 *
   597  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   598  	 * is used instead.
   599  	 *
   600  	 * @param second the second to format
   601  	 * @param digitCount the number of favored digits
   602  	 * @return the string representation of the {@code second}
   603  	 * @throws IllegalArgumentException if the passed-in {@code second} is
   604  	 * less than 0 or greater than 59 or {@code null} or {@code undefined}
   605  	 */
   606  	private function formatSecond(second:Number, digitCount:Number):String {
   607  		if (second < 0 || second > 59 || second == null) {
   608  			throw new IllegalArgumentException("Argument 'second' [" + second + "] must not be less than 0 nor greater than 59 nor 'null' nor 'undefined'.", this, arguments);
   609  		}
   610  		if (digitCount == null) digitCount = 0;
   611  		var string:String = second.toString();
   612  		return (getZeros(digitCount - string.length) + string);
   613  	}
   614  	
   615  	/**
   616  	 * Formats the passed-in {@code millisecond} into a number string with range 0 to
   617  	 * 999.
   618  	 * 
   619  	 * <p>The resulting string contains only the specified {@code digitCount} if
   620  	 * possible. This means if the millisecond is 7 and the {@code digitCount} 1, the
   621  	 * resulting string contains only one digit. But this is not possible with the
   622  	 * millisecond 588. So in this case the resulting string contains 3 digits. If
   623  	 * {@code digitCount} is greater than the actual number of digits, preceding 0s are
   624  	 * added.
   625  	 *
   626  	 * <p>If the passed-in {@code digitCount} is {@code null} or {@code undefined}, 0
   627  	 * is used instead.
   628  	 *
   629  	 * @param millisecond the millisecond to format
   630  	 * @param digitCount the number of favored digits
   631  	 * @return the string representation of the {@code millisecond}
   632  	 * @throws IllegalArgumentException if the passed-in {@code millisecond}
   633  	 * is less than 0 or greater than 999 or {@code null} or {@code undefined}
   634  	 */
   635  	private function formatMillisecond(millisecond:Number, digitCount:Number):String {
   636  		if (millisecond < 0 || millisecond > 999 || millisecond == null) {
   637  			throw new IllegalArgumentException("Argument 'millisecond' [" + millisecond + "] must not be less than 0 nor greater than 999 nor 'null' nor 'undefined'.", this, arguments);
   638  		}
   639  		if (digitCount == null) digitCount = 0;
   640  		var string:String = millisecond.toString();
   641  		return (getZeros(digitCount - string.length) + string);
   642  	}
   643  	
   644  }