AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 09.11.2006, 17:22   #1  
NJD is offline
NJD
Участник
 
210 / 26 (1) +++
Регистрация: 21.05.2003
Адрес: Moscow
Функция, вычисляющая разницу между датами.
Есть 2 даты на входе. Нужно выводить разницу между ними в формате столько то лет, столько то месяцев, столько то дней. Никто не поделится? А то самому нет времени писать. Спасибо.
Старый 09.11.2006, 17:30   #2  
Lucky13 is offline
Lucky13
Участник
1C
 
714 / 198 (8) ++++++
Регистрация: 21.10.2004
container dateDiff(date d1, date d2)
{
return [Global::yearDiff(d1,d2), Global::MthDiff(d1,d2), d2 - d1];
}
Старый 09.11.2006, 18:06   #3  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от NJD Посмотреть сообщение
Есть 2 даты на входе. Нужно выводить разницу между ними в формате столько то лет, столько то месяцев, столько то дней. Никто не поделится? А то самому нет времени писать.
Писать ничего не надо, есть стандартные функции ядра по работе с интервалами дат (см., например, тут, раздел Interval):
X++:
date    d1 = str2date("1996.12.21", 321);
date    d2 = str2date("1994.11.18", 321);
;
info(strfmt("distance is %1 year(s), %2 month(s) and %3 day(s)",
            intvNo(d1, d2, IntvScale::Year), 
            intvNo(d1, d2, IntvScale::Month),
            intvNo(d1, d2, IntvScale::Day)));
Старый 09.11.2006, 19:46   #4  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от gl00mie Посмотреть сообщение
X++:
date    d1 = str2date("1996.12.21", 321);
date    d2 = str2date("1994.11.18", 321);
1. В принципе согласен.
2. Но хотел бы чуть-чуть добавить: Вместо str2date(...) можно писать литерал 21\12\1996
__________________
полезное на axForum, github, vk, coub.
Старый 10.11.2006, 08:02   #5  
NJD is offline
NJD
Участник
 
210 / 26 (1) +++
Регистрация: 21.05.2003
Адрес: Moscow
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Писать ничего не надо, есть стандартные функции ядра по работе с интервалами дат (см., например, тут, раздел Interval):
X++:
date    d1 = str2date("1996.12.21", 321);
date    d2 = str2date("1994.11.18", 321);
;
info(strfmt("distance is %1 year(s), %2 month(s) and %3 day(s)",
            intvNo(d1, d2, IntvScale::Year), 
            intvNo(d1, d2, IntvScale::Month),
            intvNo(d1, d2, IntvScale::Day)));
спасибо, но если даты к примеру 2000.10.20 и 2000.09.30 то выдаст 1 месяц и -10 дней.ведь так? - если да, корявенько ...
Старый 10.11.2006, 08:44   #6  
petergunn is offline
petergunn
Участник
 
118 / 274 (10) ++++++
Регистрация: 30.08.2005
Адрес: Tyumen
RHRMDateDiff
Можете еще посмотреть класс RHRMDateDiff:

RHRMDateDiff rhrmDateDiff = new RHRMDateDiff( 21\01\2006, 12\03\2007 ) ;
info( strfmt( "%1, %2, %3", rhrmDateDiff.getYears(), rhrmDateDiff.getMonths(), rhrmDateDiff.getDays() ) ) ;

Результат : 1, 1, 22

Последний раз редактировалось petergunn; 10.11.2006 в 08:51.
Старый 10.11.2006, 09:31   #7  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Cool
Цитата:
Сообщение от petergunn Посмотреть сообщение
Можете еще посмотреть класс RHRMDateDiff
Увы, не всё там гладко в RHRMDateDiff...
Следующий джоб:
X++:
static void KKu_Job_6B10_TestDateDiff(Args _args)
{
    void getDateDiff(date _dateBeg, date _dateEnd)
    {
        RHRMDateDiff dd;
        dd = new RHRMDateDiff(_dateBeg, _dateEnd);
        info(strfmt("distance is %1 year(s), %2 month(s) and %3 day(s)",
                        dd.getYears(), dd.getMonths(), dd.getDays() ));
    }
 
    getDateDiff(31\01\2005, 29\04\2006);
    getDateDiff(31\01\2005, 30\04\2006);
    getDateDiff(31\01\2005, 01\05\2006);
 
    info('---------------------------');
 
    getDateDiff(31\01\2004, 27\02\2004);
    getDateDiff(31\01\2004, 28\02\2004);
    getDateDiff(31\01\2004, 29\02\2004);
    getDateDiff(31\01\2004, 01\03\2004);
}
выдает в инфолог:
Код:
distance is 1 year(s), 2 month(s) and 29 day(s)
distance is 1 year(s), 2 month(s) and 30 day(s)
distance is 1 year(s), 3 month(s) and 1 day(s)
---------------------------
distance is 0 year(s), 0 month(s) and 27 day(s)
distance is 0 year(s), 0 month(s) and 28 day(s)
distance is 0 year(s), 0 month(s) and 29 day(s)
distance is 0 year(s), 1 month(s) and 1 day(s)
Спрашивается в задачке: а где "3 месяца и 0 дней" и "1 месяц и 0 дней" соответственно?
Старый 09.11.2006, 18:53   #8  
itfs is offline
itfs
Участник
 
277 / 43 (2) +++
Регистрация: 18.07.2005
Адрес: Moscow
08.12.2006 - 09.11.2006
Результат:
distance is 0 year(s), 1 month(s) and -1 day(s)

С уважением, itfs.
Старый 10.11.2006, 12:09   #9  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от itfs Посмотреть сообщение
08.12.2006 - 09.11.2006
Результат:
distance is 0 year(s), 1 month(s) and -1 day(s)
Да, точно все оказалось несколько сложнее... Вот чуть измененный вариант именования количества периодов
X++:
// возвращает строку "кол-во периодов"
static str PeriodSklInt(IntvScale _period, int _count)
{
    str _s;
    ;
    if (!_count) return "";
    if (_count < 0) _count = -_count;
    if ((_count mod 100<10 || _count mod 100>20) && _count mod 10==1)
        switch (_period)
        {
            case IntvScale::Day     : _s = "день"; break;
            case IntvScale::Week    : _s = "неделя"; break;
            case IntvScale::Month   : _s = "месяц"; break;
            case IntvScale::Quarter : _s = "квартал"; break;
            case IntvScale::Year    : _s = "год"; break;
            default                 : _s = "период"; break;
        }
    else if ((_count mod 100<10 || _count mod 100>20) && _count mod 10>1 && _count mod 10<=4)
        switch (_period)
        {
            case IntvScale::Day     : _s = "дня"; break;
            case IntvScale::Week    : _s = "недели"; break;
            case IntvScale::Month   : _s = "месяца"; break;
            case IntvScale::Quarter : _s = "квартала"; break;
            case IntvScale::Year    : _s = "года"; break;
            default                 : _s = "периода"; break;
        }
    else
        switch (_period)
        {
            case IntvScale::Day     : _s = "дней"; break;
            case IntvScale::Week    : _s = "недель"; break;
            case IntvScale::Month   : _s = "месяцев"; break;
            case IntvScale::Quarter : _s = "кварталов"; break;
            case IntvScale::Year    : _s = "лет"; break;
            default                 : _s = "периодов"; break;
        }
    return strfmt("%1 %2",_count, _s);
}
а вот подправленная функция расчета интервала
X++:
static str myDateDiff(date _d1, date _d2, boolean _include = false)
{
    date    m_d1, m_d2;             // m_d2 >= m_d1
    str     _s;
    int     m_year, m_mth, m_days;
    ;
    if(_d1<_d2)
    {
        m_d1 = _d1;
        m_d2 = _d2;
    } else
    {
        m_d1 = _d2;
        m_d2 = _d1;
    }
    m_year  = yearDiff(m_d2,m_d1);
    m_mth   = mthOfYr(m_d2) - mthOfYr(m_d1);
    if (m_mth < 0)
        m_mth += 12;
    m_days = dayOfMth(m_d2) - dayOfMth(m_d1);
    if (m_days < 0)
    {
        m_mth--;
        m_days += dayOfMth(dateEndMth(m_d1));
    }
    if (m_mth < 0)
        m_mth += 12;
    if (_include) m_days++;
    if (m_days == dayOfMth(dateEndMth(m_d2)))
    {
        m_mth++;
        m_days = 0;
    }
    _s = strltrim(strrtrim(strfmt("%1 %2 %3",   PeriodSklInt(IntvScale::Year,  m_year),
                                                PeriodSklInt(IntvScale::Month, m_mth),
                                                PeriodSklInt(IntvScale::Day,   m_days))));
    return _s;
}
Теперь следующий код
X++:
info("distance is " + myDateDiff(10\09\2005, 01\10\2006));
info("distance is " + myDateDiff(10\10\2005, 09\10\2006));
info("distance is " + myDateDiff(31\01\2005, 29\04\2006));
info("distance is " + myDateDiff(31\01\2005, 30\04\2006));
info("distance is " + myDateDiff(31\01\2005, 01\05\2006));
info('---------------------------');
info("distance is " + myDateDiff(31\01\2004, 27\02\2004));
info("distance is " + myDateDiff(31\01\2004, 28\02\2004));
info("distance is " + myDateDiff(31\01\2004, 29\02\2004));
info("distance is " + myDateDiff(31\01\2004, 01\03\2004));
выдает в infolog
PHP код:
distance is 1 год  21 день
distance is 11 месяцев 30 дней
distance is 1 год 2 месяца 29 дней
distance is 1 год 3 месяца
distance is 1 год 3 месяца 1 день
---------------------------
distance is 27 дней
distance is 28 дней
distance is 1 месяц
distance is 1 месяц 1 день 

Последний раз редактировалось gl00mie; 10.11.2006 в 12:20.
За это сообщение автора поблагодарили: itfs (1).
Старый 10.11.2006, 17:38   #10  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
? Есть ли в Axapta функция DateAdd ?
Даа... Насколько компактнее и прозрачнее получается алгоритм на VBA при использовании функции DateAdd:
Код:
Sub VBA_TestDifferenceBetweenTwoDates()
    Dim dateBeg As Date, dateEnd As Date, dateTest As Date
    Dim y1 As Integer, m1 As Integer, y2 As Integer, m2 As Integer
    Dim ys As Integer, ms As Integer, ds As Integer
 
    dateBeg = DateSerial(2006, 1, 31)
    dateEnd = DateSerial(2006, 4, 30)
 
    y1 = Year(dateBeg): m1 = Month(dateBeg)
    y2 = Year(dateEnd): m2 = Month(dateEnd)
 
    ms = (y2 * 12 + m2) - (y1 * 12 + m1)  'предварительная разница в месяцах
    dateTest = DateAdd("m", ms, dateBeg)  'пробная дата на ms месяцев от начала
    ds = DateDiff("d", dateTest, dateEnd) 'предварит-ная разница в днях
                                          'в пределах последнего месяца (+/-)
 
    If ds < 0 Then 'ЕСЛИ предварит-но "перебрали" с целыми месяцами от начала
        ms = ms - 1                           'ТО окончат-ная разница в месяцах
        dateTest = DateAdd("m", ms, dateBeg)  'новая "пробная" дата
        ds = DateDiff("d", dateTest, dateEnd) 'окончательная разница в днях (+)
    End If
 
    ys = ms \ 12: ms = ms Mod 12 'годы - тривиально от месяцев
 
    Debug.Print ys, ms, ds '<- Годы, Месяцы, Дни разницы соответственно
 
    'или еще и недели внутри месяца :-)
    Debug.Print ys, ms, ds \ 7, ds Mod 7 '<- Годы, Месяцы, Недели, Дни разницы
End Sub
Вместо функции DateDiff для дней можно использовать и просто операцию вычитания между двумя датами.
А вот есть ли в Аксе приемлемый аналог функции DateAdd ?

P.S. 30.01.07
Как выяснилось здесь, для месяцев - есть. В Global, называется dateMthFwd.
Старый 09.11.2006, 19:18   #11  
itfs is offline
itfs
Участник
 
277 / 43 (2) +++
Регистрация: 18.07.2005
Адрес: Moscow
Нашел таки, вот 2 функции из одного класса:
X++:
static str MydateDiff(transDate _d1, transDate _d2, boolean _include = False)
{
  str _s;
  int M_year, m_mth, m_days;
  ;
  M_year = Global::yearDiff(_d2,_d1);
  M_mth = mthofYr(_d2) - mthofYr(_d1);
  if (M_mth < 0)
  {
    M_mth+=12;
  }

  M_days = DayOfmth(_d2) - DayOfmth(_d1);
  if (M_days < 0)
  {
    M_mth--;
    M_days += DayOfmth(dateEndMth(_d1));
  }

  if (_include) M_days++;

  _s = strfmt("%1 %2 %3", PeriodInterval::PeriodSklInt(IntvScale::Year, M_year),
                          PeriodInterval::PeriodSklInt(IntvScale::Month, M_mth),
                          PeriodInterval::PeriodSklInt(IntvScale::Day, M_days) );

  Return _s;
}
X++:
static Str PeriodSklInt(IntvScale: _Period, int _Count)
{  // возвращает строку "кол-во периодов"
 str _s;
 ;
 if (_Count)
 {
   if (_Count == 1)
   {
     switch (_Period)
       {
       case IntvScale::Day : _s = "день"; break;
       case IntvScale::Week : _s = "неделя"; break;
       case IntvScale::Month : _s = "месяц"; break;
       case IntvScale::Quarter : _s = "квартал"; break;
       case IntvScale:Year : _s = "год"; break;
       default : _s = "период"; break;
       }
   }
   else if ((_Count <= 4))
   {
     switch (_Period)
       {
       case IntvScale::Day : _s = "дня"; break;
       case IntvScale::Week : _s = "недели"; break;
       case IntvScale::Month : _s = "месяца"; break;
       case IntvScale::Quarter : _s = "квартала"; break;
       case IntvScale::Year : _s = "года"; break;
       default : _s = "периода"; break;
       }
   }
   else
   {
     switch (_Period)
       {
       case IntvScale::Day : _s = "дней"; break;
       case IntvScale::Week : _s = "недель"; break;
       case IntvScale::Month : _s = "месяцев"; break;
       case IntvScale::Quarter : _s = "кварталов"; break;
       case IntvScale::Year : _s = "лет"; break;
       default : _s = "периодов"; break;
       }
   }
   Return strfmt("%1 %2",_Count, _s);
 }
 else Return "";
}
}
C уважением, itfs.
За это сообщение автора поблагодарили: gl00mie (1).
Старый 11.11.2006, 07:59   #12  
NJD is offline
NJD
Участник
 
210 / 26 (1) +++
Регистрация: 21.05.2003
Адрес: Moscow
Порылся в стандарте - есть такие фишки - например из печатной формы Т2

workDate = str2date(emplTable.SeniorityDate_RU(false, ""), 123);

if (workDate)
{
dateDiff = new RHRMDateDiff(workDate, Today());
if (dateDiff.getValidate())
{
if (dateDiff.getDays() > 0)
{
worddocument.insertFieldValue("ET_SenDay",dateDiff.getDays());
}
if (dateDiff.getMonths() > 0)
{
worddocument.insertFieldValue("ET_SenMth",dateDiff.getMonths());
}
if (dateDiff.getYears() > 0)
{
worddocument.insertFieldValue("ET_SenYr",dateDiff.getYears());
}
}
}
Старый 11.11.2006, 18:49   #13  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Еще одна разность между двумя датами в годах, месяцах, днях (и не только)
Цитата:
Сообщение от Gustav Посмотреть сообщение
А вот есть ли в Аксе приемлемый аналог функции DateAdd ?
Может быть и есть где-то глубоко в Аксапте функция а-ля DateAdd, но я уже сваял свою, предназначенную только для интервала типа "месяц" (а в предложенном вчера VBA-шном алгоритме больше и не требуется - да и хорошо). Моя маленькая функция DateAddMonths корректно обрабатывает концовки месяцев, так же как и фирменные DateAdd из VBA и SQL Server.
Следующий джоб представляет эту функцию и небольшой тест для нее:
X++:
static void KKu_Job_6B11_TestDateAddMonths(Args _args)
{
    int d, m;
    date dam;
 
    // --------------------------------------------------------------------------------------
    // Returns a date containing a date to which a specified number of months has been added;
    // _months - number of months you want to add, it can be positive (to get dates in the future) or negative (to get dates in the past);
    // _date - date to which the months are added;
    date DateAddMonths(int _months, date _date)
    {
        int y2, m2, d2, ms;
        ms = ( year(_date) * 12 + mthOfYr(_date) ) + _months;
        y2 = (ms-1) div 12;
        m2 = ((ms-1) mod 12) + 1;
        d2 = min( dayOfMth(_date), dayOfMth( endMth( mkDate(1, m2, y2))) );
        return mkDate(d2, m2, y2);
    }
    // --------------------------------------------------------------------------------------
    ;
    
    // тестирование функции DateAddMonths на "критических" днях
    for (d=28; d<= 31; d++) // последние дни месяца (март 2004)
    {
        for (m=-12; m<= 12; m++) // плюс-минус год помесячно
        {
            dam = DateAddMonths( m, mkDate(d,3,2004));
            if (d != dayOfMth(dam)) // для пущей наглядности выводим в инфолог только те даты, в которых значения дней не совпадают
                info( strfmt('от даты %1.03.04 отстоит ровно на %2 месяц. дата %3', d, m, dam) );
        }
    }
}
(пижонские комментарии на английском творчески спионерены из хелпа по VBA)

Ну и раз появилась такая функция, значит появилась и возможность свой вчерашний алгоритм воплотить в X++. Что я и делаю, теперь уже полноценно присоединяясь своим "пятачком" к вкладу других коллег по ветке. Нижеследующий джоб использует рассмотренную выше функцию DateAddMonths для нужд другой функции - DateDiffYMDWD, которая для заданных начальной и конечной даты возвращает контейнер из пяти значений ("Year,Month,Day,Week,Day"), характеризующих разность между этими датами (см. комментарии в коде):
X++:
static void KKu_Job_6B11_TestDateDiff_2(Args _args)
{
    container c;
     
    date DateAddMonths(int _months, date _date)
    {
        int y2, m2, d2, ms;
        ms = ( year(_date) * 12 + mthOfYr(_date) ) + _months;
        y2 = (ms-1) div 12;
        m2 = ((ms-1) mod 12) + 1;
        d2 = min( dayOfMth(_date), dayOfMth( endMth( mkDate(1, m2, y2))) );
        return mkDate(d2, m2, y2);
    }
     
    //------------------------------------------------------------------------------------------
    // разность между двумя датами в годах, месяцах, днях/неделях-днях
    // если конечная дата меньше начальной, то вычисляется разность назад (-)
    container DateDiffYMDWD(date _dateBeg, date _dateEnd)
    {
        date dtBeg, dtEnd, dtTest;
        int ms, ds, sgn;
    
        if (_dateBeg <= _dateEnd) 
        {
            dtBeg = _dateBeg; dtEnd = _dateEnd; sgn = 1;
        }
        else // даты - наоборот, т.е. будет вычислена разность назад
        {
            dtBeg = _dateEnd; dtEnd = _dateBeg; sgn = -1;
        }
    
        ms = ( year(dtEnd) * 12 + mthOfYr(dtEnd) )
           - ( year(dtBeg) * 12 + mthOfYr(dtBeg) ); // предварительная разница в месяцах
    
        dtTest = DateAddMonths(ms, dtBeg); // пробная дата на ms месяцев вперёд от начала
        ds = dtEnd - dtTest; // предварит-ная разница в днях в пределах последнего месяца (+/-)
    
        if (ds < 0) //ЕСЛИ предварит-но "перебрали" с полными месяцами от начала
        {
            ms = ms - 1;                 //ТО окончат-ная разница в месяцах
            dtTest = DateAddMonths(ms, dtBeg);  // новая "пробная" дата
            ds = dtEnd - dtTest;    //окончательная разница в днях (+)
        }
    
        return [(sgn*ms) div 12,   // полные годы разницы - от 0 до бесконечности
                (sgn*ms) mod 12,   // полные месяцы (свыше полных лет) - от 0 до 11
                (sgn*ds)       ,   // дни неполного месяца (свыше полных месяцев) - от 0 до 30
                (sgn*ds) div 7 ,   // недели месяца (свыше полных месяцев) - от 0 до 4
                (sgn*ds) mod 7 ,   // дни неполной недели (свыше полных недель) - от 0 до 6
                (sgn*(dtEnd - dtBeg)), // общей кол-во дней разности - от от 0 до бесконечности
                (sgn*(dtEnd - DateAddMonths(ms - (ms mod 12), dtBeg)) ) // дни неполного года - от 0 до 365
               ];
    }
    //------------------------------------------------------------------------------------------
      
    c = DateDiffYMDWD( mkDate(13,11,2006), mkDate(1,1,2005) );
    info( strfmt('Difference between %1 and %2 :', mkDate(13,11,2006), mkDate(1,1,2005)));
    info( strfmt('years: %1 , months: %2 , daysOfMonth: %3 , weeksOfMonth: %4 , daysOfWeek: %5, allDays: %6, daysOfYear: %7',
                  conpeek(c,1), conpeek(c,2), conpeek(c,3), conpeek(c,4), conpeek(c,5), conpeek(c,6), conpeek(c,7)));
    info('---------------------------------------------------');
    c = DateDiffYMDWD( mkDate(13,11,2006), mkDate(1,1,2008) );
    info( strfmt('Difference between %1 and %2 :', mkDate(13,11,2006), mkDate(1,1,2008)));
    info( strfmt('years: %1 , months: %2 , daysOfMonth: %3 , weeksOfMonth: %4 , daysOfWeek: %5, allDays: %6, daysOfYear: %7',
                  conpeek(c,1), conpeek(c,2), conpeek(c,3), conpeek(c,4), conpeek(c,5), conpeek(c,6), conpeek(c,7)));
}
В общем, тему для себя удовлетворенно закрыл. Даже в двух "экземплярах": на X++ и на VBA

P.S. 13.11.06 - подправил функцию DateDiffYMDWD - теперь если конечная дата меньше начальной (_dateEnd < _dateBeg), то значения разности в возвращаемом контейнере будут со знаком "-" (т.е. разность "в прошлое"). А также добавил 6-й и 7-й элемент в контейнер.

В итоге, имея такой контейнер, можно далее легко получать такие разницы между двумя датами, как:

1. в годах, месяцах, днях;
2. в годах, месяцах, неделях, днях;
3. в годах, днях;
4. в днях;

Вариант 1 актуален для HR-менеджмента при подсчете трудового стажа сотрудника.
Варианты 2-4 имеют "чисто теоретический характер", хотя всякое может быть...

Не показаны некоторые другие возможные варианты, которые тривиально могут быть получены из имеющихся в контейнере значений:
например,
общий срок в полных месяцах = годы * 12 + месяцы (где "годы" - 1-е значение в контейнере, "месяцы" - 2-е),
или
общий срок в полных неделях = дни div 7 (где "дни" - 6-е значение в контейнере).
За это сообщение автора поблагодарили: gl00mie (2).
Старый 02.03.2012, 13:49   #14  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Формулки для тёти бухгалтера (кадровика) в Excel
Пришлось тут наваять версию алгоритма для Excel на уровне формул в соседних колонках. Делал по мотивам своей вышеприведенной процедурки VBA_TestDifferenceBetweenTwoDates.

Произошло очередное укрепление убеждения в том, что разность между двумя датами - это кол-во полных месяцев + дополнительные дни (сверх полных месяцев). Всё остальное - полные годы, доп.месяцы (сверх полных лет), недели, кварталы и что еще заблагорассудится - можно получить простыми операциями деления или получения остатка от деления.

Завязываю узелок на память: в виде файла и в виде формул (для русского Excel). В виде формул - для большей наглядности (не надо лезть в файл), а также для гостей форума, для которых файл может быть недоступен.

A1: Дата 1
B1: Дата 2
C1: Полн.Месяцы предв.
D1: Полн.Месяцы оконч.
E1: Полн.Годы
F1: Месяцы сверх полн.лет
G1: Дни сверх полн. мес.

A2: 23.09.1984
B2: 15.12.2011
C2: =(ГОД(B2)-ГОД(A2))*12+МЕСЯЦ(B2)-МЕСЯЦ(A2)
D2: =C2+ЕСЛИ(B2-ДАТАМЕС(A2;C2)<0; ЕСЛИ(B2>A2;-1; 0); ЕСЛИ(B2>A2; 0; 1))
E2: =ОТБР(D2/12)
F2: =D2-E2*12
G2: =B2-ДАТАМЕС(A2;D2)

Разность (Дата 1 - Дата 2) может быть с любым знаком, формулы корректно считают и в плюс, и в минус.


P.S.05.03.2012. Ну что ж, как часто бывает в подобных случаях, когда надо "быстро-быстро" и "вчера", после внедрения начинаешь рассуждать спокойно, литературку почитаешь, по Сети поползаешь и поймешь, что существует (и давно) решение гораздо более изящное. Так и в этот раз - нашлась в Excel суперподходящая под задачу функция РАЗНДАТ (в англ. DATEDIF; не путать с ф-цией DateDiff VBA - при похожем названии возможности несколько различаются). Функция РАЗНДАТ является недокументированной, поэтому не появляется в списках выбора, отсутствует в справке и существует в Excel в основном для совместимости с Лотусом 1-2-3.

Функция РАЗНДАТ идеальна для вычисления возраста (количества полных лет), поскольку абсолютна точна (в отличие от встречающихся в И-нете предложений делить разницу в днях на 365 или 365.25), а также для вычисления стажа (годы, месяцы, дни). Почитать про функцию можно, например, здесь: http://www.excel2003.ru/vichislenie-...ili-staja.html:
Цитата:
Синтаксис функции следующий:

РАЗНДАТ(начальная_дата; конечная_дата; способ_измерения)

Самый интересный аргумент, конечно, последний. Он определяет, каким именно образом и в каких единицах будет измеряться интервал между начальной и конечной датами. Этот параметр может принимать следующие значения:

"y" разница в полных годах
"m" в полных месяцах
"d" в полных днях
"yd" разница в днях с начала года без учета лет
"md" разница в днях без учета месяцев и лет
"ym" разница в полных месяцах без учета лет
Дам свою, надеюсь, более понятную интерпретацию трех последних опций:
"yd" - количество дней сверх полных лет (от 0 до 365)
"md" - количество дней сверх полных месяцев (от 0 до 30)
"ym" - количество месяцев сверх полных лет (от 0 до 11)

Таким образом, полный интервал между двумя датами может быть представлен одним из следующих вариантов:
* количество дней РАЗНДАТ(дата1,дата2,"d") (или просто разница дат без использования РАЗНДАТ: дата2 - дата1)
* кол-во полных лет РАЗНДАТ(дата1,дата2,"y") + кол-во дней сверх полных лет РАЗНДАТ(дата1,дата2,"yd")
* кол-во полных месяцев РАЗНДАТ(дата1,дата2," m") + кол-во дней сверх полных месяцев РАЗНДАТ(дата1,дата2,"md")
* случай расчета стажа: кол-во полных лет РАЗНДАТ(дата1,дата2,"y") + кол-во полных месяцев сверх полных лет РАЗНДАТ(дата1,дата2,"ym") + кол-во дней сверх полных месяцев РАЗНДАТ(дата1,дата2,"md")

Да, и конечная дата должна быть больше начальной даты, т.е. в минус функция РАЗНДАТ автоматически не считает (в этом случае следует поменять даты местами).

Добавил в прилагаемый файл формулы с РАЗНДАТ.
Вложения
Тип файла: xls TestDifferenceBetweenTwoDates2.xls (30.5 Кб, 470 просмотров)

Последний раз редактировалось Gustav; 05.03.2012 в 10:32.
Теги
дата

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Как сделать AND между двумя датасорсами на одном уровне в query rkorchagin DAX: Программирование 15 15.01.2009 17:42
Разница между запросами Rect DAX: Программирование 13 05.12.2006 12:44
Расчеты между компаниями Yuri Nikitenko DAX: Функционал 9 15.11.2004 08:49
дизайн отчета (как сделать его с разделителями между столбцами?) puz DAX: Программирование 7 02.09.2003 08:50
Функция "Удалить строки" Oks DAX: Функционал 1 03.07.2002 18:09
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра
Комбинированный вид Комбинированный вид

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 15:49.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.