Fungsi JavaScript untuk menambahkan X bulan ke tanggal

209

Saya mencari cara termudah, terbersih untuk menambahkan X bulan ke tanggal JavaScript.

Saya lebih suka tidak menangani bergulir tahun ini atau harus menulis fungsi saya sendiri .

Apakah ada sesuatu yang dibangun di dalam yang dapat melakukan ini?

Chad
sumber
4
Coba tambahkan metode objek prototipe tanggal, seperti begitu --------------- Date.prototype.addMonth = fungsi (n) {return Date baru (this.setMonth (this.getMonth (this.getMonth ( ) + n)); };
Moises Hidalgo
1
@ kr37, jawaban Moises Hidalgo tidak akan berfungsi dengan benar jika bulan target tidak memiliki nomor hari ini. Jawaban bmpasini juga menangani ini
Alexandru Severin
Jawaban di sini tidak terlalu baik, jawaban yang lebih baik ada di Menambahkan bulan ke Tanggal dalam JavaScript .
RobG

Jawaban:

278

Fungsi berikut menambahkan bulan ke tanggal dalam JavaScript ( sumber ). Ini memperhitungkan roll-overs tahun akun dan panjang bulan yang bervariasi:

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());

Solusi di atas mencakup kasus tepi bergerak dari bulan dengan jumlah hari lebih besar dari bulan tujuan. misalnya.

  • Tambahkan dua belas bulan ke 29 Februari 2020 (harus 28 Februari 2021)
  • Tambahkan satu bulan ke 31 Agustus 2020 (harus 30 September 2020)

Jika hari dalam sebulan berubah saat melamar setMonth, maka kami tahu kami telah melimpah ke bulan berikutnya karena perbedaan panjang bulan. Dalam hal ini, kami gunakan setDate(0)untuk kembali ke hari terakhir bulan sebelumnya.

Catatan: versi jawaban ini menggantikan versi sebelumnya (di bawah) yang tidak dengan anggun menangani panjang bulan yang berbeda.

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);
Chad
sumber
130
Hati-hati - ini tidak berfungsi untuk kasus tepi, seperti menambah hari ke-31 di sebagian besar bulan. Sebagai contoh, 31 Oktober 2011 + 1 bulan menggunakan metode ini adalah 01 Desember 2011 menggunakan objek Tanggal standar Javascript.
tad
6
Tangkapan bagus, tad. Saya terkejut bahwa saya tidak melihat itu. Perhatikan bahwa T-SQL pilih DATEADD (bulan, 1, '2011-10-31') menghasilkan '2011-11-30', yang menurut saya masuk akal. Saya mendapat 10 suara dengan jawaban yang buruk. Keren. :-)
Chad
2
Tonton tahun kabisat - menambahkan 1 tahun hingga 29 Februari dalam satu tahun kabisat akan memberi Anda hasil yang tidak terduga tergantung pada bahasa apa yang Anda gunakan (sebagai jawaban umum). Inilah yang menurunkan platform cloud Microsoft Azure selama beberapa jam pada 2012
Ben Walding
15
Ada addMonthsfungsi di sini yang menghormati akhir bulan (misalnya 2012-01-31 + 1 month = 2012-02-29dan seterusnya).
RobG
3
cukup tambahkan untuk menetapkan tanggal 1 setiap bulan: today.setHours (0, 0, 0, 0); today.setDate (1);
Alexey Strakh
56

Saya menggunakan perpustakaan moment.js untuk manipulasi tanggal-waktu . Kode sampel untuk ditambahkan satu bulan:

var startDate = new Date(...);
var endDateMoment = moment(startDate); // moment(...) can also be used to parse dates in string format
endDateMoment.add(1, 'months');
anre
sumber
12
Untuk kewarasan Anda sendiri, gunakan moment.js.
Gaʀʀʏ
3
Sepenuhnya setuju dengan @garry: gunakan moment.js.
Michel
2
Saya setuju bahwa ini adalah cara terbersih, tetapi pertanyaannya adalah "Apakah ada sesuatu yang dibangun di dalam yang dapat melakukan ini?" Moment menambahkan lusinan Kb ke ukuran halaman
Evgeny
Ini bagus. Tidak ada plugin lain yang saya temukan di internet yang dapat melakukan jauh lebih baik dari ini.
Eraniichan
26

Fungsi ini menangani kasing tepi dan cepat:

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
}

uji:

> 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"

Pembaruan untuk tanggal non-UTC: (oleh A.Hatchkins)

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
}

uji:

> 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"
aMarCruz
sumber
Satu-satunya jawaban yang masuk akal di sini. Tetapi perawatan harus dilakukan untuk tanggal yang bukan UTC. Ini bisa membawa hasil yang tidak terduga (mis. 31 Jan + 1 bulan = 1 Mar untuk UTC + 1 zona waktu tengah malam)
Antony Hatchkins
1
Masalah lainnya adalah keduanya memodifikasi tanggal asli dan mengembalikan nilai yang dimodifikasi. Mungkin masuk akal untuk menambahkannya ke prototipe tanggal atau menggunakan variabel lokal untuk tanggal yang diubah dalam fungsi?
Antony Hatchkins
Terima kasih Antony, variabel lokal masuk akal.
aMarCruz
1
Saya sarankan untuk menghapus baris "tanggal kembali" atau menambahkan var lokal.
Antony Hatchkins
Diperbarui menggunakan variabel lokal dan tes terpisah.
aMarCruz
12

Mempertimbangkan tidak satu pun dari jawaban ini yang akan menjelaskan tahun berjalan ketika bulan tersebut berubah, Anda dapat menemukan jawaban yang saya buat di bawah ini yang seharusnya menanganinya:

Metode:

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;
}

Pemakaian:

return new Date().addMonths(2);
Gila kontrol
sumber
Jawaban ini tidak memperhitungkan penambahan bulan jika tidak ada tanggal yang sesuai di bulan yang dihasilkan (mis. 31 Jan + 1 bulan => 2 atau 3 Mar). Tampaknya juga berpikir ada masalah dengan menambahkan lebih dari 12 bulan ke Tanggal: tidak ada. Tidak perlu menambahkan, katakanlah, 13 bulan, untuk menambah 1 tahun dan 1 bulan. Tambahkan saja 13 bulan.
RobG
9

Diambil dari respons @bmpsini dan @Jazaret , tetapi tidak memperluas prototipe: menggunakan fungsi polos ( Mengapa memperluas objek asli adalah praktik yang buruk? ):

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;
}

Gunakan:

var nextMonth = addMonths(new Date(), 1);
Miquel
sumber
4

Dari jawaban di atas, satu-satunya yang menangani kasus tepi (bmpasini dari perpustakaan datejs) memiliki masalah:

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)

ok tapi

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

lebih buruk :

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"

Hal ini disebabkan oleh waktu yang tidak ditetapkan, sehingga kembali ke 00:00:00, yang kemudian dapat rusak ke hari sebelumnya karena perubahan zona waktu atau penghematan waktu atau apa pun ...

Inilah solusi yang saya usulkan, yang tidak memiliki masalah itu, dan saya pikir, lebih elegan karena tidak bergantung pada nilai-nilai hard-coded.

/**
* @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];
};

Unit berhasil diuji dengan:

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')
);
pansay
sumber
3

Karena sebagian besar jawaban disorot, kita bisa menggunakan metode setMonth () bersama dengan getMonth () metode untuk menambahkan jumlah bulan tertentu ke tanggal yang diberikan.

Contoh: (seperti yang disebutkan oleh @ChadD dalam jawabannya.)

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

Tapi kita harus hati-hati menggunakan solusi ini karena kita akan mendapat masalah dengan kasus tepi.

Untuk menangani kasus tepi, jawaban yang diberikan di tautan berikut bermanfaat.

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

Sampath Dilhan
sumber
3

Solusi sederhana: 2678400000adalah 31 hari dalam milidetik

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

Memperbarui:

Gunakan data ini untuk membangun fungsi kita sendiri:

  • 2678400000 - 31 hari
  • 2592000000 - 30 hari
  • 2505600000 - 29 hari
  • 2419200000 - 28 hari
dr.dimitru
sumber
8
Beberapa bulan tidak memiliki 31 hari
Alexandru Severin
Setuju, tetapi Anda dapat dengan mudah menyesuaikan, atau menulis fungsi
dr.dimitru
2
Saya menduga bahwa hal yang paling dicari orang adalah fungsi yang secara otomatis memperhitungkan hari dalam hitungan bulan.
Alexander Bird
@AlexanderBird kemudian gunakan alat antipeluru yang dibuat oleh orang lain, seperti moment.js
dr.dimitru
2
Jawaban ini tidak mengakomodasi penghematan siang hari, di mana tidak semua hari panjangnya 24 jam (atau 8,64e7 milidetik).
RobG
2
d = new Date();

alert(d.getMonth()+1);

Bulan memiliki indeks berbasis 0, harus waspada (4) yaitu 5 (mungkin);

Ben
sumber
Metode ini dapat mengembalikan nilai <0 dan lebih besar dari 12
Patrick
2

Hanya dengan menambahkan jawaban dan komentar yang diterima.

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);
iamjt
sumber
2
Ketika Anda mengatur tanggal (31) ke "1 Feb" Anda mendapatkan "3 Mar". Apakah itu yang benar-benar Anda inginkan?
Antony Hatchkins
2

Saya menulis solusi alternatif ini yang berfungsi baik bagi saya. Ini berguna ketika Anda ingin menghitung akhir kontrak. Misalnya, mulai = 2016-01-15, bulan = 6, akhir = 2016-7-14 (yaitu hari terakhir - 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>

Contoh:

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
David Ragazzi
sumber
0

Berikut ini adalah contoh cara menghitung tanggal di masa mendatang berdasarkan input tanggal (membershipssignup_date) + bulan ditambahkan (membershipsmonths) melalui bidang formulir.

Bidang keanggotaanhipsmonths memiliki nilai default 0

Trigger link (dapat berupa acara pertukaran yang terlampir pada bidang istilah keanggotaan):

<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;
}
John G
sumber
0

Seperti yang ditunjukkan oleh banyak jawaban rumit dan jelek yang disajikan, Tanggal dan Waktu dapat menjadi mimpi buruk bagi programmer yang menggunakan bahasa apa pun. Pendekatan saya adalah mengonversi tanggal dan nilai 'delta t' menjadi Epoch Time (dalam ms), melakukan aritmatika apa pun, lalu mengonversi kembali ke "waktu manusia".

// 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 );

Ini ditulis untuk use case yang sedikit berbeda, tetapi Anda harus dapat dengan mudah mengadaptasinya untuk tugas-tugas terkait.

EDIT: Sumber lengkap di sini !

Dan Ahlquist
sumber
3
Jawaban tidak berguna, karena tidak menangani bulan dengan jumlah hari yang berbeda, yang merupakan pertanyaan.
Benubird
1
@ Benubird - karena Anda bertanya dengan sopan, saya telah mengunggah sumber lengkapnya. Tautan dalam pos ini.
Dan Ahlquist
Ini juga tidak mengakomodasi penghematan siang hari, di mana tidak semua hari panjangnya 24 jam (atau 8,64e7 milidetik).
RobG
Tidak, tidak. DST adalah masalah lokalisasi.
Dan Ahlquist
0

Semua ini tampaknya terlalu rumit dan saya kira itu menjadi perdebatan tentang apa sebenarnya yang menambahkan "satu bulan" artinya. Apakah ini berarti 30 hari? Apakah ini berarti dari tanggal 1 hingga tanggal 1? Dari hari terakhir hingga hari terakhir?

Jika yang terakhir, kemudian menambahkan satu bulan ke 27 Februari membuat Anda ke 27 Maret, tetapi menambahkan satu bulan ke 28 Februari membuat Anda ke 31 Maret (kecuali dalam tahun kabisat, di mana itu membuat Anda ke 28 Maret). Kemudian kurangi satu bulan dari 30 Maret membuat Anda ... 27 Februari? Siapa tahu...

Bagi mereka yang mencari solusi sederhana, cukup tambahkan milidetik dan lakukan.

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

atau

Date.prototype.addDays = function(days) {
  this = new Date(this.getTime() + (days * 86400000));
};
jdforsythe
sumber
Saya merasa jelas bahwa "menambahkan X bulan" berarti bulan meningkat X. Memang benar bahwa jawaban Anda memiliki langkah-langkah algoritmik yang kurang untuk kode, tetapi sayangnya saya merasa bahwa akurasi mengalahkan kesederhanaan. Ketika orang mencari jawaban, mereka ingin '31 Juli 2016' menjadi '31 Agustus 2016'. Dan ini tidak akan melakukan itu. Selain itu, jika Anda benar-benar menginginkan kesederhanaan dengan biaya akurasi, mengapa tidak bertahan d.setMonth(d.getMonth()+1)? Jadi ini bahkan bukan ide yang paling sederhana.
Alexander Bird
Ini juga tidak mengakomodasi penghematan siang hari, di mana tidak semua hari panjangnya 24 jam (atau 8,64e7 milidetik).
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 );

}
ea234
sumber
1
Saya merasa sangat verwirrend ketika sebagian besar kode dalam bahasa Inggris dan variabel di Deutsch. ;)
Bernhard Hofmann
-1

Terkadang berguna membuat tanggal oleh satu operator seperti pada parameter BIRT

Saya mendapat 1 bulan kembali dengan:

new Date(new Date().setMonth(new Date().getMonth()-1));   
Andrei Koshelap
sumber
-3
var a=new Date();
a.setDate(a.getDate()+5);

Seperti metode yang dinyatakan di atas, Anda dapat menambahkan bulan Dateberfungsi.

Venkatesh
sumber