Bir tarihe X ay eklemek için JavaScript işlevi


209

Bir JavaScript tarihine X ay eklemek için en kolay, en temiz yolu arıyorum.

Daha doğrusu istemem yılın üzerinde yuvarlanan işlemek veya zorunda kendi işlevi yazma .

Bunu yapabilecek bir şey var mı?


4
Tarihin prototip nesnesine şu şekilde bir yöntem eklemeyi deneyin: --------------- Date.prototype.addMonth = function (n) {return new Date (this.setMonth (this.getMonth ( ) + n)); };
Moises Hidalgo

1
@ kr37, hedef ayda bugünün numarası yoksa Moises Hidalgo'nun yanıtı düzgün çalışmayacaktır. bmpasini'nin yanıtı da bunu ele alıyor
Alexandru Severin

Buradaki yanıtlar çok iyi değil, daha iyi yanıtlar JavaScript'te bir Tarihe ay ekleme bölümünde .
RobG

Yanıtlar:


278

Aşağıdaki işlev, JavaScript'teki ( kaynak ) bir tarihe aylar ekler . Yıl devir ve değişen ay uzunluklarını dikkate alır:

function addMonths(date, months) {
    var d = date.getDate();
    date.setMonth(date.getMonth() + +months);
    if (date.getDate() != d) {
      date.setDate(0);
    }
    return date;
}

// Add 12 months to 29 Feb 2016 -> 28 Feb 2017
console.log(addMonths(new Date(2016,1,29),12).toString());

// Subtract 1 month from 1 Jan 2017 -> 1 Dec 2016
console.log(addMonths(new Date(2017,0,1),-1).toString());

// Subtract 2 months from 31 Jan 2017 -> 30 Nov 2016
console.log(addMonths(new Date(2017,0,31),-2).toString());

// Add 2 months to 31 Dec 2016 -> 28 Feb 2017
console.log(addMonths(new Date(2016,11,31),2).toString());

Yukarıdaki çözüm, hedef aydan daha fazla sayıda günle bir aydan taşınmanın uç durumunu kapsar. Örneğin.

  • 29 Şubat 2020'ye on iki ay ekleyin (28 Şubat 2021 olmalıdır)
  • 31 Ağustos 2020'ye bir ay ekleyin (30 Eylül 2020 olmalıdır)

Başvuru sırasında ayın günü değişirse setMonth, ay uzunluğundaki farklılık nedeniyle bir sonraki aya taştığımızı biliyoruz. Bu durumda, setDate(0)önceki ayın son gününe dönmek için kullanırız .

Not: Bu yanıtın bu sürümü, farklı ay uzunluklarını zarif bir şekilde ele almayan önceki bir sürümün (aşağıda) yerini almaktadır.

var x = 12; //or whatever offset
var CurrentDate = new Date();
console.log("Current date:", CurrentDate);
CurrentDate.setMonth(CurrentDate.getMonth() + x);
console.log("Date after " + x + " months:", CurrentDate);

130
Dikkatli - bu, çoğu ayın 31'inci gününe ekleme gibi kenar durumlarda işe yaramaz. Örneğin, bu yöntemi kullanan 31 Ekim 2011 + 1 ay, Javascript'in standart Date nesnesi kullanılarak 01 Aralık 2011'dir.
tad

6
İyi yakaladın, biraz. Bunu görmediğime şaşırdım. T-SQL Select DATEADD'nin (ay, 1, '2011-10-31') '2011-11-30' verdiğini ve bunun makul olduğunu unutmayın. Kötü bir cevapla 10 oy aldım. Güzel. :-)
Çad

2
Ayrıca artık yılları izleyin - artık yılda 29 Şubat'a 1 yıl eklemek, hangi dili kullandığınıza bağlı olarak (genel bir yanıt olarak) öngörülemeyen sonuçlar verecektir. Bu, Microsoft Azure bulut platformunu 2012'de birkaç saat boyunca indirdi
Ben Walding

15
Bir yoktur addMonthsişlevi burada saygı ayın sonuna (örneğin o 2012-01-31 + 1 month = 2012-02-29ve benzeri).
RobG

3
sadece her ayın 1. tarihini ayarlamak için ekleyin: today.setHours (0, 0, 0, 0); today.setDate (1);
Alexey Strakh

56

Tarih-saat manipülasyonları için moment.js kütüphanesini kullanıyorum . Bir ay eklemek için örnek kod:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');

12
Kendi akıl sağlığınız için moment.js'yi kullanın.
Gaʀʀʏ

3
@Garry ile kesinlikle katılıyorum: moment.js kullanın.
Michel

2
Bunun en temiz yol olduğunu kabul ediyorum, ama soru " Bunu yapabilen yerleşik bir şey var mı ?" Moment, sayfa boyutuna onlarca Kb ekliyor
Evgeny

Bu iyiydi. İnternette bundan daha iyisini yapabilen başka hiçbir eklenti bulamadım.
Eraniichan

26

Bu işlev kenar durumlarını işler ve hızlıdır:

function addMonthsUTC (date, count) {
  if (date && count) {
    var m, d = (date = new Date(+date)).getUTCDate()

    date.setUTCMonth(date.getUTCMonth() + count, 1)
    m = date.getUTCMonth()
    date.setUTCDate(d)
    if (date.getUTCMonth() !== m) date.setUTCDate(0)
  }
  return date
}

Ölçek:

> d = new Date('2016-01-31T00:00:00Z');
Sat Jan 30 2016 18:00:00 GMT-0600 (CST)
> d = addMonthsUTC(d, 1);
Sun Feb 28 2016 18:00:00 GMT-0600 (CST)
> d = addMonthsUTC(d, 1);
Mon Mar 28 2016 18:00:00 GMT-0600 (CST)
> d.toISOString()
"2016-03-29T00:00:00.000Z"

UTC dışı tarihler için güncelleme: (A.Hatchkins tarafından)

function addMonths (date, count) {
  if (date && count) {
    var m, d = (date = new Date(+date)).getDate()

    date.setMonth(date.getMonth() + count, 1)
    m = date.getMonth()
    date.setDate(d)
    if (date.getMonth() !== m) date.setDate(0)
  }
  return date
}

Ölçek:

> d = new Date(2016,0,31);
Sun Jan 31 2016 00:00:00 GMT-0600 (CST)
> d = addMonths(d, 1);
Mon Feb 29 2016 00:00:00 GMT-0600 (CST)
> d = addMonths(d, 1);
Tue Mar 29 2016 00:00:00 GMT-0600 (CST)
> d.toISOString()
"2016-03-29T06:00:00.000Z"

Burada tek makul cevap. Ancak UTC dışı tarihler için dikkatli olunmalıdır. Beklenmedik sonuçlar getirebilir (örn. 31 Ocak + 1 ay = 1 Mart UTC + 1 saat dilimi gece yarısı için)
Antony Hatchkins

1
Başka bir sorun, hem orijinal tarihi değiştirmesi hem de değiştirilen değeri döndürmesidir. Belki tarih prototipine eklemek ya da işlevdeki değiştirilen tarih için yerel bir değişken kullanmak mantıklıdır?
Antony Hatchkins

Teşekkürler Antony, yerel bir değişken mantıklı.
aMarCruz

1
"Dönüş tarihi" satırını kaldırmanızı veya yerel bir değişken eklemenizi öneririm.
Antony Hatchkins

Yerel değişken ve ayrılmış testler kullanılarak güncellendi.
aMarCruz

12

Bu cevapların hiçbirinin ayın değiştiği cari yılı hesaba katmayacağı düşünüldüğünde, aşağıda yaptığım ve bu konuyu ele alması gereken bir cevap bulabilirsiniz:

Yöntem:

Date.prototype.addMonths = function (m) {
    var d = new Date(this);
    var years = Math.floor(m / 12);
    var months = m - (years * 12);
    if (years) d.setFullYear(d.getFullYear() + years);
    if (months) d.setMonth(d.getMonth() + months);
    return d;
}

Kullanımı:

return new Date().addMonths(2);

Bu yanıt, ay içinde karşılık gelen bir tarih olmadığı için ay eklemeyi hesaba katmaz (örn. 31 Ocak + 1 ay => 2 veya 3 Mart). Ayrıca bir Tarihe 12 aydan fazla ekleme konusunda bir sorun olduğunu düşünüyor: yok. 13 ay eklerken 1 yıl 1 ay eklemeye gerek yoktur. Sadece 13 ay ekleyin.
RobG

9

Alındığı @bmpsini ve @Jazaret yanıtları ancak prototip uzanan değil: (düz işlevleri kullanarak ? Neden yerli nesneleri kötü uygulama uzanan edilir ):

function isLeapYear(year) { 
    return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); 
}

function getDaysInMonth(year, month) {
    return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
}

function addMonths(date, value) {
    var d = new Date(date),
        n = date.getDate();
    d.setDate(1);
    d.setMonth(d.getMonth() + value);
    d.setDate(Math.min(n, getDaysInMonth(d.getFullYear(), d.getMonth())));
    return d;
}

Kullanın:

var nextMonth = addMonths(new Date(), 1);

4

Yukarıdaki yanıtlardan, uç vakaları (bmpasini'nin datejs kütüphanesinden) işleyen tek sorunun bir sorunu var:

var date = new Date("03/31/2015");
var newDate = date.addMonths(1);
console.log(newDate);
// VM223:4 Thu Apr 30 2015 00:00:00 GMT+0200 (CEST)

tamam ama:

newDate.toISOString()
//"2015-04-29T22:00:00.000Z"

daha da kötüsü :

var date = new Date("01/01/2015");
var newDate = date.addMonths(3);
console.log(newDate);
//VM208:4 Wed Apr 01 2015 00:00:00 GMT+0200 (CEST)
newDate.toISOString()
//"2015-03-31T22:00:00.000Z"

Bu, ayarlanmadığı zamandan kaynaklanmaktadır, bu nedenle saat dilimi veya zaman kazandıran değişiklikler veya her neyse ...

İşte bu problemi olmayan ve aynı zamanda bence, sabit kodlanmış değerlere dayanmadığı için daha şık olan önerilen çözümüm.

/**
* @param isoDate {string} in ISO 8601 format e.g. 2015-12-31
* @param numberMonths {number} e.g. 1, 2, 3...
* @returns {string} in ISO 8601 format e.g. 2015-12-31
*/
function addMonths (isoDate, numberMonths) {
    var dateObject = new Date(isoDate),
        day = dateObject.getDate(); // returns day of the month number

    // avoid date calculation errors
    dateObject.setHours(20);

    // add months and set date to last day of the correct month
    dateObject.setMonth(dateObject.getMonth() + numberMonths + 1, 0);

    // set day number to min of either the original one or last day of month
    dateObject.setDate(Math.min(day, dateObject.getDate()));

    return dateObject.toISOString().split('T')[0];
};

Ünite aşağıdakilerle başarıyla test edildi:

function assertEqual(a,b) {
    return a === b;
}
console.log(
    assertEqual(addMonths('2015-01-01', 1), '2015-02-01'),
    assertEqual(addMonths('2015-01-01', 2), '2015-03-01'),
    assertEqual(addMonths('2015-01-01', 3), '2015-04-01'),
    assertEqual(addMonths('2015-01-01', 4), '2015-05-01'),
    assertEqual(addMonths('2015-01-15', 1), '2015-02-15'),
    assertEqual(addMonths('2015-01-31', 1), '2015-02-28'),
    assertEqual(addMonths('2016-01-31', 1), '2016-02-29'),
    assertEqual(addMonths('2015-01-01', 11), '2015-12-01'),
    assertEqual(addMonths('2015-01-01', 12), '2016-01-01'),
    assertEqual(addMonths('2015-01-01', 24), '2017-01-01'),
    assertEqual(addMonths('2015-02-28', 12), '2016-02-28'),
    assertEqual(addMonths('2015-03-01', 12), '2016-03-01'),
    assertEqual(addMonths('2016-02-29', 12), '2017-02-28')
);

3

Yanıtların çoğu vurgulandığı gibi, belirli bir tarihe belirli sayıda ay eklemek için setMonth () yöntemini getMonth () yöntemiyle birlikte kullanabiliriz .

Örnek: (cevabında @ChadD tarafından belirtildiği gibi.)

var x = 12; //or whatever offset 
var CurrentDate = new Date();
CurrentDate.setMonth(CurrentDate.getMonth() + x);

Ancak bu çözümü dikkatle kullanmalıyız çünkü uç durumlarda sorun yaşayacağız.

Uç vakaları ele almak için, aşağıdaki bağlantıda verilen cevap yardımcı olacaktır.

https://stackoverflow.com/a/13633692/3668866


3

Basit çözüm: 2678400000milisaniyede 31 gündür

var oneMonthFromNow = new Date((+new Date) + 2678400000);

Güncelleme:

Kendi fonksiyonumuzu oluşturmak için bu verileri kullanın:

  • 2678400000 - 31 gün
  • 2592000000 - 30 gün
  • 2505600000 - 29 gün
  • 2419200000 - 28 gün

8
Bazı ayların 31 günü yok
Alexandru Severin

Kabul edin, ancak işlevi kolayca ayarlayabilir veya yazabilirsiniz
dr.dimitru

2
İnsanların aradığı şeyin, aylar içinde günler içinde otomatik olarak dikkate aldığı çok işlev olduğunu tahmin ediyorum.
Alexander Bird

@AlexanderBird sonra moment.js gibi başkası tarafından oluşturulmuş kurşun geçirmez bir araç kullanın
dr.dimitru

2
Bu cevap, tüm günlerin 24 saat (veya 8.64e7 milisaniye) olmadığı gün ışığından tasarruf etmeyi içermez.
RobG

2
d = new Date();

alert(d.getMonth()+1);

Aylar 0 tabanlı bir dizine sahiptir, 5 (mayıs) olan (4) uyarmalıdır;


Bu yöntem, <0 ve 12'den büyük bir değer döndürebilir
Patrick

2

Sadece kabul edilen cevaba ve yorumlara eklemek için.

var x = 12; //or whatever offset
var CurrentDate = new Date();

//For the very rare cases like the end of a month
//eg. May 30th - 3 months will give you March instead of February
var date = CurrentDate.getDate();
CurrentDate.setDate(1);
CurrentDate.setMonth(CurrentDate.getMonth()+X);
CurrentDate.setDate(date);

2
Tarih (31) 'i 1 Şubat' olarak ayarladığınızda "3 Mar" alırsınız. Gerçekten istediğin bu mu?
Antony Hatchkins

2

Bana uygun olan bu alternatif çözümü yazdım. Bir sözleşmenin sonunu hesaplamak istediğinizde kullanışlıdır. Örneğin, başlangıç ​​= 2016-01-15, ay = 6, bitiş = 2016-7-14 (yani son gün - 1):

<script>
function daysInMonth(year, month)
{
    return new Date(year, month + 1, 0).getDate();
}

function addMonths(date, months)
{
    var target_month = date.getMonth() + months;
    var year = date.getFullYear() + parseInt(target_month / 12);
    var month = target_month % 12;
    var day = date.getDate();
    var last_day = daysInMonth(year, month);
    if (day > last_day)
    {
        day = last_day;
    }
    var new_date = new Date(year, month, day);
    return new_date;
}

var endDate = addMonths(startDate, months);
</script>

Örnekler:

addMonths(new Date("2016-01-01"), 1); // 2016-01-31
addMonths(new Date("2016-01-01"), 2); // 2016-02-29 (2016 is a leap year)
addMonths(new Date("2016-01-01"), 13); // 2017-01-31
addMonths(new Date("2016-01-01"), 14); // 2017-02-28

0

Aşağıda, form alanları aracılığıyla tarih girişine (üyelikssignup_tarihi) + eklenen aylara (üyelik ayları) dayalı olarak gelecekteki bir tarihin nasıl hesaplanacağına ilişkin bir örnek verilmektedir.

Membershipsmonths alanı varsayılan değeri 0

Tetikleyici bağlantısı (üyelik terimi alanına ekli bir değişim etkinliği olabilir):

<a href="#" onclick="calculateMshipExp()"; return false;">Calculate Expiry Date</a>

function calculateMshipExp() {

var calcval = null;

var start_date = document.getElementById("membershipssignup_date").value;
var term = document.getElementById("membershipsmonths").value;  // Is text value

var set_start = start_date.split('/');  

var day = set_start[0];  
var month = (set_start[1] - 1);  // January is 0 so August (8th month) is 7
var year = set_start[2];
var datetime = new Date(year, month, day);
var newmonth = (month + parseInt(term));  // Must convert term to integer
var newdate = datetime.setMonth(newmonth);

newdate = new Date(newdate);
//alert(newdate);

day = newdate.getDate();
month = newdate.getMonth() + 1;
year = newdate.getFullYear();

// This is British date format. See below for US.
calcval = (((day <= 9) ? "0" + day : day) + "/" + ((month <= 9) ? "0" + month : month) + "/" + year);

// mm/dd/yyyy
calcval = (((month <= 9) ? "0" + month : month) + "/" + ((day <= 9) ? "0" + day : day) + "/" + year);

// Displays the new date in a <span id="memexp">[Date]</span> // Note: Must contain a value to replace eg. [Date]
document.getElementById("memexp").firstChild.data = calcval;

// Stores the new date in a <input type="hidden" id="membershipsexpiry_date" value="" name="membershipsexpiry_date"> for submission to database table
document.getElementById("membershipsexpiry_date").value = calcval;
}

0

Sunulan karmaşık, çirkin cevapların çoğunun gösterdiği gibi, Tarihler ve Zamanlar herhangi bir dili kullanan programcılar için bir kabus olabilir. Benim yaklaşımım tarihleri ​​ve 'delta t' değerlerini Epoch Time'a (ms cinsinden) dönüştürmek, herhangi bir aritmetik yapmak, sonra tekrar "insan zamanına" dönüştürmektir.

// Given a number of days, return a Date object
//   that many days in the future. 
function getFutureDate( days ) {

    // Convert 'days' to milliseconds
    var millies = 1000 * 60 * 60 * 24 * days;

    // Get the current date/time
    var todaysDate = new Date();

    // Get 'todaysDate' as Epoch Time, then add 'days' number of mSecs to it
    var futureMillies = todaysDate.getTime() + millies;

    // Use the Epoch time of the targeted future date to create
    //   a new Date object, and then return it.
    return new Date( futureMillies );
}

// Use case: get a Date that's 60 days from now.
var twoMonthsOut = getFutureDate( 60 );

Bu, biraz farklı bir kullanım durumu için yazılmıştır, ancak ilgili görevlere kolayca uyarlayabilmeniz gerekir.

EDIT: Tam kaynak burada !


3
Yararsız cevap, çünkü farklı gün sayısıyla ayları işlemiyor, soru bu.
Benubird

1
@Benubird - çok kibarca sorduğunuz için, tam kaynağı yükledim. Bu yayındaki bağlantı.
Dan Ahlquist

Bu, tüm günlerin 24 saat (veya 8.64e7 milisaniye) olmadığı gün ışığından tasarruf etmeyi de içermez.
RobG

Hayır. DST bir yerelleştirme konusudur.
Dan Ahlquist

0

Tüm bunlar çok karmaşık görünüyor ve sanırım "bir ay" eklemenin tam olarak ne anlama geldiğine dair tartışmalara giriyor. 30 gün mü demek? 1'den 1'e anlamına mı geliyor? Son günden son güne mi?

İkincisi, 27 Şubat'a bir ay eklemek sizi 27 Mart'a götürür, ancak 28 Şubat'a bir ay eklemek sizi 31 Mart'a götürür (artık yıllar hariç, sizi 28 Mart'a götürür). Sonra 30 Mart'tan bir ay çıkarmak seni ... 27 Şubat? Kim bilir...

Basit bir çözüm arayanlar için sadece milisaniye ekleyin ve tamamlayın.

function getDatePlusDays(dt, days) {
  return new Date(dt.getTime() + (days * 86400000));
}

veya

Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};

"X ayları eklemenin" ayın X kadar arttığı anlamına geldiği anlaşılıyor. Cevabınızın kodlamak için daha az algoritmik adımlar içerdiği doğrudur, ancak maalesef doğruluğun basitliği bozduğunu hissediyorum. İnsanlar bir cevap aradıklarında, '31 Temmuz 2016'nın' 31 Ağustos 2016 'olmasını istiyorlar. Ve bu böyle olmazdı. Ayrıca, doğruluk maliyetiyle gerçekten basitlik istiyorsanız, neden sadık kalmıyorsunuz d.setMonth(d.getMonth()+1)? Yani bu en basit fikir bile değil.
Alexander Bird

Bu, tüm günlerin 24 saat (veya 8.64e7 milisaniye) olmadığı gün ışığından tasarruf etmeyi de içermez.
RobG

-1
addDateMonate : function( pDatum, pAnzahlMonate )
{
    if ( pDatum === undefined )
    {
        return undefined;
    }

    if ( pAnzahlMonate === undefined )
    {
        return pDatum;
    }

    var vv = new Date();

    var jahr = pDatum.getFullYear();
    var monat = pDatum.getMonth() + 1;
    var tag = pDatum.getDate();

    var add_monate_total = Math.abs( Number( pAnzahlMonate ) );

    var add_jahre = Number( Math.floor( add_monate_total / 12.0 ) );
    var add_monate_rest = Number( add_monate_total - ( add_jahre * 12.0 ) );

    if ( Number( pAnzahlMonate ) > 0 )
    {
        jahr += add_jahre;
        monat += add_monate_rest;

        if ( monat > 12 )
        {
            jahr += 1;
            monat -= 12;
        }
    }
    else if ( Number( pAnzahlMonate ) < 0 )
    {
        jahr -= add_jahre;
        monat -= add_monate_rest;

        if ( monat <= 0 )
        {
            jahr = jahr - 1;
            monat = 12 + monat;
        }
    }

    if ( ( Number( monat ) === 2 ) && ( Number( tag ) === 29 ) )
    {
        if ( ( ( Number( jahr ) % 400 ) === 0 ) || ( ( Number( jahr ) % 100 ) > 0 ) && ( ( Number( jahr ) % 4 ) === 0 ) )
        {
            tag = 29;
        }
        else
        {
            tag = 28;
        }
    }

    return new Date( jahr, monat - 1, tag );
}


testAddMonate : function( pDatum , pAnzahlMonate )
{
    var datum_js = fkDatum.getDateAusTTMMJJJJ( pDatum );
    var ergebnis = fkDatum.addDateMonate( datum_js, pAnzahlMonate );

    app.log( "addDateMonate( \"" + pDatum + "\", " + pAnzahlMonate + " ) = \"" + fkDatum.getStringAusDate( ergebnis ) + "\"" );
},


test1 : function()
{
    app.testAddMonate( "15.06.2010",    10 );
    app.testAddMonate( "15.06.2010",   -10 );
    app.testAddMonate( "15.06.2010",    37 );
    app.testAddMonate( "15.06.2010",   -37 );
    app.testAddMonate( "15.06.2010",  1234 );
    app.testAddMonate( "15.06.2010", -1234 );
    app.testAddMonate( "15.06.2010",  5620 );
    app.testAddMonate( "15.06.2010", -5120 );

}

1
Kodun çoğu İngilizce ve Deutsch değişkenlerinde ist zaman çok verwirrend buluyorum. ;)
Bernhard Hofmann

-1

Bazen yararlı BIRT parametreleri gibi bir operatör tarafından tarih oluşturma

1 ay önce aşağıdakileri yaptım:

new Date(new Date().setMonth(new Date().getMonth()-1));   

-3
var a=new Date();
a.setDate(a.getDate()+5);

Yukarıda belirtilen yöntem gibi, Datefonksiyona ay ekleyebilirsiniz .

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.