Tarihsel Fonksiyonlar
Excel’de Tarihlerin Sayısal Karşılığı
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ün sayı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.
Tarihsel İşlemler ve 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$ arası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şkenleri Variant olarak 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. (Tabii 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ç olarak da "/" girilmeli, "." değil. Gün girerken 01 girsen bile enter'a 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.
Metinsel Tarihleri Tarih Tipine Çevirme Yöntemleri
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ğlamaktadı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 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 bilgisi korunmaz. 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 detaylarına buradan ulaşabilirsiniz[^1^][1].
[^1^][1]: [Statology - Convert String to Date in VBA](https://www.statology.org/vba-convert-string-to-date/)Metinsel Tarihleri Tarih Tipine Çevirme Yöntemleri
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ğlamaktadı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 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 bilgisi korunmaz. 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 detaylarına buradan ulaşabilirsiniz[^1^][1].
y1 = Format(tarih1, "Short Date") 'önceden tanımlanmış parametre
y2 = Format(tarih2, "dd.mm.yyyy") 'bu ve aşağıdakiler ise kullanıcı tanımlı parametrelerdir
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
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:
VBA'de Tarihsel İşlemler İçin Matematiksel Fonksiyonlar
VBA'de, Excel'deki gibi +1/-1 diyerek toplama çıkarma yapılamıyor. Matematiksel işlemler için belirli fonksiyonlar kullanılmalıdır.
DateDiff/DateAdd
Örnekler:
- DateValue("21.01.1979") + 1
- DateSerial(1979, 1, 21) + 1
- CDate("21.01.1979") + 1
- #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:
IsDate ile "Tarih mi?" Sorgulaması
Bir değerin tarih olup olmadığını sorgulamak 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, schedule 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, şu an bu kısı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'ye 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, TimeSerial veya TimeValue ile yapabiliriz.
If Hour(Now) > 13 Then '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 ve saniye döndürülür
DatePart
Yukarıdaki parça parça alan fonksiyonların hepsini tek bir fonksiyon ve parametre ile de yapabiliriz, mesela yıl almak için DatePart("yyyy", tarih) kullanılabilir. Ancak yukarıdakileri kullanmak daha kolaydır. Yani basit parça alma için bu fonksiyonu kullanmayacağız. Bununla beraber yılın kaçıncı günü, yılın hangi çeyreği, yılın hangi haftası gibi bilgiler 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ı verirken tek y yılın gününü verir, d de ayın gününü verir. Parametre olarak kullanılabilecek tüm ifadelere yine yukarıda 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ılbaşı: 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ı bulmak 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