30.09.2018 tarihinde Excel bölümüne Dış Verilerle çalışmak sayfası eklenmiştir

04.08.2018 tarihinde VBA bölümüne ObjelerDünyası sayfası eklenmiştir

25.07.2018 tarihinde VBA bölümüne Outlook programlama sayfası eklenmiştir

13.07.2018 tarihinde VBA bölümüne Formlar-Kontroller sayfası eklenmiştir

25.05.2018 Hosting şirketi dğeiştirmekten kaynaklı bir hata nedeniyle Excelent add-ini indirirken hata alınmaktaydı. Bu hata düzeltilmiştir. İki ayrı download alternatifi sunulmuştur. Kurumunuzun BT politikalarının veya şahsi PC’nizdeki güvenlik ayarlarının izin vermesi durumunda yöntemlerden biriyle kurulum yapabilmelisiniz. Bi sorun olursa bana iletebilirseniz sevinirim.

VBAMakro Fonksiyonlar 3

Tarihsel Fonksiyonlar

Ön Bilgiler

Excel’de tüm tarihlerin sayısal bir karşılığı vardır. Aslında karşılığı vardır demekten ziyade tüm tarihler bir sayıdır demek daha doğru olur. Excel bunları gösterirken tarih formatında gösterir. Tarihler tam sayı depolanırken saatler küsurlu formda tutulur. Bu sayı 1.1.1900'den itibaren geçen günsayısıdır. Ör:17.05.2015 arka planda 42141 sayısı olarak tutulur.

Ayraçları, bölgesel ayarlar belirler, ancak tarih “#” işaretleri arasında yazılırsa bölgesel ayarlardan bağımsız olarak Amerikan tarih formatına göre belirtilmiş olur:Ay/gün/yıl. Bunu pek kullanmanızı önermem, ancak görürseniz de şaşırmayın. Biz bildiğimiz formatta kullanalım, yoksa 5 Temmuz yazacağınız yerde 7 Mayıs gibi sonuçlar elde edebilirsiniz, bu da önemli hatalara neden olabilir.

Tarihlerle ilgili dikkat edilmesi gereken bir husus var. Formatlı Gösterim tipi, kaynak tipi veya dönüş tipi. Formatlı Gösterim şekli her zaman stringtir. Ancak kaynak tip veya dönüş tipi farklı tipler de olabilir.

Tarihlerin kaynak/dönüş tipi aşağıdakiler olabilir.

  • String
  • Nümerik
  • Tarihsel
    • # karakterleri arasında
    • Tarihsel bir fonksiyondan dönen değer olarak. (Excel hücresinden veya Date, DateSerial gibi VBA fonksiyonlarından)

Konuyla ilgili daha detaylı bilgiye bu sayfadan ulaşabilirsiniz.

DateTime modülü

Tarihsel işlemlerimizi yaparken DateTime modülünü kullanıyor olacağız. Bu modüldeki fonksiyonlara geçmeden önce sık kullanılan property'lere bir bakalım.

Propertyler

Date ve Date$: O anki günü verirler. İlki Variant döndürürken ikincisi String döndürür. Aralarındaki fark, Variant olanda null işlemi yapabilmenizdir. Bir diğer fark da Date ile bir sayıyı matematiksel işleme tabi tutabilirsiniz ancak Date$ ile bunu yapamazsınız .

Debug.Print Date+1 'yarının tarihini verir
Debug.Print Date$+1 'hata verir. String olduğu için.

Aşağıdaki kod, üzerinde çalıştığımız dosyanın son değişikliğin tarihinin bugünden küçük olması durumunda çalışır.

'fso tanımının yapıldığı kodlar
If fso.DateModified < Date Then
   'diğer kodlar
End if

Time ve Time$: Şu anın saatini verir. aralarındaki fark Date/Date$ arsındaki farkların aynısıdır.

Now: Şu anın tarih ve saatini verir. Date ve Time'ın birleşimi gibi düşünülebilir.

Değişken tanımlamalar

DateTime modülünün içindeki Date tipi ile hem tarih hem saat tanımlanabilir. Aşağıdaki örneklerde tüm değişkenli Variant tanımlıyoruz.

Dim tarih1 'As Date
Dim tarih2 'As Date<
Dim stringtarih1 'As String
Dim stringtarih2 'As String
Dim numeriktarih1 'As Long
Dim numeriktarih2 'As Double
Dim diyezlitarih1 'As Date
Dim diyezlitarih2 'As Date
Dim hucredentarih1 'As Date
Dim hucredentarih2 'As Date
	

Örnek kodlara geçmeden önce, Range nesnesinin Value ve Value2 özelliklerinin farkını tekrar edelim. Value, hücrenin içeriğini alırken, Value2 tarihleri numerik değere çevirerek depolar. (Tabi siz bir değişkeni Date olarak tanımladıysanız, ona Value2 sonucunu aktarsanız bile sayısal değer değil yine tarihsel formattaki değeri depolanır.)

Dönüş tipleri

tarih1 = Now
tarih2 = DateSerial(1979, 1, 21)
stringtarih1 = "21.01.1979"
stringtarih2 = "01.01.1979"
numeriktarih1 = 42574
numeriktarih2 = 42574.127
diyezlitarih1 = #1/21/1979# 'amerikan formatında olmak zorunda, önce ay ve ayraç oalrak da "/" girili "." dğeil. gün girerken 01 
girsen bile enterea basıp alta geçince kaybolurlar.
diyezlitarih2 = #1/21/1979 4:30:00 AM# 'amerikan 
formatında olmak zorunda, önce ay ve ayraç olarak da /
hucredentarih1 = range("a1").Value 'hücre içeriği:24.07.17 13:14
hucredentarih2 = range("a1").Value2 'hücre içeriği:21.01.1979

Şimdi bunların çeşitli özelliklerini yazdıralım.

Debug.Print "tarih1", vbTab, tarih1, TypeName(tarih1),VarType(tarih1), IsDate(tarih1)
Debug.Print "tarih2", vbTab, tarih2, vbTab, TypeName(tarih2), VarType(tarih2), IsDate(tarih2)
Debug.Print "stringtarih1", vbTab, stringtarih1, vbTab, TypeName(stringtarih1), VarType(stringtarih1), IsDate(stringtarih1)
Debug.Print "stringtarih2", vbTab, stringtarih2, vbTab, TypeName(stringtarih2), VarType(stringtarih2), IsDate(stringtarih2)
Debug.Print "numeriktarih1", vbTab, numeriktarih1, vbTab,TypeName(numeriktarih1), VarType(numeriktarih1), IsDate(numeriktarih1)
Debug.Print "numeriktarih2", vbTab, numeriktarih2, vbTab,TypeName(numeriktarih2), VarType(numeriktarih2), IsDate(numeriktarih2)
Debug.Print "diyezlitarih1", vbTab, diyezlitarih1, vbTab,TypeName(diyezlitarih1), VarType(diyezlitarih1), IsDate(diyezlitarih1)
Debug.Print "diyezlitarih2", vbTab, diyezlitarih2, TypeName(diyezlitarih2), VarType(diyezlitarih2), IsDate(diyezlitarih2)
Debug.Print "hucredentarih1", hucredentarih1, vbTab,TypeName(hucredentarih1), VarType(hucredentarih1), IsDate(hucredentarih1)
Debug.Print "hucredentarih2", hucredentarih2, vbTab,TypeName(hucredentarih2), VarType(hucredentarih2), IsDate(hucredentarih2)

Çıktısı aşağıdaki gibi olacaktır.

Dönüşüm İşlemleri

Bazen, gösterim tipi metinsel olan tarihleri tarih tipine çevirip onlar üzerinden işlemlerinize devam etmek istersiniz. Bunun için VBA bize 4 yöntem sağlmaktadır. Dördü de küçük nüanslar göstermektedir.

1.Yöntem:CDate(Exp):CDate, bir ifadeyi tarih yaparken tipini de tarih yapar. Üstelik saat v.s bilgisi varsa bunları da korur. İçine her tür ifadeyi alabilir.

x1 = CDate(tarih1)
x2 = CDate(tarih2)
x3 = CDate(stringtarih1)
x4 = CDate(stringtarih2)
x5 = CDate(numeriktarih1)
x6 = CDate(numeriktarih2)
x7 = CDate(diyezlitarih1)
x8 = CDate(diyezlitarih2)
x9 = CDate(hucredentarih1)
x10 = CDate(hucredentarih2)	

Şimdi bunları yazdıralım.

Debug.Print x1, TypeName(x1), VarType(x1)
Debug.Print x2, TypeName(x2), VarType(x2)
Debug.Print x3, TypeName(x3), VarType(x3)
Debug.Print x4, TypeName(x4), VarType(x4)
Debug.Print x5, TypeName(x5), VarType(x5)
Debug.Print x6, TypeName(x6), VarType(x6)
Debug.Print x7, TypeName(x7), VarType(x7)
Debug.Print x8, TypeName(x8), VarType(x8)
Debug.Print x9, TypeName(x9), VarType(x9)
Debug.Print x10, TypeName(x10), VarType(x10)

Çıktısı şöyle olacaktır:

2.Yöntem:DateValue(Metin): Bu fonksiyon ile metin formatındaki tarihler gerçek tarihe çevrilir. Saat v.s detayını korumaz. Argüman olarak sadece metin alır.

DateValue("21.01.1979") '21 Ocak 1979 tarihini verir
DateValue(28876) 'hata verir. Çünkü argüman olarak sadece metin alır.

3.Yöntem:Format(exp, format): sadece gösterim şeklini tarihsel yapar, dönüş tipi Stringtir. Dönen değerle tarihsel işlem yapılamaz.

İkinci parametre olarak önceden tanımlanmış formatlar da girilebilir, kullanıcı tanımlı formatlar da. Bunların detayın buradan ulaşabilriiniz.

y1 = Format(tarih1, "Short Date") 'önceden tanımlanmış parametre
y2 = Format(tarih2, "dd.mm.yyyy") 'bu ve aşağıdakiler ise kullanıcı tanımılı paremtredir
y3 = Format(stringtarih1, "dd.mm.yyyy")
y4 = Format(stringtarih2, "dd.mm.yyyy")
y5 = Format(numeriktarih1, "dd.mm.yyyy")
y6 = Format(numeriktarih2, "dd.mm.yyyy")
y7 = Format(diyezlitarih1, "dd.mm.yyyy")
y8 = Format(diyezlitarih2, "dd.mm.yyyy")
y9 = Format(hucredentarih1, "dd.mm.yyyy")
y10 = Format(hucredentarih2, "dd.mm.yyyy")

Şimdi de bunların çıktısını alalım

Debug.Print y1, TypeName(y1), VarType(y1)
Debug.Print y2, TypeName(y2), VarType(y2)
Debug.Print y3, TypeName(y3), VarType(y3)
Debug.Print y4, TypeName(y4), VarType(y4)
Debug.Print y5, TypeName(y5), VarType(y5)
Debug.Print y6, TypeName(y6), VarType(y6)
Debug.Print y7, TypeName(y7), VarType(y7)
Debug.Print y8, TypeName(y8), VarType(y8)
Debug.Print y9, TypeName(y9), VarType(y9)
Debug.Print y10, TypeName(y10), VarType(y10)

Çıktı sonucu aşağıdaki gibidir:

4.Yöntem:FormatDateTime(d,constant)):FormatDateTime da string döndürür, ama kullanımı daha basittir, seçenekler sınırlıdır. Seçeneklerde constantlar var, ön tanımlı veya kullanıcı tanımlı parametre yok.

z1 = FormatDateTime(tarih1, vbShortDate)
z2 = FormatDateTime(tarih2, vbLongDate)
z3 = FormatDateTime(stringtarih1, vbLongDate)
z4 = FormatDateTime(stringtarih2, vbLongDate)
z5 = FormatDateTime(numeriktarih1, vbLongDate)
z6 = FormatDateTime(numeriktarih2, vbLongDate)
z7 = FormatDateTime(diyezlitarih1, vbLongDate)
z8 = FormatDateTime(diyezlitarih2, vbLongDate)
z9 = FormatDateTime(hucredentarih1, vbLongDate)
z10 = FormatDateTime(hucredentarih2, vbLongDate)

Şimdi de çıktı alalım.

Debug.Print z1, vbTab, TypeName(z1), VarType(z1), IsDate(z1) 'short olduğu ve içinde gün ismi geçmediği için True,  aşağıdakiler false false false
Debug.Print z2, TypeName(z2), VarType(z2), IsDate(z2)
Debug.Print z3, TypeName(z3), VarType(z3), IsDate(z3)
Debug.Print z4, TypeName(z4), VarType(z4), IsDate(z4)
Debug.Print z5, TypeName(z5), VarType(z5), IsDate(z5)
Debug.Print z6, TypeName(z6), VarType(z6), IsDate(z6)
Debug.Print z7, TypeName(z7), VarType(z7), IsDate(z7)
Debug.Print z8, TypeName(z8), VarType(z8), IsDate(z8)
Debug.Print z9, TypeName(z9), VarType(z9), IsDate(z9)
Debug.Print z10, TypeName(z10), VarType(z10), IsDate(z10)

Sonuç aşağıdaki gibi olacaktır:

Toplama Çıkarma

VBA'de, Exceldeki yaptığımız gibi +1/-1 diyerek toplama çıkarma yapılamıyor. Matematiksel işlemler için belirli fonksiyonlar kullanılmalıdır.

  • DateDiff/DateAdd
  • DateValue("21.01.1979")+1
  • Dateserial(1979,1,21)+1
  • CDate("21.01.1979")+1
  • #"li üzerinde işlem: #5/22/97# - #1/10/97#
  • Date döndüren herhangi bir fonksiyonla. (Date/Now veya UDF gibi)
c1 = diyezlitarih1 + 1 'datevalue veya DateAdd demeye gerek kalmadan doğrudan kullanılabilir
c2 = DateAdd("d", 1, stringtarih1)
c3 = stringtarih1 + 1 'istenmeyen sonuç: noktaları uçup ekleme yapar, yani yıla eklenmiş olur, 21011980
c4 = DateValue(stringtarih1) + 1
c5 = CDate(stringtarih1) + 1
c6 = tarih1 - tarih2
c7 = DateDiff("d", tarih2, tarih1)
c8 = (tarih1 - tarih2) / 30 'yaklaşık ay sayısı
c9 = DateDiff("m", tarih2, tarih1) 'kesin ay sayısı
c10 = (tarih1 - tarih2) / 365 

Şimdi de çıktılarını alalım.

Debug.Print vbNewLine
Debug.Print c1, vbTab, TypeName(c1), IsDate(c1)
Debug.Print c2, vbTab, TypeName(c2), IsDate(c2)
Debug.Print c3, vbTab, TypeName(c3), IsDate(c3)
Debug.Print c4, vbTab, TypeName(c4), IsDate(c4)
Debug.Print c5, vbTab, TypeName(c5), IsDate(c5)
Debug.Print c6, TypeName(c6), IsDate(c6)
Debug.Print c7, vbTab, TypeName(c7), IsDate(c7)
Debug.Print c8, TypeName(c8), IsDate(c8)
Debug.Print c9, vbTab, TypeName(c9), IsDate(c9)
Debug.Print c10, TypeName(c10), IsDate(c10)

Çıktı şöyle olacaktır:

Diğer işlemler

IsDate ile "Tarih mi?" sorgulaması

Bir değerin tarih olup olmadığını sorgulanması için içeriğin Short Date veya saatli Short Date olması lazım. Long Date veya numerik olursa  tarih olarak algılamaz.

Aşağıdaki örnekte, shcedule edilmiş bir makro ile, içindeki dosya isimleri "Falanfilan raporu - 25.07.2017 Sonuçları.xlsm" gibi olan dosyalara bakıyor ve belirtilen süreden önce eski olanları sildiriyorum. Belirli süreyi tespit etme işlemini "Diğer kodlar" bölümünde yapıyorum, şuan bu ksıım önemli değil. Her rapor için tespit ettiğim bu süre değerini bir dictionary'de depoluyorum. Mid(isim, Len(isim) - 19, 10) ile kestiğim kısım 25.07.2017'a denk gelen kısım ve bunun tarih olup olmadığını sorguluyorum. Ancak bazen ilgili klasörde manuel kaydedilmiş dosyalar olabiliyor, bunların silinmesini istemiyorum, keza içinde Format ifadesi geçen dosyaların da silinmesini istemiyorum.

Sub eskilerisil()
'Diğer kodlar
For Each d In 
	klasor.Files
	isim = fso.GetBaseName(d)
	If Len(isim) > 19 Then
		If IsDate(Mid(isim, Len(isim) - 19, 10)) Then tarih = DateValue(Mid(isim,Len(isim) - 19, 10))
	Else
	    tarih = DateSerial(2099, 12, 31)
	End If

	If Not aysonumu( Date - dict(kls)) = True And _
	tarih < Date - dict(kls) And InStr(isim, "Format") = 0 Then
		Kill d
	End If
Next d
End Sub

Saat karşılaştırma

Hour'la, TimeSerial veya TimeValeu ile yapabiliriz.

If Hour(Now)>13 'dakika detayı gerekli değilse
If TimeValue(Now) > #1:00 PM# Then
If TimeValue(Now) > #13:00# Then 'otomatik PM'e çevrilir
If TimeValue(Now) > #13:30:00# Then 'otomatik PM'e çevrilir
If TimeValue(Now) > TimeSerial(13, 0, 0) Then

Bir tarihin belirli kısımlarını alma

Parça fonksiyonları

tarih="31.12.2016 13:35:00"

Debug.Print Year(tarih) '2016
Debug.Print Month(tarih) '12
Debug.Print Day(tarih) '31

'Hour, Minute ve Second ile de saat, dakika e saniye döndürülür

DatePart

Yukarıdak parça parça alan fonksiyonların hepsini tek bir fonksiyon ve parametre ile de yapabilriz, mesela yıl almak için DatePart("yyyy",tarih) kullanılabilir. Ancak yukarıdakileri kullamak daha kolaydır. Yani basit parça alma için bu fonksiyonu kallanmaucağz. Bununla beraber yılın kaçıncı günü, yılın hangi çeyreği, yılın hangi haftası v.s için bu fonksiyon lazım. Mesela bu söylediğim son 3 amaç için sırayla y,q,ww parametrelerini kullanırız.

Dikkat:y ile yyyy karıştırmayın. yyyy yılı veriken tek y yılın gününü verir, d de ayın gününü verir. Parametre olarak kullanılabilecek tüm fadelere yine yukarda linkini verdiğimiz bu sayfadan erişebilirsiniz.

Ay ve hafta adı

Debug.Print MonthName(Month(tarih)) 'Aralık
Debug.Print WeekdayName(Weekday(tarih)) 'Pazar

TimeSerial ve DateSerial

Yıl,Ay, Gün veya Saat,Dakika,Saniye bilgilerinin bir yerden temin edilmesi durumunda da belirli tarih ve saatler elde edilebilmektedir.

Debug.Print DateSerial(2017,1,5)
Debug.Print TimeSerial(8,3,12)

Bazı özel tarihler

Aybaşı:DateSerial(year(Date),month(Date),1)
Aysonu: DateSerial(Year(Date), Month(Date)+1 , 1)-1 veya daha sade olarak DateSerial(Year(Date), Month(Date) + 1, 0)
Haftabaşı: Date - WeekDay(Date, vbUseSystem) + 1 ‘veya vbMonday
Haftasonu:Date - WeekDay(Date, vbUseSystem) + 7
Yılbvaşlı: DateSerial(Year(Date), 1, 1)
Yılsonu: DateSerial(Year(Date)+1, 1, 0)

Timer ile geçen süreyi hesaplama

Timer property'sini genelde bir prosedürün ne kadar sürede çalıştığını bumak amaçlı kullanırız.

Sub timerkontrol()
Dim başlangıç As Single
Dim bitiş As Single
Dim i As Long

başlangıç = Timer

For i = 1 To 100000000 'Bu yapı For-Next döngüsüdür. Şimdilik bu döngünün nasıl kullanıldığını bilmiyor olabilirsiniz, buna takılmayın. Sonraki bölümlerde detaylıca incelenecek.
k = k + 1
Next i

bitiş = Timer

MsgBox ("İşlem süresi:" & vbNewLine & Round(bitiş - başlangıç, 2) & " saniyedir.")
End Sub

YORUMLAR