Função que retorna o número de dias úteis entre duas datas utilizando AdvPL

Olá pessoal…

Hoje vou mostrar uma função desenvolvida que retorna o número de dias úteis entre duas datas utilizando a linguagem AdvPL.

AdvPL

AdvPL

Esta rotina para a contagem dos dias, desconsidera sábados, domingos e feriados (cadastrados na tabela 63 da SX5), então por exemplo, entre os dias 30/10/2016 até 05/11/2016, quantos dias são úteis?

A rotina no caso retorna 4, que representa o número de dias úteis entre as duas datas de referência.

Abaixo a rotina completa.

//Bibliotecas
#Include "Protheus.ch"

/*/{Protheus.doc} zDiasUteis
Função que retorna a quantidade de dias úteis entre duas datas
@author Atilio
@since 25/10/2016
@version 1.0
@type function
	@param dDtIni, date, Data inicial a ser considerada
	@param dDtFin, date, Data final a ser considerada
	@example u_zDiasUteis(sToD("20161001"), sToD("20161025"))
	u_zDiasUteis(FirstDate(dDataBase), LastDate(dDataBase))
	@obs Quanto as feriados, eles devem estar cadastrados na SX5, tabela "63"
/*/

User Function zDiasUteis(dDtIni, dDtFin)
	Local aArea    := GetArea()
	Local nDias    := 0
	Local dDtAtu   := sToD("")
	Default dDtIni := dDataBase
	Default dDtFin := dDataBase
	
	//Enquanto a data atual for menor ou igual a data final
	dDtAtu := dDtIni
	While dDtAtu <= dDtFin
		//Se a data atual for uma data Válida
		If dDtAtu == DataValida(dDtAtu) 
			nDias++
		EndIf
		
		dDtAtu := DaySum(dDtAtu, 1)
	EndDo
	
	RestArea(aArea)
Return nDias

Bom pessoal, por hoje é só.
Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

10 Responses

  1. Rodriog disse:

    Parabéns pelos posts, tem alguma funcao que retorno o 5. dia util do mes?

    • Dan_Atilio disse:

      Boa noite Rodrigo, tudo bem?
      De cabeça desconheço, o que você poderia fazer é:

      dDataRef := Date() //Pega a data de referência
      dPrimDia := FirstDate(dDataRef) //Pega o primeiro dia do mês
      d5Dias := DaySum(dPrimDia, 4) //Soma mais 4 dias no primeiro dia do mês
      dQuinto := DataValida(d5Dias)
      

      Seria +- assim eu acho.
      Um grande abraço.

  2. gabriel disse:

    Bom dia Dan, tudo certo?

    Gostaria de saber se você pode me auxiliar em um fonte aqui.

    Este fonte faz o totalizador por período definido nos parâmetros iniciais, gostaria de saber se tem como eu fazer o totalizador também pelas datas que constam neste período?

    Segue fonte:

    User Function IFINR028()

    Local vPerg := {}
    Private cNomRel := “IFINR028”
    Private rTitRel := “Relatório de Limite Extra”
    Private cPerg := PadR(cNomRel,Len(SX1->X1_GRUPO))

    aAdd(vPerg,{“Data de ?” ,”D”, 8, 0, “G”,,””}) //MV_PAR01
    aAdd(vPerg,{“Data até ?”,”D”, 8, 0, “G”,,””}) //MV_PAR02

    U_FtCriaSX1( cPerg, vPerg)

    If !Pergunte(cPerg,.T.)
    Return
    EndIf

    oReport := ReportDef()
    oReport:PrintDialog()

    Return

    ********************************
    Static Function ReportDef()
    ********************************
    Private oReport

    //*Sintaxe: TReport():New(cNome,cTitulo,cPerguntas,bBlocoCodigo,cDescricao)*/
    oReport := TReport():NEW(cNomRel, rTitRel, cPerg, {|oReport| Imprime(oReport)}, rTitRel, .F.)
    oReport:SetTotalInLine(.F.)
    oReport:SetLandscape(.T.)
    oReport:lParamPage := .F.

    oSec := TRSection():New(oReport , rTitRel , {} )

    TRCELL():NEW(oSec , “FILIAL” , “SZB”, “Filial”, , 2)
    TRCELL():NEW(oSec , “CONTRATO” , “SZB”, “Contrato”, , 15)
    TRCELL():NEW(oSec , “CODIGO” , “SZB”, “Codigo”, , 6)
    TRCELL():NEW(oSec , “LOJA” , “SZB”, “Loja”, , 3)
    TRCELL():NEW(oSec , “NOME” , “SZB”, “Nome”, , 60)
    TRCELL():NEW(oSec , “DATA_DO_LIMITE” , “SZB”, “Dta.do Limite”, , 8)
    TRCELL():NEW(oSec , “RESPOSAVEL” , “SZB”, “Resposavel”, , 25)
    TRCELL():NEW(oSec , “EMITENTE” , “SZB”, “Emitente”, , 20)
    TRCELL():NEW(oSec , “HISTORICO” , “SZB”, “Historico”, , 60)
    TRCELL():NEW(oSec , “VALOR_INSERIDO” , “SZB”, “Valor Inserido”, “@E 999,999,999.99”, 15)

    oBreak := TRBreak():New(oSec,oSec:Cell(“VALOR_INSERIDO”),,.T.)
    //TRFunction():New(oSecCab:Cell(“ZDA_QUANT”),/*cId*/,”COUNT” ,/*oBreak*/,”QUANTIDADE DE NOTAS”,/*cPicture*/,/*uFormula*/,.F. ,.T. ,.F. ,oSecCab)
    TRFunction():New(oSec:Cell(“VALOR_INSERIDO”),”Total Geral R$” ,”SUM”,oBreak,”Total R$”,”@E 999,999,999.99″,,.F.,.T.)

    oSec:SetLeftMargin(0)

    Return oReport

    ********************************
    Static Function IMPRIME(oReport)
    ********************************

    Local cAliTMP := GetNextAlias()
    Private oSec := oReport:Section(1)

    oReport:SetTitle(rTitRel+” de “+DtoC(MV_PAR01)+” até “+DtoC(MV_PAR02))

    oReport:Section(1):BeginQuery()

    If Select(“cAliTMP”) 0
    dbSelectArea(“cAliTMP”)
    dbCloseArea()
    EndIf

    BeginSQL alias cAliTMP

    SELECT ZB_FILIAL FILIAL,
    ZB_CONTRA CONTRATO,
    ZB_CLIENTE CODIGO,
    ZB_LOJCLI LOJA,
    A1_NOME NOME,
    SUBSTR(ZB_DATINC,7,2)||’/’||SUBSTR(ZB_DATINC,5,2)||’/’||SUBSTR(ZB_DATINC,1,4) DATA_DO_LIMITE,
    ZB_USERNAM RESPOSAVEL,
    UTL_RAW.CAST_TO_VARCHAR2(dbms_lob.substr(ZB_JUSTIF,100,1)) HISTORICO, //BUSCA AS INFORMACOES DE UM CAMPO MEMO
    ZB_VLADIC VALOR_INSERIDO
    FROM %table:SZB% ZB
    INNER JOIN SA1010 A1 ON A1_COD = ZB_CLIENTE
    AND ZB_LOJCLI = A1_LOJA
    AND A1.D_E_L_E_T_ = ‘ ‘
    WHERE ZB_FILIAL = %xFilial:SZB%
    AND ZB_DATINC BETWEEN %exp:MV_PAR01% and %exp:MV_PAR02%
    AND ZB.D_E_L_E_T_ = ‘ ‘
    ORDER BY ZB.ZB_DATINC DESC

    EndSql

    If Select(“cAliTMP”) 0
    dbSelectArea(“cAliTMP”)
    dbCloseArea()
    EndIf

    oReport:Section(1):EndQuery()
    oReport:Section(1):Print()

    If !Empty(cAliTMP) .and. Select(cAliTMP) 0
    dbSelectArea(cAliTMP)
    (cAliTMP)->(dbCloseArea())
    endif

    Return

    • Boa tarde Gabriel, tudo joia graças a Deus e você?

      Você precisa adicionar um TRBreak da coluna com o seu campo de Data, ai sempre que alterar o valor, ele irá quebrar o relatório.

      Ah, caso você seja um Assinante Premium, basta nos enviar uma mensagem no WhatsApp ou eMail que fica mais rápido para conversarmos.

      Um grande abraço.

      • gabriel disse:

        Boa Tarde Dan, bah voce me ajudou muito fiz o oBreak pela Data com isto cada vez que a data for diferente ele faz a quebra de linha e depois me joga o total dos valores.

        oBreak := TRBreak():New(oSec,oSec:Cell(“DATA_DO_LIMITE”),,.T.)
        TRFunction():New(oSec:Cell(“DATA_DO_LIMITE”),,,oBreak,,,,.F.,.T.)
        TRFunction():New(oSec:Cell(“VALOR_INSERIDO”),,”SUM”,oBreak,”Total R$”,”@E 999,999,999.99″,,.F.,.T.)

        Muito obrigado pela sua atenção. Precisar de alguma coisa que for do meu alcance estou a disposição.
        Abraço

  3. gabriel disse:

    Boa dia Dan, tudo certo, estava vendo este link Função que retorna o quinto dia útil do mês

    Mas estou com outro problema, sera que consegue me ajudar?

    Como faço pra rodar uma User Function por agendamento (scheduler) todo primeiro dia util do mes seguinte?

    Eu faco manualmente uma rotina que zera o valor da movimentação da agua, precisaria colocar esta rotina pra zerar automaticamente.

    Exemplo hoje é dia 13/04/2023 entao dia 01/05/2023 teria que rodar automaticamente a funcao pois é o 1 dia util do mes 05.
    Agora se cai-se o dia 01/05/2023 num sábado teria que rodar automaticamente a função dia 03/05 pois seria segunda feira.

    Teria como me ajudar?

    • Fala Gabriel, tudo joia graças a Deus e você?
      Não me lembro se direto no Scheduler do SIGACFG tem a opção para o primeiro dia útil.
      Caso não tenha, logo no começo do seu fonte, logo no começo você teria que adicionar alguma validação pra identificar que é o primeiro dia útil, algo +- assim:

      //Pega a data atual
      dDataHoje := Date()
      
      //Pega o primeiro dia do mês
      dPriDiaUti := FirstDate(dDataHoje)
      
      //Enquanto o dia for diferente da data válida (for um feriado ou fds)
      While dPriDiaUti != DataValida(dPriDiaUti)
          
      	//Incrementa 1 dia
      	dPriDiaUti := DaySum(dPriDiaUti, 1)
      EndDo
      
      //Se o primeiro dia útil for igual a data de hoje
      If dPriDiaUti == dDataHoje
      	//Aqui dentro você executa o que precisa
      EndIf
      

      Grande abraço.

  4. Herbert Jean disse:

    Excelente o fonte, estou aprendendo advpl como faço para chamar essa função no Protheus?

    Existe algum fonte que abre um pop up para inserir nome da funcao para chamar?

Deixe uma resposta para Dan_AtilioCancelar resposta

Terminal de Informação