Datetime nesnesinin dakikası nasıl yuvarlanır


101

Kullanılarak datetimeüretilmiş bir nesnem var strptime().

>>> tm
datetime.datetime(2010, 6, 10, 3, 56, 23)

Yapmam gereken, dakikayı en yakın 10. dakikaya çevirmek. Şu ana kadar yaptığım şey, dakika değerini alıp üzerinde round () kullanmaktı.

min = round(tm.minute, -1)

Ancak, yukarıdaki örnekte olduğu gibi, dakika değeri 56'dan büyük olduğunda geçersiz bir saat verir. Yani: 3:60

Bunu yapmanın daha iyi bir yolu nedir? datetimeBunu destekliyor mu ?

Yanıtlar:


133

Bu, datetimetm'de depolanan bir nesnenin 'tabanını' önceki 10 dakika işaretine yuvarlatılmış olarak alacaktır tm.

tm = tm - datetime.timedelta(minutes=tm.minute % 10,
                             seconds=tm.second,
                             microseconds=tm.microsecond)

En yakın 10 dakikalık işarete klasik yuvarlama istiyorsanız, şunu yapın:

discard = datetime.timedelta(minutes=tm.minute % 10,
                             seconds=tm.second,
                             microseconds=tm.microsecond)
tm -= discard
if discard >= datetime.timedelta(minutes=5):
    tm += datetime.timedelta(minutes=10)

veya bu:

tm += datetime.timedelta(minutes=5)
tm -= datetime.timedelta(minutes=tm.minute % 10,
                         seconds=tm.second,
                         microseconds=tm.microsecond)

95

Herhangi bir zaman atlamasında bir tarih saatini saniye cinsinden yuvarlayan genel işlev:

def roundTime(dt=None, roundTo=60):
   """Round a datetime object to any time lapse in seconds
   dt : datetime.datetime object, default now.
   roundTo : Closest number of seconds to round to, default 1 minute.
   Author: Thierry Husson 2012 - Use it as you want but don't blame me.
   """
   if dt == None : dt = datetime.datetime.now()
   seconds = (dt.replace(tzinfo=None) - dt.min).seconds
   rounding = (seconds+roundTo/2) // roundTo * roundTo
   return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)

1 saat yuvarlama ve 30 dakika yuvarlama içeren örnekler:

print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60)
2013-01-01 00:00:00

print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=30*60)
2012-12-31 23:30:00

11
Maalesef bu, tz-duyarlı tarih saat ile çalışmaz. Bunun dt.replace(hour=0, minute=0, second=0)yerine kullanılmalı dt.min.
skoval:

2
@ skoval00 + druska tz-duyarlı tarih saatini desteklemek için tavsiyelerinize göre düzenlendi. Teşekkürler!
Le Droid

Teşekkürler @ skoval00 - işlevin neden verilerimle çalışmadığını anlamam biraz zaman aldı
Michel Mesquita

1
Bu benim için uzun süre hiç çalışmıyor. örn. roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60*24*7)vsroundTime(datetime.datetime(2012,12,30,23,44,59,1234),roundTo=60*60*24*7)
CPBL

Sorunu anlamak için buna bakın:datetime.timedelta(100,1,2,3).seconds == 1
CPBL

15

Değiştirdiğim en iyi cevaptan, yalnızca datetime nesnelerini kullanan uyarlanmış bir sürüme, bu, dönüşümü saniyelere yapmak zorunda kalmaz ve çağıran kodu daha okunaklı hale getirir:

def roundTime(dt=None, dateDelta=datetime.timedelta(minutes=1)):
    """Round a datetime object to a multiple of a timedelta
    dt : datetime.datetime object, default now.
    dateDelta : timedelta object, we round to a multiple of this, default 1 minute.
    Author: Thierry Husson 2012 - Use it as you want but don't blame me.
            Stijn Nevens 2014 - Changed to use only datetime objects as variables
    """
    roundTo = dateDelta.total_seconds()

    if dt == None : dt = datetime.datetime.now()
    seconds = (dt - dt.min).seconds
    # // is a floor division, not a comment on following line:
    rounding = (seconds+roundTo/2) // roundTo * roundTo
    return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)

1 saat yuvarlama ve 15 dakika yuvarlama içeren örnekler:

print roundTime(datetime.datetime(2012,12,31,23,44,59),datetime.timedelta(hour=1))
2013-01-01 00:00:00

print roundTime(datetime.datetime(2012,12,31,23,44,49),datetime.timedelta(minutes=15))
2012-12-31 23:30:00

1
Ayrıca iyi değil: print roundTime(datetime.datetime(2012,12,20,23,44,49),datetime.timedelta(days=15)) 2012-12-20 00:00:00whileprint roundTime(datetime.datetime(2012,12,21,23,44,49),datetime.timedelta(days=15)) 2012-12-21 00:00:00
CPBL

3
Yukarıdakileri takip edin: Sadece gelişigüzel zaman deltaları için, örneğin 1 günden fazla olanlar için işe yaramadığına dikkat edin. Bu soru dakikaları yuvarlamakla ilgilidir, bu nedenle bu uygun bir kısıtlama, ancak kodun yazılma biçiminde daha açık olabilir.
CPBL

15

Stijn Nevens kodunu kullandım (teşekkürler Stijn) ve paylaşacak küçük bir eklentim var. Yukarı, aşağı yuvarlama ve en yakına yuvarlama.

güncelleme 2019-03-09 = yorum Spinxz dahil; teşekkür ederim.

güncelleme 2019-12-27 = Bart dahil edildi; teşekkür ederim.

"X saat" veya "X dakika" veya "X saniye" date_delta için test edilmiştir.

import datetime

def round_time(dt=None, date_delta=datetime.timedelta(minutes=1), to='average'):
    """
    Round a datetime object to a multiple of a timedelta
    dt : datetime.datetime object, default now.
    dateDelta : timedelta object, we round to a multiple of this, default 1 minute.
    from:  http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python
    """
    round_to = date_delta.total_seconds()
    if dt is None:
        dt = datetime.now()
    seconds = (dt - dt.min).seconds

    if seconds % round_to == 0 and dt.microsecond == 0:
        rounding = (seconds + round_to / 2) // round_to * round_to
    else:
        if to == 'up':
            # // is a floor division, not a comment on following line (like in javascript):
            rounding = (seconds + dt.microsecond/1000000 + round_to) // round_to * round_to
        elif to == 'down':
            rounding = seconds // round_to * round_to
        else:
            rounding = (seconds + round_to / 2) // round_to * round_to

    return dt + datetime.timedelta(0, rounding - seconds, - dt.microsecond)

# test data
print(round_time(datetime.datetime(2019,11,1,14,39,00), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,2,14,39,00,1), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,3,14,39,00,776980), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,4,14,39,29,776980), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2018,11,5,14,39,00,776980), date_delta=datetime.timedelta(seconds=30), to='down'))
print(round_time(datetime.datetime(2018,11,6,14,38,59,776980), date_delta=datetime.timedelta(seconds=30), to='down'))
print(round_time(datetime.datetime(2017,11,7,14,39,15), date_delta=datetime.timedelta(seconds=30), to='average'))
print(round_time(datetime.datetime(2017,11,8,14,39,14,999999), date_delta=datetime.timedelta(seconds=30), to='average'))
print(round_time(datetime.datetime(2019,11,9,14,39,14,999999), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2012,12,10,23,44,59,7769),to='average'))
print(round_time(datetime.datetime(2012,12,11,23,44,59,7769),to='up'))
print(round_time(datetime.datetime(2010,12,12,23,44,59,7769),to='down',date_delta=datetime.timedelta(seconds=1)))
print(round_time(datetime.datetime(2011,12,13,23,44,59,7769),to='up',date_delta=datetime.timedelta(seconds=1)))
print(round_time(datetime.datetime(2012,12,14,23,44,59),date_delta=datetime.timedelta(hours=1),to='down'))
print(round_time(datetime.datetime(2012,12,15,23,44,59),date_delta=datetime.timedelta(hours=1),to='up'))
print(round_time(datetime.datetime(2012,12,16,23,44,59),date_delta=datetime.timedelta(hours=1)))
print(round_time(datetime.datetime(2012,12,17,23,00,00),date_delta=datetime.timedelta(hours=1),to='down'))
print(round_time(datetime.datetime(2012,12,18,23,00,00),date_delta=datetime.timedelta(hours=1),to='up'))
print(round_time(datetime.datetime(2012,12,19,23,00,00),date_delta=datetime.timedelta(hours=1)))

Bu bana yardımcı oldu. PySpark'ta kullanılıyorsa, tarih saatini bir tarih saat nesnesi yerine bir dizge olarak ayrıştırmak için bunu eklemek istiyorum.
Maksimum

4
"Yukarı" yuvarlama, belki de çoğu insanın beklediğini yapmıyor. Dt'nin yuvarlamaya ihtiyacı olmasa bile bir sonraki date_delta'ya yuvarlarsınız: ör. Round_to = 60 ile 15: 30: 00.000 15: 31: 00.000 olur
spinxz 18'17'de

upYuvarlama bu fonksiyon ile zaten hatalı olduğu; 2019-11-07 14:39:00.776980ile date_deltaeşit 30 saniye ve örneğin için to='up'sonuçlar 2019-11-07 14:39:00.
Bart

1
Çok teşekkürler!! Her ne kadar upgenel kullanımını olmayabilir yuvarlama sen dakikalık sınırında başlar uygulamalarla uğraşırken, gerektiğinde
Rahul Bharadwaj

5

Pandaların tarih-saat tur özelliği vardır, ancak Pandalar'daki çoğu şeyde olduğu gibi Seri formatında olması gerekir.

>>> ts = pd.Series(pd.date_range(Dt(2019,1,1,1,1),Dt(2019,1,1,1,4),periods=8))
>>> print(ts)
0   2019-01-01 01:01:00.000000000
1   2019-01-01 01:01:25.714285714
2   2019-01-01 01:01:51.428571428
3   2019-01-01 01:02:17.142857142
4   2019-01-01 01:02:42.857142857
5   2019-01-01 01:03:08.571428571
6   2019-01-01 01:03:34.285714285
7   2019-01-01 01:04:00.000000000
dtype: datetime64[ns]

>>> ts.dt.round('1min')
0   2019-01-01 01:01:00
1   2019-01-01 01:01:00
2   2019-01-01 01:02:00
3   2019-01-01 01:02:00
4   2019-01-01 01:03:00
5   2019-01-01 01:03:00
6   2019-01-01 01:04:00
7   2019-01-01 01:04:00
dtype: datetime64[ns]

Dokümanlar - Sıklık dizesini gerektiği gibi değiştirin.


Referans için, Timestampiçerir floorve ceilaynı zamanda
poulter7

3

koşulu kullanmak istemiyorsanız, modulooperatörü kullanabilirsiniz :

minutes = int(round(tm.minute, -1)) % 60

GÜNCELLEME

böyle bir şey mi istedin

def timeround10(dt):
    a, b = divmod(round(dt.minute, -1), 60)
    return '%i:%02i' % ((dt.hour + a) % 24, b)

timeround10(datetime.datetime(2010, 1, 1, 0, 56, 0)) # 0:56
# -> 1:00

timeround10(datetime.datetime(2010, 1, 1, 23, 56, 0)) # 23:56
# -> 0:00

.. sonucu string olarak istiyorsanız. datetime sonucunu elde etmek için timedelta kullanmak daha iyidir - diğer yanıtlara bakın;)


Ama o zaman buradaki sorun, saatin de artması gerektiğidir
Lucas Manco

1
@Lucas Manco - Benim çözümüm de iyi çalışıyor ve bence daha mantıklı.
Omnifarious

2

bunu kullanıyorum. tz farkında veri zamanlarıyla çalışma avantajına sahiptir.

def round_minutes(some_datetime: datetime, step: int):
    """ round up to nearest step-minutes """
    if step > 60:
        raise AttrbuteError("step must be less than 60")

    change = timedelta(
        minutes= some_datetime.minute % step,
        seconds=some_datetime.second,
        microseconds=some_datetime.microsecond
    )

    if change > timedelta():
        change -= timedelta(minutes=step)

    return some_datetime - change

sadece bir saatten az zaman dilimleri için çalışma dezavantajına sahiptir.


2

İşte kayan nokta hassas sorunları ve harici kitaplık bağımlılıkları olmayan daha basit bir genelleştirilmiş çözüm:

import datetime

def time_mod(time, delta, epoch=None):
    if epoch is None:
        epoch = datetime.datetime(1970, 1, 1, tzinfo=time.tzinfo)
    return (time - epoch) % delta

def time_round(time, delta, epoch=None):
    mod = time_mod(time, delta, epoch)
    if mod < (delta / 2):
       return time - mod
    return time + (delta - mod)

Senin durumunda:

>>> tm = datetime.datetime(2010, 6, 10, 3, 56, 23)
>>> time_round(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 4, 0)

1

Basit bir yaklaşım:

def round_time(dt, round_to_seconds=60):
    """Round a datetime object to any number of seconds
    dt: datetime.datetime object
    round_to_seconds: closest number of seconds for rounding, Default 1 minute.
    """
    rounded_epoch = round(dt.timestamp() / round_to_seconds) * round_to_seconds
    rounded_dt = datetime.datetime.fromtimestamp(rounded_epoch).astimezone(dt.tzinfo)
    return rounded_dt

0
def get_rounded_datetime(self, dt, freq, nearest_type='inf'):

    if freq.lower() == '1h':
        round_to = 3600
    elif freq.lower() == '3h':
        round_to = 3 * 3600
    elif freq.lower() == '6h':
        round_to = 6 * 3600
    else:
        raise NotImplementedError("Freq %s is not handled yet" % freq)

    # // is a floor division, not a comment on following line:
    seconds_from_midnight = dt.hour * 3600 + dt.minute * 60 + dt.second
    if nearest_type == 'inf':
        rounded_sec = int(seconds_from_midnight / round_to) * round_to
    elif nearest_type == 'sup':
        rounded_sec = (int(seconds_from_midnight / round_to) + 1) * round_to
    else:
        raise IllegalArgumentException("nearest_type should be  'inf' or 'sup'")

    dt_midnight = datetime.datetime(dt.year, dt.month, dt.day)

    return dt_midnight + datetime.timedelta(0, rounded_sec)

0

Stijn Nevens'e dayalı ve Django için değiştirilmiş, geçerli zamanı en yakın 15 dakikaya yuvarlamak için kullanılır.

from datetime import date, timedelta, datetime, time

    def roundTime(dt=None, dateDelta=timedelta(minutes=1)):

        roundTo = dateDelta.total_seconds()

        if dt == None : dt = datetime.now()
        seconds = (dt - dt.min).seconds
        # // is a floor division, not a comment on following line:
        rounding = (seconds+roundTo/2) // roundTo * roundTo
        return dt + timedelta(0,rounding-seconds,-dt.microsecond)

    dt = roundTime(datetime.now(),timedelta(minutes=15)).strftime('%H:%M:%S')

 dt = 11:45:00

tam tarih ve saate ihtiyacınız varsa, sadece .strftime('%H:%M:%S')


0

İstisna yakalandığında hız için en iyisi değil, ancak bu işe yarayacaktır.

def _minute10(dt=datetime.utcnow()):
    try:
        return dt.replace(minute=round(dt.minute, -1))
    except ValueError:
        return dt.replace(minute=0) + timedelta(hours=1)

Zamanlamalar

%timeit _minute10(datetime(2016, 12, 31, 23, 55))
100000 loops, best of 3: 5.12 µs per loop

%timeit _minute10(datetime(2016, 12, 31, 23, 31))
100000 loops, best of 3: 2.21 µs per loop

0

Bir datetimenesne için belirli bir zaman birimine, burada saniyelere yuvarlamak için iki satırlık sezgisel bir çözüm t:

format_str = '%Y-%m-%d %H:%M:%S'
t_rounded = datetime.strptime(datetime.strftime(t, format_str), format_str)

Farklı bir birime yuvarlamak isterseniz, basitçe değiştirin format_str.

Bu yaklaşım, yukarıdaki yöntemlerde olduğu gibi keyfi zaman miktarlarına yuvarlanmaz, ancak belirli bir saate, dakikaya veya saniyeye yuvarlamanın güzel bir Pythonic yoludur.


0

Diğer çözüm:

def round_time(timestamp=None, lapse=0):
    """
    Round a timestamp to a lapse according to specified minutes

    Usage:

    >>> import datetime, math
    >>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), 0)
    datetime.datetime(2010, 6, 10, 3, 56)
    >>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), 1)
    datetime.datetime(2010, 6, 10, 3, 57)
    >>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), -1)
    datetime.datetime(2010, 6, 10, 3, 55)
    >>> round_time(datetime.datetime(2019, 3, 11, 9, 22, 11), 3)
    datetime.datetime(2019, 3, 11, 9, 24)
    >>> round_time(datetime.datetime(2019, 3, 11, 9, 22, 11), 3*60)
    datetime.datetime(2019, 3, 11, 12, 0)
    >>> round_time(datetime.datetime(2019, 3, 11, 10, 0, 0), 3)
    datetime.datetime(2019, 3, 11, 10, 0)

    :param timestamp: Timestamp to round (default: now)
    :param lapse: Lapse to round in minutes (default: 0)
    """
    t = timestamp or datetime.datetime.now()  # type: Union[datetime, Any]
    surplus = datetime.timedelta(seconds=t.second, microseconds=t.microsecond)
    t -= surplus
    try:
        mod = t.minute % lapse
    except ZeroDivisionError:
        return t
    if mod:  # minutes % lapse != 0
        t += datetime.timedelta(minutes=math.ceil(t.minute / lapse) * lapse - t.minute)
    elif surplus != datetime.timedelta() or lapse < 0:
        t += datetime.timedelta(minutes=(t.minute / lapse + 1) * lapse - t.minute)
    return t

Bu yardımcı olur umarım!



0

Aşırı karmaşık görünüyor

def round_down_to():
    num = int(datetime.utcnow().replace(second=0, microsecond=0).minute)
    return num - (num%10)

0

evet, verileriniz bir pandalar serisindeki bir DateTime sütununa aitse, yerleşik pandas.Series.dt.round işlevini kullanarak verileri yuvarlayabilirsiniz. Pandas.Series.dt.round'daki belgelere buradan bakın . 10 dakikaya yuvarlama durumunda, aşağıdaki gibi Series.dt.round ('10min') veya Series.dt.round ('600s') olacaktır:

pandas.Series(tm).dt.round('10min')

Örnek kod eklemek için düzenleyin:

import datetime
import pandas

tm = datetime.datetime(2010, 6, 10, 3, 56, 23)
tm_rounded = pandas.Series(tm).dt.round('10min')
print(tm_rounded)

>>> 0   2010-06-10 04:00:00
dtype: datetime64[ns]

Bu cevabın yeni veya yararlı bir şey kattığından emin değilim. Aynı şeyi açıklayan bir cevap zaten vardı: stackoverflow.com/a/56010357/7851470
Georgy

evet, bunları bana gösterdiğin için teşekkür ederim. Yanıtıma örnek kod eklememek ve diğer insanların yanıtlarını kontrol etmemek benim hatam. Bu konuda kendimi geliştirmeye çalışacağım.
Nguyen Bryan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.