VB .NET - Datas, horas: conceitos e operações (TimeSpan, DateTime)


 No artigo de hoje vou mostrar como podemos tratar datas, horas e realizar algumas operações usando os objeto TimeSpan e DateTime.

O objeto TimeSpan

Um objeto TimeSpan representa um intervalo de tempo, ou duração de tempo, medida como um número positivo ou negativo de dias,  horas, minutos, segundos e frações de segundo.

Leia também o artigo : VB .NET - Tratamento de datas e horas

A unidade de tempo usada para medir a maior duração é um dia. Intervalos de tempos são medidos em dias pois o número de dias pois o número de dias em unidades de tempo , como meses e anos, sofre variação.

Um TimeSpan pode ser representado como  [-.] d. hh: mm: ss. ff,  onde :

- o sinal indica um intervalo de tempo negativo;
- d indica dias;
- hh representa as horas medida em 24 horas;
- mm representa os minutos;
- ss representa os segundos;
- ff representa as frações de segundo.

Assim , a representação de texto de um objeto TimeSpan com  1.0E + ticks 13   é   11.13:46:40 que significa 11 dias , 13 horas , 46 minutos e 40 segundos.

Obtendo a data e hora do sistema

O namespace System.DataTime permite o acesso a métodos e propriedades que tratam datas e horas. A seguir temos os principais métodos e o tipo do retorno.

 

Método Tipo Retorno
Date
Day
DayOfWeek
DayOfYear
Hour
Millisecond
Minute
Month
Now
Second
Ticks
TimeOfDay
Today
Year
DateTime
Int32
Int32
Int32
Int32
nt32
Int32
Int32
DateTime
Int32
Int64
TimeSpan
DateTime
Int32

O tipo DateTime representa datas e horas com valores entre a meia-noite de 1 de janeiro de 0001 e 11:59:59 de 31 de dezembro de 9999.

Os valores de hora são medidos em unidades de 100 nano segundos chamadas Ticks sendo que uma data especifica é o número de pulsos contados a partir da meia-noite de 01/01/0001 no calendário Gregoriano.

A propriedade Now retorna a data na qual você pode armazenar em uma variável Date ou processar diretamente. Existem muitas propriedades e métodos disponíveis para extrair informações a partir de Datas. O exemplo abaixo extrai e exibe a data , a hora e os ticks transcorridos;

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button1.Click

   Dim
datahoraAtual As DateTime = Now

   lstDataHora.Items.Add("Agora")
   lstDataHora.Items.Add("")
   lstDataHora.Items.Add(datahoraAtual)
   lstDataHora.Items.Add("")
   lstDataHora.Items.Add("Data : " & datahoraAtual.ToShortDateString)
   lstDataHora.Items.Add("Hora : " & datahoraAtual.ToShortTimeString)
   lstDataHora.Items.Add("Ticks : " & datahoraAtual.Ticks.ToString)

End Sub

Usei as propriedades ToShortDateString e ToShortTimeString para extrair a data e a hora para um formato mais legível.

Nota: O VB6 armazena a data e a hora em um formato diferente do VB.NET os programadores VB acessavam os valores numéricos de datas como um Double. As datas no VB.NET são armazenadas como inteiro longo em um formato incompatível com o do VB6. Portanto na migração é melhor reescrever o seu código usando o novo recurso para data. Para converter uma informação de data no formato antigo para o novo formato use as funções : ToOADate() e FromOADate() que são específicas para este propósito.

Migração VB->VB.NET : Quando você for trabalhar com datas utilize o tipo Date e evite Double.

O VB6.0 permite que você use o tipo Double para armazenar datas , este tipo usa quatro bytes . No VB.NET uma variável do tipo Date usa o tipo de dado DateTime que é um inteiro de 8 bytes ; portanto não podemos converte Double para Date no VB.NET. O código abaixo é válido no VB 6.0 mas causa um erro de compilação no VB.NET.

Dim dbl As Double
Dim data As Date
data = Now
dbl = data                   'VB.NET: Não aceita datas em variáveis do tipo Double
dbl = DateAdd("d", 1, dbl)   'VB.NET: Não usa Double em funções date
dat = CDate(dbl)             'VB.NET: CDate não pode converter double para date

Embora o VB.NET forneça as funções ToOAdate e FromOADate para converte double e date , durante a migração fica difícil determinar a sua intenção em usar Double para armazenar datas. Portanto sempre use o tipo Date para armazenar datas.

Se você quiser determinar o fuso horário local ou o horário de verão local pode usar o objeto TimeZone que fornece propriedades e métodos para determinar o nome da da área , o número de horas do fuso horário a partir de Greenwich (GMT) e se o horário de verão local esta em uso.

Abaixo temos um exemplo que mostra a utilização do objeto TimeZone.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim fusoHorario As TimeZone = TimeZone.CurrentTimeZone

lstTimeZone.Items.Add("DaylightName: " & fusoHorario.DaylightName)
lstTimeZone.Items.Add("")
lstTimeZone.Items.Add("StandardName: " & fusoHorario.StandardName)
lstTimeZone.Items.Add("")
lstTimeZone.Items.Add("IsDaylightSavingTime(Now): " & fusoHorario.IsDaylightSavingTime(Now))
lstTimeZone.Items.Add("")
lstTimeZone.Items.Add("Fuso horário em relação a GMT [GetUtcOffset(Now)] : " & fusoHorario.GetUtcOffset(Now).ToString)
lstTimeZone.Items.Add("")
lstTimeZone.Items.Add("Hora do Sistema é Hora Local: DateTimeKind.Local = " & DateTimeKind.Local)
'lstTimeZone.Items.Add(Now.Kind = DateTimeKind.Local)
lstTimeZone.Items.Add("Hora do Sistema é Universal Coordinated Time: DateTimeKind.Utc = " & DateTimeKind.Utc)
lstTimeZone.Items.Add("")
'lstTimeZone.Items.Add(Now.Kind = DateTimeKind.Utc)
lstTimeZone.Items.Add("Hora do Sistema não esta especificado: Now.Kind = " & DateTimeKind.Unspecified)
'lstTimeZone.Items.Add(Now.Kind = DateTimeKind.Unspecified)

End Sub

A variável fusohorario é definida como do tipo TimeZone e recebe a informação do fuso horário do sistema atual na sequência estamos extraindo as informações desejadas como: nome da zona local, o número de horas do fuso horário local em relação a GMT e se o horário de verão esta em uso.

A propriedade Kind é um de 2 bits que indica se a DateTime estrutura representa um horário local, um horário universal coordenado (UTC). Ela obtém um valor que indica se a hora representada por essa instância é baseada no horário local, hora universal coordenada (UTC), ou nenhum. Ela é usada também para tratar conversões entre local e hora UTC. Esta propriedade pertence a Date e não a TimeZone.

Nota: O Horário local é relativo a um fuso horário específico. Um fuso horário está associado a um deslocamento de fuso horário, que é o deslocamento do fuso horário medido em horas a partir do ponto de origem UTC. Além disso, o horário local opcionalmente é afetado pelo horário de verão, que adiciona ou subtrai algumas horas ao dia.

Enquanto que a Hora UTC é adequada para cálculos, comparações, e armazenamento datas e horas em arquivos o Horário local é apropriado para exibição em interfaces de usuário.

Os cálculos usando uma estrutura DateTime, tais como somar ou subtrair, não modificam o valor da estrutura. Em vez disso, o cálculo retorna uma nova estrutura DateTime cujo valor é o resultado de cálculo.

Operações de conversão entre hora local e hora UTC devem levar o horário de verão em conta, mas não as operações aritméticas e de comparação.

Os cálculos e comparações de objetos DateTime são significativas se os objetos representam horas no mesmo fuso horário. Por esse motivo, se nenhuma zona de tempo for especificada para os objetos, estará pressuposto que o desenvolvedor tem algum mecanismo externo, como uma variável explícita ou diretiva, que pode ser usada para determinar o fuso horário no qual um objeto DateTime foi criado.

Cada membro DateTime usa implicitamente o calendário gregoriano para executar sua operação, com exceção de construtores que especificam um calendário.

Operações para membros do tipo DateTime leva em detalhes o número de dias em um mês e anos bissexto.

Vejamos a seguir alguns cálculos usando o namespace DateTime:

leia também o artigo : VB.NET Medindo diferença de tempos

Somando e subtraindo datas

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Dim datahoraAtual As DateTime = Now

lstDataHora.Items.Add(datahoraAtual)
lstDataHora.Items.Add("")
lstDataHora.Items.Add("Um ano atrás : " & datahoraAtual.AddYears(-1))
lstDataHora.Items.Add("No ano seguinte : " & datahoraAtual.AddYears(+1))
lstDataHora.Items.Add("No mês seguinte : " & datahoraAtual.AddMonths(+1))
lstDataHora.Items.Add("No mês anterior : " & datahoraAtual.AddMonths(-1))
lstDataHora.Items.Add("No dia anterior : " & datahoraAtual.AddDays(-1))
lstDataHora.Items.Add("No próximo dia : " & datahoraAtual.AddDays(+1))

End Sub

Note que o objeto DateTime não possui um método para subtrair datas/horas mas podemos usar o método Add e usar argumentos com valores negativos para obter o mesmo resultado.

O objeto DateTime possui um método Subtract mas este método subtrai outro valor do tipo Date ou do tipo TimeSpan.Abaixo temos o método Subtract e os argumentos usados:
Public Function Subtract(value As System.TimeSpan) As Date
Public Function Subtract(value As Date) As System.TimeSpan

Existem 7 métodos que podemos usar para somar unidades de tempo a uma data:

Determinando o número de dias entre duas datas

Para calcular o número de dias entre duas datas podemos usar o método Subtract da classe DateTime. Primeiro calculamos o TimeSpan entre duas datas e em seguida usando a propriedade Days da classe TimeSpan extraímos o número de dias.

O objeto TimeSpan representa a diferença de tempo entre duas datas , podemos subtrair uma data de outra data usando o método Subtract o qual retorna um TimeSpan. Para acessar as unidades de tempo a partir do TimeSpan , usamos as propriedades para cada tipo de unidade de tempo.  Vejamos a seguir um exemplo que calcula o número de dias de vida de uma pessoa calculados a partir da data do seu nascimento até  o dia de hoje.

Crie um novo projeto no VB 2005 Express Edition com um nome sugestivo e no formulário padrão inclua um controle DateTimePicker e alguma Labels.

a seguir inclua o seguinte código no formulário:

Private Function calculaNumeroDiasVividos() As Integer


Dim
dataNascimento As
Date

Dim horasVividas As TimeSpan

Dim diasVividos As Integer


' obtem o valor da data de nascimento do controle DateTimePickerl

dataNascimento = dtpDataNascimento.Value
' subtri a data de nascimento da data atual

horasVividas = Now.Subtract(dataNascimento)
'Calcula o número de dias entre as datas

diasVividos = horasVividas.Days


'se o valor for menor que 1 indica que a data de nascimento é maior que a data atual

If diasVividos < 1 Then

     Throw New ArgumentException("Você ainda não nasceu...")

End If

 

Return diasVividos

 

End Function
 

Private Sub dtpDataNascimento_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles dtpDataNascimento.ValueChanged


'verifica se a data informada é valida
If
IsDate(dtpDataNascimento.Value)
Then

   Try
         'chama a função para calcular o numero de dias e exibe o resultado na label do formulário

         lblDiasVividos.Text = calculaNumeroDiasVividos()

   Catch exc As Exception
          
MsgBox(exc.Message)

    End Try

Else

    MessageBox.Show("Informe uma data válida", "Data Inválida", MessageBoxButtons.OK, MessageBoxIcon.Error)

End If

End Sub

 

A rotina calculaNumeroDiasVividos calcula o número de dias entre as duas datas e é chamada toda vez que ocorre o evento ValueChanged do controle DateTimePicker. (Talvez não fosse uma boa idéia usar esta lógica, devido ao tratamento de erro que eu implementei...)

Os membros da classe TimeSpan que podemos usar para calcular a diferença entre datas são:  Days, Hours, Minutes, Seconds, e Milliseconds. No exemplo usamos o método Days. Note que se o número de dias vividos for menor que 1 lançamos uma exceção.

Cada um dos membros  retorna um valor inteiro arredondado indicando a diferença entre duas datas(hora).

Temos também as propriedades TotalDays, TotalHours, TotalMinutes, TotalSeconds, e TotalMilliseconds  que retornam valores decimais que não são arredondados para o próximo intervalo.

Determinando o dia da semana

Para obter um número ou string representando o dia da semana para uma data você pode usar a propriedade DayOfWeek que retorna um número de 0 (Domingo) até 6 (Sábado) para o dia da semana , ou se preferir, usar o método ToString() para retornar o nome do dia semana. Você pode ainda usar as várias opções de formatação de string através do método String.Format() para retornar o nome do dia da semana no formato abreviado ou expandido.

O código abaixo mostra como usar estes recursos exibindo o dia da semana nos formatos números, abreviado e expandido:

Dim dataAtual As Date = Now
Dim diaDaSemana As Integer = dataAtual.DayOfWeek
Dim diaDaSemanaAbreviado As String = Format(dataAtual, "ddd")
Dim diaDaSemanaExpandido As String = String.Format("{0:dddd}", dataAtual)

Dim resultado As String = String.Format("Hoje é o dia da semana : {0}, ou {1}, ou {2}", diaDaSemana, diaDaSemanaAbreviado, diaDaSemanaExpandido)

MsgBox(resultado)

 Determinando o dia do ano

Para determinar o dia do ano para uma data você pode usar a propriedade DayOfYear. O número obtido se situa no intervalo de 1 a 366.

O código abaixo obtém o dia do ano para a data atual:


Dim dataAtual As Date = Now
Dim diaDoAno As Integer = dataAtual.DayOfYear

Dim resultado As String = String.Format("Dia do ano para {0:D}: {1}", Now, diaDoAno)
MsgBox(resultado)

Determinar o número de dias em um Mês

Para obter o número de dias em um mês você pode usar a função DaysInMonth fornecida pelo objeto Date que retorna o número de dias em um mês para um determinado mês do ano. DaysInMonth é uma função compartilhada ou shared function.

O código a seguir mostra a sintaxe correta para obter o número de dias no mês atual:

Dim diasNoMes As Integer = Date.DaysInMonth(Now.Year, Now.Month)
MsgBox(String.Format("Número de dias no mês atual: {0}", diasNoMes))

Calculando se um ano é bissexto

Para verificar se um ano é bissexto podemos usar o método IsLeapYear() do objeto Date. Este método retorna True se o ano passado como argumento for um ano bissexto e Fase caso contrário.

Dim anoBissexto As Boolean = Date.IsLeapYear(Now.Year)
MsgBox(String.Format("{0} É um ano bissexto ? {1}", Now.Year, IIf(anoBissexto, "SIM", "NÃO")))

Perceba que o método IsLeapYear é um método estático (shared) por este motivo devemos chamá-lo diretamente do objeto Date e não a partir de uma instância deste objeto.

E por enquanto é só. Até o próximo artigo...

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti