Python Datetime Modülü İle Tarih - Zaman İşlemleri

Python ile çalışırken sıklıkla bu işlemin ve/veya paketin R ya da JS karşılığı nedir gibi sorular sorarak ilerliyorum. Bu konuda birkaç yazı daha yayınlayacağım. Bu karşılaştırmanın temel nedeni hem hafızamı taze tutarak pratik yapmak hem de farklı çözüm yöntemlerine açık olmak istemem. Bu yazıda da kısmen bu konuya ve Python programlama dilinin ön tanımlı olarak sunduğu datetime modülüne değinmeye çalışacağım.

AA

R programlama dili as.Date fonksiyonu ile temel bir şekilde tarih veri tipi ile ilgili işlemler yapılabilmesini mümkün kılmakta1. Ancak, zaman dilimleri ve biçimler arasında geçişlerde bu fonksiyon maalesef yeterli olmayabiliyor. Bu gibi durumlarda, tidyverse koleksiyonunun bir parçası olan lubridate paketi ile pratik bir şekilde tarih ve saat verileri yönetilebiliyor2. JavaScript/NodeJS tarafında ise hatırlayabildiğim sadece datetime modülü oldu3.

Python tarafında ise öne çıkan modül ön tanımlı olarak yine datetime adı ile sunuluyor. Bunun yanı sıra, kullanılabilecek başka ne gibi paketler olduğunu da merak ederek kısa bir araştırma yaptım ve Python için yazının son bölümünde datetime modülüne alernatif olabilecek birkaç pakete yer verdim.

Datetime Modülü

Python veri tipleri arasında tarih-zaman tipi yer almamakta. Ancak, datetime modülü ile metin dizileri ve tarih-zaman biçimleri arasında işlemler yürütülebilmekte. Python İle Basit Twitter API İşlemleri başlıklı yazıda yer alan örnek üzerine çalışırken, tarih işlemlerinde hem datetime modülü dokümantasyonunu inceleme ve sunulan metodlarla ilgili örnek işlemler gerçekleştirme imkanım oldu. Bu yazıda da aldığım kısa notları paylaşmak ve basit birkaç örneğe yer vermek istiyorum4 5.

İlgili yazıdaki tarih işlemi şu şekildeydi:

currentTime = datetime.datetime.today().strftime('%Y-%m-%d') # '2021-07-31'
previousTime = (datetime.datetime.today() - datetime.timedelta(days=7)).strftime('%Y-%m-%d') # '2021-07-24'

datetime modülünü import datetime ile çalışmalara dahil edebiliriz. Ancak, modül bünyesinde date, time ve datetime sınıflarını barındırdığı için sadece ilgili sınıf da çağırılabilir.

from datetime import datetime # datetime.now()
# import datetime # datetime.datetime.now()

# dir(datetime.datetime)
# ['__add__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__rsub__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', 'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fold', 'fromisoformat', 'fromordinal', 'fromtimestamp', 'hour', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min', 'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime', 'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'today', 'toordinal', 'tzinfo', 'tzname', 'utcfromtimestamp', 'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year']

date sınıfı tarihler ile ilgili işlemler yapılabilmesini sağlarken, time zaman ile ilgili işlemleri barındırır. datetime ise date ve time sınıflarını kapsar.

NOW ve TODAY

now() ve today() metodları bize anlık olarak yıl, ay, gün, saat, dakika, saniye ve mikrosaniye bilgilerini verir. Bu zaman bilgilerini ayrı ayrı çağırmak da mümkün.

datetime.datetime.today() # datetime.datetime(2021, 7, 31, 19, 31, 46, 197762)

datetime.datetime.now().year # 2021
datetime.datetime.now().month # 7
datetime.datetime.now().day # 31
datetime.datetime.now().hour # 19
datetime.datetime.now().minute # 31
datetime.datetime.now().second # 46
datetime.datetime.now().microsecond # 197762

STRFTIME ve STRPTIME

strftime() metodu direktifler aracılığı ile tarih ve saat formatlarını yönetebilmemizi ve bu işlem sonucunda tarih nesnesinin bir karakter dizisine dönüştürülmesini sağlar.

datetime.datetime.now().strftime(format='%Y') # 2021
datetime.datetime.now().strftime(format='%m/%d/%y') # 07/31/21
datetime.datetime.now().strftime(format='%x') # 07/31/21
datetime.datetime.now().strftime(format='%d.%m.%Y') # 31.07.2021

strptime() metodu ise yine tarih ve saat formatlarını kullanarak bir karakter dizisini tarih nesnesine dönüştürebilmemizi mümkün kılar.

datetime.datetime.strptime('31 July 2014 12:34:44', '%d %B %Y %H:%M:%S') # datetime.datetime(2014, 7, 31, 12, 34, 44)
datetime.datetime.strptime('07/31/21', '%m/%d/%y') # datetime.datetime(2021, 7, 31, 0, 0)
datetime.datetime.strptime('07/31/21', '%x') # datetime.datetime(2021, 7, 31, 0, 0)

Bir başka örnekte de farklı seperatörlere sahip tarihsel ifadeleri tarih formatına dönüştürelim. Bu aşamada ek olarak regex işlemleri yapmamızı sağlayan re paketini de örneğe dahil edeceğim.

import re
dates = [datetime.datetime.strptime(re.sub('[^0-9]', '/', d), '%m/%d/%Y') for d in ['11-23-2021', '05/06/1991', '10 10 2000']]
print(dates)

Öne Çıkan Direktifler

Aşağıda sıklıkla kullanılan bazı direktifleri tablo halinde görebilirsiniz6 7.

Direktif Anlamı Örnek
%X Lokal olarak uyarlanmış zaman 21:30:00 (en_US)
%x Lokal olarak uyarlanmış tarih 08/16/1988 (en_US)
%Y 4 haneli yıl 2021, 1990, vb.
%y 2 haneli yıl 21,90, vb.
%m Sayısal olarak ay 01, 10, 12
%B Lokal olarak uyarlanmış ay July, Temmuz, vb.
%b Lokal olarak uyarlanmış, kısaltılmış ay Jul, Tem, vb.
%A Lokal olarak uyarlanmış günün adı Monday, Pazartesi, vb.
%a Lokal olarak uyarlanmış, kısaltılmış günün adı Mon, Pzt, vb.
%w Sayısal olarak haftanın günü 0 (Pazar), 3, 6, vb.
%d Sayısal olarak ayın günü 01, 22, 31, vb.
%H 24 saalik zaman gösterimi 00, 11, 23, vb.
%I 12 saatlik zaman gösterimi 01, 10, 24, vb.
%p Lokal olarak uyarlanmış gün periyodu AM, ÖÖ, PM, ÖS
%M Dakika 00,33,59
%S Saniye 00, 33, 59
%Z Zaman dilimi UTC, EST, GMT, vb.
%z Sayısal formatta zaman dilimi +0000, -0400, vb.

Lokal tanımları sisteme göre değişiklik gösterebilir. Seçilebilir tanımları görmek için komut satırında locale -a komutunu uygulayabilirsiniz. Bu komut gibi bir çıktı döndürecektir.

C
C.UTF-8
en_US.utf8
tr_TR
POSIX

Lokal bilgisini ayrıca Python modülü olan locale ile de görüntülemek mümkün8.

import locale
print(locale.getlocale()) # ('en_US', 'UTF-8')

locale -a ile listelenen seçenekler arasından birini seçmek için setlocale() metodunu kullanabiliriz.

locale.setlocale(locale.LC_ALL, 'en_US')
datetime.datetime.now().strftime(format='%b') # Aug
datetime.datetime.now().strftime(format='%x') # 07/31/2021

locale.setlocale(locale.LC_ALL, 'tr_TR')
datetime.datetime.now().strftime(format='%b') # Ağu
datetime.datetime.now().strftime(format='%x') # 31/07/2021

CTIME

ctime() metodu belirtilen tarih ve zamanı okunaklı bir karakter dizisi verir.

datetime.datetime.today().ctime() # Sat Jul 31 21:21:24 2021
datetime.datetime(2021, 7, 1).ctime() # Thu Jul  1 00:00:00 2021
(datetime.datetime.today() - datetime.timedelta(days=217)).ctime() # Sat Dec 26 21:24:04 2020
datetime.datetime.ctime(datetime.datetime.now()) # Sun Aug  1 07:21:55 2021

TIMESTAMP ve FROMTIMESTAMP

Dosya/dizin işlemlerinde oluşturma ve son değişiklik tarihleri gibi değerleri zaman etiketi (timestamp) biçiminde görüntüleriz.

os.stat('Image.png').st_mtime #1627801430.7354434

timestamp() metodu tarih ve zaman değerlerini zaman etiketi biçimine dönüştürebilmemizi sağlar.

datetime.datetime.today().timestamp() # 1627802786.984535
(datetime.datetime.today() - datetime.timedelta(days=217)).timestamp() # 1609054148.42208
datetime.datetime.fromtimestamp(datetime.datetime.timestamp(datetime.datetime.now())) # datetime.datetime(2021, 8, 1, 7, 33, 7, 576549)

fromtimestamp() metodu ise zaman etiketinin insan tarafından okunabilir (human readable) biçime dönüştürülmesini sağlar.

datetime.datetime.fromtimestamp(1627802786.984535) # datetime.datetime(2021, 8, 1, 7, 26, 26, 984535)

Diğer İşlemler

Zaman işlemlerinde kullanılabilecek diğer konulara da kısaca değinelim.

ISOFORMAT ve FROMISOFORMAT

isoformat() metodu tarih-zaman değerini ISO formatında edinebilmemizi sağlar.

datetime.datetime.utcfromtimestamp(1627804283.441646).isoformat() # 2021-08-01T07:51:23.441646

fromisoformat() metodu ISO formatındaki tarihsel ifadeyi datetime nesnesine dönüştürür.

datetime.fromisoformat('2021-08-01T00:05:23') # datetime.datetime(2021, 8, 1, 0, 5, 23)

TIMEDELTA

Timedelta nesnesi, bir süreyi, iki tarih veya saat arasındaki farkı temsil eder, bu sayede karşılaştırmalar ve matematiksel işlemler yapılabilir9.

datetime.datetime.today() - datetime.timedelta(days=7) # datetime.datetime(2021, 7, 25, 8, 2, 45, 800333)
datetime.datetime.today() + datetime.timedelta(days=-127) # datetime.datetime(2021, 3, 27, 8, 16, 3, 744547)
(datetime.datetime.today() - datetime.timedelta(days=50, hours=8, seconds=27, microseconds=10, minutes=5)).isoformat() # 2021-06-11T23:59:19.479398

REPLACE

datetime.datetime(2002, 12, 31).replace(day=15) # datetime.datetime(2002, 12, 15, 0, 0)
datetime.datetime(2021, 5, 10).replace(year=1995, month=11, day=15).isocalendar() # (1995, 46, 3)

DİĞER MODÜLLER

PANDAS

Pandas datetime ve timedelta veri tiplarini kullanılabilir olarak sunar. Bu sayede, ön tanımlı datetime modülünün yanı sıra, Pandas kapsamında sunulan datetime ve timedelta nesneleri ile de kolaylıkla tarihsel-zamansal işlemler yapabilmek mümkün hale gelmektedir10 11.

import pandas as pd
pd.to_datetime("1th of Aug, 2021") # Timestamp('2021-08-01 00:00:00')

df = pd.DataFrame(['2021-10-05', '2019-05-06', '2015-03-11'], columns=['Dates'])
[(pd.Timestamp(dt) + pd.Timedelta('-15 day')).day_name() for dt in df['Dates']] # ['Monday', 'Sunday', 'Tuesday']

pd.Timedelta("1 days 2 hours") # Timedelta('1 days 02:00:00')
pd.Timedelta(pd.offsets.Day(2)) # Timedelta('2 days 00:00:00')

pd.Series(pd.date_range("2012-1-1", periods=3, freq="D"))
'''
0   2012-01-01
1   2012-01-02
2   2012-01-03
dtype: datetime64[ns]
'''

pd.date_range("2018-01-01", periods=3, freq="H")
'''
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 01:00:00', '2018-01-01 02:00:00'],
dtype='datetime64[ns]', freq='H')
'''

pd.date_range(start='1/1/2018', end='1/08/2018')
'''
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
               '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
              dtype='datetime64[ns]', freq='D')
'''

pd.date_range(start='1/1/2018', periods=5, freq='M')
'''
DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30',
               '2018-05-31'],
              dtype='datetime64[ns]', freq='M')
'''

df = pd.DataFrame({'Dates': ['2021-10-05', '2021-10-06', '2021-10-04','2021-11-15', '2021-11-21', '2021-11-09'], 'Counts': [20,23,41,55,32,24]})
df['Dates'] = pd.to_datetime(df['Dates'])
df.resample('M', on='Dates')['Counts'].mean()

'''
Dates
2021-10-31    28.0
2021-11-30    37.0
Freq: M, Name: Counts, dtype: float64
'''

df['Counts'].groupby(df['Dates'].dt.month).mean()
'''
Dates
10    28.0
11    37.0
Name: Counts, dtype: float64
'''

DATEUTIL

dateutil datetime'in daha pratik ve geliştirilmiş versiyonu olarak değerlendirilebilir12.

from datetime import *; from dateutil.relativedelta import *
import calendar

NOW = datetime.now()
TODAY = date.today()
print(NOW) # 2021-08-01 08:43:27.891459
print(TODAY) # 2021-08-01

NOW+relativedelta(months=+1) # datetime.datetime(2021, 9, 1, 8, 45, 51, 522361)

ARROW

Python için geliştirilen Arrow'un, R programlama dili için geliştirilen lubridate paketine en aykın seçenek olduğunu düşünüyorum13. Özellikle zaman dilimleri ilgili metodlar sayeside oldukça kolay bir şekilde yönetilebilmekte.

import arrow

arrow.utcnow() # <Arrow [2021-08-01T08:49:17.631022+00:00]>
arrow.utcnow().shift(hours=-1) # <Arrow [2021-08-01T07:50:21.604033+00:00]>
arrow.utcnow().to('GMT+3') # <Arrow [2021-08-01T11:53:15.176165+03:00]>
arrow.utcnow().to('GMT-3').shift(hours=-1) # <Arrow [2021-08-01T10:54:03.573688+03:00]>
arrow.utcnow().to('EST').shift(hours=2).humanize() # in 2 hours

SIMPLEDATE

Simpledate paketi zaman dilimi ve formatlar gibi biçimler arasında dönüştürme başta olmak üzere çeşitli kolaylaştırıcı metodlar sunar14.

!pip install pytz tzlocal simple-date

import simpledate

simpledate.SimpleDate(tz='America/New_York') # SimpleDate('2021-08-01 05:19:47.150963 EDT', tz='America/New_York')
simpledate.SimpleDate(tz=120, format='%Y-%m-%d %z') # SimpleDate('2021-08-01 +0200', tz='pytz.FixedOffset(120)')
simpledate.SimpleDate('2013-01-01', tz='EST', country='US') # SimpleDate('2013-01-01')
simpledate.SimpleDate('6/12/2013', format='%m/%d/%Y') # SimpleDate('06/12/2013', tz='Etc/UTC')

[simpledate.best_guess_utc(date) for date in ['1/6/2013 BST', '1/6/2013 EST', 'Tue, 18 Jun 2013 12:19:09 -0400']]
'''
[datetime.datetime(2013, 5, 31, 23, 0, tzinfo=<UTC>),
 datetime.datetime(2013, 1, 6, 5, 0, tzinfo=<UTC>),
 datetime.datetime(2013, 6, 18, 16, 19, 9, tzinfo=<UTC>)]
'''

datetime paketi ile ilgili notlarım şimdilik bu kadar. İlerleyen zaman içerisinde yeni notlar ve örnekler eklemeye devam edeceğim.