package util{
import mx.formatters.DateFormatter;
import mx.utils.ObjectUtil;
import mx.utils.StringUtil;
public class DateUtils {
// Days of week
public static const MONDAY:String = "monday";
public static const TUESDAY:String = "tuesday";
public static const WEDNESDAY:String = "wednesday";
public static const THURSDAY:String = "thursday";
public static const FRIDAY:String = "friday";
public static const SATURDAY:String = "saturday";
public static const SUNDAY:String = "sunday";
// Months of year
public static const JANUARY:String = "january";
public static const FEBRUARY:String = "february";
public static const MARCH:String = "march";
public static const APRIL:String = "april";
public static const MAY:String = "may";
public static const JUNE:String = "june";
public static const JULY:String = "july";
public static const AUGUST:String = "august";
public static const SEPTEMBER:String = "september";
public static const OCTOBER:String = "october";
public static const NOVEMBER:String = "november";
public static const DECEMBER:String = "december";
// Date parts
public static const YEAR:String = "fullYear";
public static const MONTH:String = "month";
public static const WEEK:String = "week";
public static const DAY_OF_MONTH:String = "date";
public static const HOURS:String = "hours";
public static const MINUTES:String = "minutes";
public static const SECONDS:String = "seconds";
public static const MILLISECONDS:String = "milliseconds";
public static const DAY_OF_WEEK:String = "day";
// Numeric value of "last", to get last item for a specific time
public static const LAST:Number = -1;
// Date masks
public static const SHORT_DATE_MASK:String = "MM/DD/YY";
public static const MEDIUM_DATE_MASK:String = "MMM D, YYYY";
public static const LONG_DATE_MASK:String = "MMMM D, YYYY";
public static const FULL_DATE_MASK:String = "EEEE, MMMM D, YYYY";
// Time masks
public static const SHORT_TIME_MASK:String = "L:NN A";
public static const MEDIUM_TIME_MASK:String = "L:NN:SS A";
// TZD = TimeZoneDesignation = GMT + or - X hours, non-standard, requires a slight hack
public static const LONG_TIME_MASK:String = MEDIUM_TIME_MASK + " TZD";
// Internal values for using in date/time calculations
private static const SECOND_VALUE:uint = 1000;
private static const MINUTE_VALUE:uint = DateUtils.SECOND_VALUE * 60;
private static const HOUR_VALUE:uint = DateUtils.MINUTE_VALUE * 60;
private static const DAY_VALUE:uint = DateUtils.HOUR_VALUE * 24;
private static const WEEK_VALUE:uint = DateUtils.DAY_VALUE * 7;
// Internal variable used in date/time formatting
private static var _dateFormatter:DateFormatter;
private static function get dateFormatter():DateFormatter {
if ( !_dateFormatter ) {
_dateFormatter = new DateFormatter;
}
return _dateFormatter;
}
// a generic object for holding day of the week values
private static var _objDaysOfWeek:Object = null;
public static function get objDaysOfWeek():Object {
if ( !_objDaysOfWeek ) {
_objDaysOfWeek = {};
_objDaysOfWeek[ DateUtils.SUNDAY ] = 0;
_objDaysOfWeek[ DateUtils.MONDAY ] = 1;
_objDaysOfWeek[ DateUtils.TUESDAY ] = 2;
_objDaysOfWeek[ DateUtils.WEDNESDAY ] = 3;
_objDaysOfWeek[ DateUtils.THURSDAY ] = 4;
_objDaysOfWeek[ DateUtils.FRIDAY ] = 5;
_objDaysOfWeek[ DateUtils.SATURDAY ] = 6;
}
return _objDaysOfWeek;
}
// a generic object for holding month values
private static var _objMonth:Object = null;
public static function get objMonth():Object {
if ( !_objMonth ) {
_objMonth = {};
_objMonth[ DateUtils.JANUARY ] = 0;
_objMonth[ DateUtils.FEBRUARY ] = 1;
_objMonth[ DateUtils.MARCH ] = 2;
_objMonth[ DateUtils.APRIL ] = 3;
_objMonth[ DateUtils.MAY ] = 4;
_objMonth[ DateUtils.JUNE ] = 5;
_objMonth[ DateUtils.JULY ] = 6;
_objMonth[ DateUtils.AUGUST ] = 7;
_objMonth[ DateUtils.SEPTEMBER ] = 8;
_objMonth[ DateUtils.OCTOBER ] = 9;
_objMonth[ DateUtils.NOVEMBER ] = 10;
_objMonth[ DateUtils.DECEMBER ] = 11;
}
return _objMonth;
}
public function DateUtils() {
}
/**
*
* 添加或减少天数
*/
public static function addDay(date:Date,days:int):Date
{
if(date!=null)
{
date.setDate(days);
}
return date;
}
/**
* 两个日期相差的天数
*
*/
public static function daysOfDates(date0:Date,date1:Date):int
{
var days0:Number=date0.time;
var days1:Number=date1.time;
var days:Number=(days0-days1)/DAY_VALUE;
return days;
}
/**
* 判断时间是否在开始和结束时间之间
*
*/
public static function isBetween(fromdate:Date,todate:Date,current:Date):Boolean
{
if(ObjectUtil.dateCompare(fromdate,current)!==1&&ObjectUtil.dateCompare(todate,current)!=-1)
{
return true;
}
return false;
}
/**
*位于某个时间之后
*/
public static function after(date0:Date,date1:Date):Boolean
{
if(ObjectUtil.dateCompare(date0,date1)==1)
{
return true;
}
return false;
}
/**
*位于某个时间之前
*/
public static function before(date0:Date,date1:Date):Boolean
{
if(ObjectUtil.dateCompare(date0,date1)==-1)
{
return true;
}
return false;
}
/**
*两个时间相等
*/
public static function equals(date0:Date,date1:Date):Boolean
{
if(ObjectUtil.dateCompare(date0,date1)==0)
{
return true;
}
return false;
}
/**
* @private
*
* This function will remove any invalid characters from the date/time mask based upon a pattern
*
* @param mask The string for matching
* @param pattern The valid characters for this mask
* @param defaultValue The default value to return to the calling page should the mask not match the pattern
*
* @return Returns a validated <code>mask</code> based upon the original pattern
*/
private static function removeInvalidDateTimeCharacters( mask:String, pattern:String, defaultValue:String ):String {
// test for invalid date and time characters
if ( mask.replace( new RegExp( pattern, "ig" ), "" ).length > 0 ) {
// if user is passing an invalid mask, default to defaultValue
mask = defaultValue;
}
// temporarily replace TZD with lowercase tzd for replacing later
return mask.replace( new RegExp( "TZD", "i" ), "tzd" );
}
/**
* Formats a date into a certain date/time format
*
* @param date The date to format
* @param mask How the date should be formatted
*
* @return A formatted date
*/
public static function dateTimeFormat( date:Date, mask:String="MM/DD/YYYY L:NN:SS A" ):String {
return buildDateTime( date, mask, "(Y|M|D|E|A|J|H|K|L|N|S|TZD|//W)+", DateUtils.SHORT_DATE_MASK + ' ' + DateUtils.SHORT_TIME_MASK );
}
/**
* Formats a time into a certain time format
*
* @param date The date to format
* @param mask How the date should be formatted
*
* @return A formatted time
*/
public static function timeFormat( date:Date, mask:String=DateUtils.SHORT_TIME_MASK ):String {
return buildDateTime( date, mask, "(A|:|J|H|K|L|N|S|TZD|//s)+", DateUtils.SHORT_TIME_MASK );
}
/**
* Formats a date into a certain date format
*
* @param date The date to format
* @param mask How the date should be formatted
*
* @return A formatted date
*/
public static function dateFormat( date:Date, mask:String=DateUtils.SHORT_DATE_MASK ):String {
return buildDateTime( date, mask, "(Y|M|D|E|//W)+", DateUtils.SHORT_DATE_MASK );
}
/**
* @private
*
* Formats a date into a certain date/time format
*
* @param date The date to format
* @param mask The string for matching
* @param pattern The valid characters for this mask
* @param defaultValue The default value to return to the calling page should the mask not match the pattern
*
* @return A formatted date
*/
private static function buildDateTime( date:Date, mask:String, pattern:String, defaultValue:String ):String {
dateFormatter.formatString = removeInvalidDateTimeCharacters( mask, pattern, defaultValue );
return dateFormatter.format( date ).replace( new RegExp( "TZD", "i" ), buildTimeZoneDesignation( date ) );
}
/**
* @private
*
* Calculates a timeZoneOffset, and converts it to a string, in standard GMT XX:XX format
*
* @param date The date on which to calculate the offset
*
* @return The formatted time zone designation
*/
private static function buildTimeZoneDesignation( date:Date ):String {
if ( !date ) {
return "";
}
var timeZoneAsString:String = "GMT ";
// timezoneoffset is the number that needs to be added to the local time to get to GMT, so
// a positive number would actually be GMT -X hours
if ( date.getTimezoneOffset() / 60 > 0 && date.getTimezoneOffset() / 60 < 10 ) {
timeZoneAsString += "-0" + ( date.getTimezoneOffset() / 60 ).toString();
} else if ( date.getTimezoneOffset() < 0 && date.timezoneOffset / 60 > -10 ) {
timeZoneAsString += "0" + ( -1 * date.getTimezoneOffset() / 60 ).toString();
}
// add zeros to match standard format
timeZoneAsString += "00";
return timeZoneAsString;
}
/**
* Adds the specified number of "date parts" to a date, e.g. 6 days
*
* @param datePart The part of the date that will be added
* @param number The total number of "dateParts" to add to the date
* @param date The date on which to add
*
* @return The new date
*/
public static function dateAdd( datePart:String, number:Number, date:Date ):Date {
var _returnDate:Date = new Date( date );
switch ( datePart ) {
case DateUtils.YEAR:
case DateUtils.MONTH:
case DateUtils.DAY_OF_MONTH:
case DateUtils.HOURS:
case DateUtils.MINUTES:
case DateUtils.SECONDS:
case DateUtils.MILLISECONDS:
_returnDate[ datePart ] += number;
break;
case DateUtils.WEEK:
_returnDate[ DateUtils.DAY_OF_MONTH ] += number * 7;
break;
default:
/* Unknown date part, do nothing. */
break;
}
return _returnDate;
}
/**
* Gets the day of the week
*
* @param date The date for which to get the day of the week
*
* @return A number representing the day of the week, 0 to 6
*/
public static function dayOfWeek( date:Date ):Number {
return date.getDay();
}
/**
* Gets the ordinal value or day of the year
*
* @param date The date for which to get the day of the year
*
* @return A number representing the day of the year, 1 to 365 or 366 for a leap year
*/
public static function dayOfYear( date:Date ):Number {
// add one as it has to include first of year
return DateUtils.dateDiff( DateUtils.DAY_OF_MONTH, new Date( date.fullYear, DateUtils.monthAsNumber( DateUtils.JANUARY ), 1 ), date ) + 1;
}
/**
* Gets the week of the year
*
* @param date The date for which to get the week of the year
*
* @return A number representing the week of the year, 1 to 53 ( as there are slightly more than 52 weeks of days in a year)
*/
public static function weekOfYear( date:Date ):Number {
return Math.ceil( DateUtils.dayOfYear( date ) / 7 );
}
/**
* Converts the day of the week to a Flex day of the week
*
* @param date The human readable day of week
*
* @return The Flex converted day of week or 0 aka Sunday
*/
public static function toFlexDayOfWeek( localDayOfWeek:Number ):Number {
return ( localDayOfWeek > 0 && localDayOfWeek < 8 ) ? localDayOfWeek - 1 : 0;
}
/**
* Gets the Xth day of the month.
* e.g. get the 3rd Wednesday of the month
*
* @param iteration The iteration of the month to get e.g. 4th or Last
* @param strDayOfWeek The day of the week as a string
* @param date The date containing the month and year
*
* @return The date of the xth dayOfWeek of the month
*/
public static function dayOfWeekIterationOfMonth( iteration:Number, strDayOfWeek:String, date:Date ):Date {
// get the numeric day of the week for the requested day
var _dayOfWeek:Number = dayOfWeekAsNumber( strDayOfWeek );
// get the date for the first of the month
var _firstOfMonth:Date = new Date( date.fullYear, date.month, 1 );
// calculate how many days to add to get to the requested day from the first of the month
var _daysToAdd:Number = _dayOfWeek - DateUtils.dayOfWeek( _firstOfMonth );
// if dayOfWeek is before the first of the month, get the dayOfWeek for the following week
if ( _daysToAdd < 0 ) {
_daysToAdd += 7;
}
// set the date to the first day of the week for the requested date
var _firstDayOfWeekOfMonth:Date = DateUtils.dateAdd( DateUtils.DAY_OF_MONTH, _daysToAdd, _firstOfMonth );
// return the date if iteration is 1
if ( iteration == 1 ) {
return _firstDayOfWeekOfMonth;
} else {
// if requesting an iteration that is more than is in that month or requesting the last day of week of month
// return last date for that day of week of month
if ( ( DateUtils.totalDayOfWeekInMonth( strDayOfWeek, date ) < iteration ) || ( iteration == DateUtils.LAST ) ) {
iteration = DateUtils.totalDayOfWeekInMonth( strDayOfWeek, date );
}
// subtract 1 as it starts from the first dayOfWeek of month
return DateUtils.dateAdd( DateUtils.WEEK, iteration - 1, _firstDayOfWeekOfMonth );
}
}
/**
* Gets the days in the month
*
* @param date The date to check
*
* @return The number of days in the month
*/
public static function daysInMonth( date:Date ):Number {
// get the first day of the next month
var _localDate:Date = new Date( date.fullYear, DateUtils.dateAdd( DateUtils.MONTH, 1, date ).month, 1 );
// subtract 1 day to get the last day of the requested month
return DateUtils.dateAdd( DateUtils.DAY_OF_MONTH, -1, _localDate ).date;
}
/**
* Gets the total number of dayOfWeek in the month
*
* @param strDayOfWeek The day of week to check
* @param date The date containing the month and year
*
* @return The number of <code>strDayOfWeek</code> in that month and year
*/
public static function totalDayOfWeekInMonth( strDayOfWeek:String, date:Date ):Number {
var _startDate:Date = DateUtils.dayOfWeekIterationOfMonth( 1, strDayOfWeek, date );
var _totalDays:Number = DateUtils.dateDiff( DateUtils.DAY_OF_MONTH, _startDate, new Date( date.fullYear, date.month, DateUtils.daysInMonth( date ) ) );
// have to add 1 because have to include first day that is found i.e. if wed is on 2nd of 31 day month, would total 5, of if wed on 6th, would total 4
return Math.floor( _totalDays / 7 ) + 1;
}
/**
* Converts the month to a Flex month
*
* @param date The human readable month
*
* @return The Flex converted month or 0 aka January
*/
public static function toFlexMonth( localMonth:Number ):Number {
return ( localMonth > 0 && localMonth < 13 ) ? localMonth - 1 : 0;
}
/**
* Determines whether a value is actually a valid date
*
* @param value The date value
*
* @return <code>true</code> means this is a valid date, <code>false</code> means it is not a valid date
*/
public static function isDate( value:String ):Boolean {
return Date.parse( value ) > 0;
}
/**
* Formats a date to the string version of the day of the week
*
* @param date The date to format
*
* @return A formatted day of week
*/
public static function dayOfWeekAsString( date:Date ):String {
return DateUtils.dateFormat( date, "EEEE" );
}
/**
* Formats a date to the numeric version of the day of the week
*
* @param strDayOfWeek The day of week to convert
*
* @return A formatted day of week or -1 if day not found
*/
public static function dayOfWeekAsNumber( strDayOfWeek:String ):Number {
return ( objDaysOfWeek[ strDayOfWeek ] >= 0 ) ? objDaysOfWeek[ strDayOfWeek ] : -1;
}
/**
* Formats a date to the string version of the month
*
* @param date The date to format
*
* @return A formatted month
*/
public static function monthAsString( date:Date ):String {
return DateUtils.dateFormat( date, "MMMM" );
}
/**
* Formats a month to the numeric version of the month
*
* @param strMonth The month to convert
*
* @return A formatted month or -1 if month not found
*/
public static function monthAsNumber( strMonth:String ):Number {
return ( objMonth[ strMonth ] >= 0 ) ? objMonth[ strMonth ] : -1;
}
/**
* Gets the number of days in the year
*
* @param date The date to check
*
* @return The total number of days in the year
*/
public static function daysInYear( date:Date ):Number {
return DateUtils.dateDiff(
DateUtils.DAY_OF_MONTH,
new Date( date.fullYear, DateUtils.monthAsNumber( DateUtils.JANUARY ), 1 ),
DateUtils.dateAdd( DateUtils.YEAR, 1, new Date( date.fullYear, DateUtils.monthAsNumber( DateUtils.JANUARY ), 1 ) ) );
}
/**
* Determines whether the year is a leap year or not
*
* @param date The date to check
*
* @return <code>true</code> means it is a leap year, <code>false</code> means it is not a leap year.
*/
public static function isLeapYear( date:Date ):Boolean {
return daysInYear( date ) > 365;
}
/**
* Determines the number of "dateParts" difference between 2 dates
*
* @param datePart The part of the date that will be checked
* @param startDate The starting date
* @param endDate The ending date
*
* @return The number of "dateParts" difference
*/
public static function dateDiff( datePart:String, startDate:Date, endDate:Date ):Number {
var _returnValue:Number = 0;
switch ( datePart ) {
case DateUtils.MILLISECONDS:
_returnValue = endDate.time - startDate.time;
break;
case DateUtils.SECONDS:
_returnValue = Math.floor( DateUtils.dateDiff( DateUtils.MILLISECONDS, startDate, endDate ) / DateUtils.SECOND_VALUE );
break;
case DateUtils.MINUTES:
_returnValue = Math.floor( DateUtils.dateDiff( DateUtils.MILLISECONDS, startDate, endDate ) / DateUtils.MINUTE_VALUE );
break;
case DateUtils.HOURS:
_returnValue = Math.floor( DateUtils.dateDiff( DateUtils.MILLISECONDS, startDate, endDate ) / DateUtils.HOUR_VALUE );
break;
case DateUtils.DAY_OF_MONTH:
// TODO: Need to figure out DST problem i.e. 23 hours at DST start, 25 at end.
// Math.floor causes rounding down error with DST start at dayOfYear
_returnValue = Math.floor( DateUtils.dateDiff( DateUtils.MILLISECONDS, startDate, endDate ) / DateUtils.DAY_VALUE );
break;
case DateUtils.WEEK:
_returnValue = Math.floor( DateUtils.dateDiff( DateUtils.MILLISECONDS, startDate, endDate ) / DateUtils.WEEK_VALUE );
break;
case DateUtils.MONTH:
// if start date is after end date, switch values and take inverse of return value
if ( DateUtils.dateDiff( DateUtils.MILLISECONDS, startDate, endDate ) < 0 ) {
_returnValue -= DateUtils.dateDiff( DateUtils.MONTH, endDate, startDate );
} else {
// get number of months based upon years
_returnValue = DateUtils.dateDiff( DateUtils.YEAR, startDate, endDate ) * 12;
// subtract months then perform test to verify whether to subtract one month or not
// the check below gets the correct starting number of months (but may need to have one month removed after check)
if ( endDate.month != startDate.month ) {
_returnValue += ( endDate.month <= startDate.month ) ? 12 - startDate.month + endDate.month : endDate.month - startDate.month;
}
// have to perform same checks as YEAR
// i.e. if end date day is <= start date day, and end date milliseconds < start date milliseconds
if ( ( endDate[ DateUtils.DAY_OF_MONTH ] < startDate[ DateUtils.DAY_OF_MONTH ] ) ||
( endDate[ DateUtils.DAY_OF_MONTH ] == startDate[ DateUtils.DAY_OF_MONTH ] &&
endDate[ DateUtils.MILLISECONDS ] < startDate[ DateUtils.MILLISECONDS ] ) ) {
_returnValue -= 1;
}
}
break;
case DateUtils.YEAR:
_returnValue = endDate.fullYear - startDate.fullYear;
// this fixes the previous problem with dates that ran into 2 calendar years
// previously, if 2 dates were in separate calendar years, but the months were not > 1 year apart, then it was returning too many years
// e.g. 11/2008 to 2/2009 was returning 1, but should have been returning 0 (zero)
// if start date before end date and months are less than 1 year apart, add 1 to year to fix offset issue
// if end date before start date and months are less than 1 year apart, subtract 1 year to fix offset issue
// added month and milliseconds check to make sure that a date that was e.g. 9/11/07 9:15AM would not return 1 year if the end date was 9/11/08 9:14AM
if ( _returnValue != 0 ) {
// if start date is after end date
if ( _returnValue < 0 ) {
// if end date month is >= start date month, and end date day is >= start date day, and end date milliseconds > start date milliseconds
if ( ( endDate[ DateUtils.MONTH ] > startDate[ DateUtils.MONTH ] ) ||
( endDate[ DateUtils.MONTH ] == startDate[ DateUtils.MONTH ] && endDate[ DateUtils.DAY_OF_MONTH ] > startDate[ DateUtils.DAY_OF_MONTH ] ) ||
( endDate[ DateUtils.MONTH ] == startDate[ DateUtils.MONTH ] && endDate[ DateUtils.DAY_OF_MONTH ] == startDate[ DateUtils.DAY_OF_MONTH ] &&
endDate[ DateUtils.MILLISECONDS ] > startDate[ DateUtils.MILLISECONDS ] ) ) {
_returnValue += 1;
}
} else {
// if end date month is <= start date month, and end date day is <= start date day, and end date milliseconds < start date milliseconds
if ( ( endDate[ DateUtils.MONTH ] < startDate[ DateUtils.MONTH ] ) ||
( endDate[ DateUtils.MONTH ] == startDate[ DateUtils.MONTH ] && endDate[ DateUtils.DAY_OF_MONTH ] < startDate[ DateUtils.DAY_OF_MONTH ] ) ||
( endDate[ DateUtils.MONTH ] == startDate[ DateUtils.MONTH ] && endDate[ DateUtils.DAY_OF_MONTH ] == startDate[ DateUtils.DAY_OF_MONTH ] &&
endDate[ DateUtils.MILLISECONDS ] < startDate[ DateUtils.MILLISECONDS ] ) ) {
_returnValue -= 1;
}
}
}
break;
}
return _returnValue;
}
}
}