Get ISO date string based on month name - javascript

I have to create two ISO date strings in an Array based on the long month name.
Input: 'August'
Output: ['2020-08-01T00:00:00', '2020-08-31T00:00:00']
I am thinking about a switch case to check for each month but I am not sure how to create the ISO string with this information. I could match August with 08 and replace a pre-defined ISO string and replace it based on the input but this doesn't sound very clever.

You can get the month names from toLocaleString for the locale you are in or hardcode an array.
Then calculate the first of this month and the 0th of NEXT month
I had to normalise the time to 15:00 to handle timezones.
const yyyy = new Date().getFullYear();
const months = Array.from(Array(12).keys())
.map(month => new Date(yyyy, month, 5, 15, 0, 0, 0)
.toLocaleString('default', { month: 'long'}) );
const zeroTime = str => `${str.split("T")[0]}T00:00:00`;
const getDates = month => {
const monthNum = months.indexOf(month);
const start = new Date(yyyy, monthNum, 1, 15, 0, 0, 0),
end = new Date(yyyy, monthNum+1, 0, 15, 0, 0, 0)
return [zeroTime(start.toISOString()), zeroTime(end.toISOString())];
};
console.log(getDates("August"))

There are many ways to go about this, the only real issue is how to generate the list of month names. The following uses mplungian's approach of generating them in the browser default language, though I'm not sure that's a good idea.
The other part of the problem is to generate timestamps for the start and end of the month. That's pretty simple given a year and month number. You can use UTC values and toISOString, then trim the trailing Z to get local timestamps.
The following should be efficient as it only generates one date and array to get the month names, then one more of each on each call of getMonthDates. It also uses for loops instead of creating arrays and using array methods for iteration.
It also provides separate functions for getting the month number from the name and dates from month number and year. They use ECMAScript month number (0 = Jan, etc.) but could easily be converted to use calendar month number (1 = Jan, etc.).
// Given year and ECMAScript month number, return an array of ISO 8601
// formatted local timestamps for first and last days of the month
let getMonthDates = (monthNum = 0, year = new Date().getFullYear()) => {
let date = new Date(Date.UTC(year, monthNum));
let start = date.toISOString().substring(0, 19);
date.setUTCMonth(monthNum + 1, 0);
return [start, date.toISOString().substring(0, 19)];
}
// Given a month name, return it's ECMAScript month number
// Uses host default language for month name
let getMonthDatesFromName = (() => {
let date = new Date();
let monthNames = (() => {
date.setMonth(0, 1);
for (var arr=[], i=0; i<12; i++) {
arr.push(date.toLocaleString('default',{month:'long'}));
date.setMonth(i+1)
}
return arr;
})();
return (monthName, year) => {
let monthNum = monthNames.indexOf(monthName);
// If month name not found, return undefined
return monthNum < 0? void 0 : getMonthDates(monthNum, year);
}
})();
/** getMonthDatesFromName examples
*/
// Undefined if month name not valid
console.log('foo: ' + getMonthDatesFromName('foo'));
// Current month name
let monthName = new Date().toLocaleString('default',{month: 'long'});
console.log(monthName + ': ' + getMonthDatesFromName(monthName).toString());
// February, 2024
console.log('February, 2024: ' + getMonthDatesFromName('February', 2024).toString());
/** getMonthDates examples
*/
// January of current year by default
console.log('Default: ' + getMonthDates().toString());
// Month of current year by default, e.g. for April
console.log('April: ' + getMonthDates(3).toString());
// Month and year - February 2024
console.log('1, 2024: ' + getMonthDates(1, 2024).toString());

Related

How do I get the days between the today's day and the last day of the month using Moment.js?

Here's the code that I have right now:
const moment = require('moment')
const m = moment
const currDay = m().format('D')
const dayOfWeek = m().format('dddd')
const daysInMonth = m().daysInMonth()
const startOfMonth = moment().startOf('month').format('YYYY-MM-DD hh:mm');
const endOfMonth = moment().endOf('month').format('YYYY-MM-DD hh:mm');
I need to create a calendar row where the first item would be the todays date, and the rest of the calendar items would be the whatever amount of days are left depending on the current month so I could render each day in between in my HTML with Vue.
Example: Wed 8, Thu 9, Fri 10 ... Fri 31.
I think the OP is tripped up on the common mistake of formatting prematurely. format is good to see an intermediate result, but doing so produces a string that's no good for additional calculation.
Try to handle date objects only. Convert to strings only when you must: (a) presenting to a human reader, or (b) serializing for storage or transmission.
Working without formatting...
const daysRemainingThisMonth = moment().endOf('month').diff(moment(), 'days');
console.log(`There are ${daysRemainingThisMonth} days remaining this month`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
Just as a POJS equivalent, if you have a function to return the last day of the month, you can use that and just get the difference between the two dates, e.g.
function getMonthEnd(date = new Date()) {
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
}
function getMonthDaysLeft(date = new Date()) {
return getMonthEnd(date).getDate() - date.getDate();
}
let d = new Date();
console.log(`There are ${getMonthDaysLeft(d)} days left in ${d.toLocaleString('en',{month:'long'})}.`);
To get a list/array of the days remaining, just loop over a date, adding 1 day at a time, and write the dates in the required format into the list:
function getMonthDaysLeftAsList(date = new Date()) {
let d = new Date(+date);
// Formatter
let f = new Intl.DateTimeFormat('en',{
day: 'numeric',
month: 'short'
});
let m = d.getMonth();
let dayList = [];
while (d.getMonth() == m) {
dayList.push(f.format(d));
d.setDate(d.getDate() + 1);
}
return dayList;
}
console.log(getMonthDaysLeftAsList());

Converting date JavaScript

I'm currently populating a table with data from a soap web service, the date comes as a string (example 44250). I created a function to format it into a yyyy/mm/dd format.
Outside the loop I have this function:
Date.prototype.addDays = function (days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
};
Inside the loop I have:
else if (detailsItem == details[i].children[1].innerHTML) {
const dbDays = days[i].innerHTML;
const daysInt = parseInt(dbDays, 0);
const newDate = firstDate.addDays(daysInt);
// Format the date to a readable value
const partsDate = {
date: newDate.getDate(),
month: newDate.getMonth() + 1,
year: newDate.getYear() + 1900,
};
finalDate = `${partsDate.date}/${partsDate.month}/${partsDate.year}`;
const td = document.createElement("td");
td.textContent = finalDate;
tr.appendChild(td);
}
the else if is just checking when to add the date to the table while populating it.
I now need to send a request to the service using the date again but in the previous format, but the date has to be in the same row as the button click, the service only accepts the string format of the date, I'm currently stuck and unsure on how to format it back.
This is the button click function which has to then format the date back to a format such as 44250.
btn.onclick = function () {
// Loops through the table to find the slot and date when clicking the button on the same row
var tableRow = document.getElementById("booking-table"),
rIndex;
for (var i = 0; i < tableRow.rows.length; i++) {
tableRow.rows[i].onclick = function () {
rIndex = this.rowIndex;
bookingDay = this.cells[1].innerHTML;
bookingSlot = this.cells[2].innerHTML;
console.log(bookingSlot, bookingDay);
};
}
Any help on how to accomplish this would be appreciated.
The value "44250" looks like the number of days since 31 Dec 1899 (the epoch), which means a value of "1" converts to 1 Jan 1900. If that's correct, you can create a Date from it using:
let dbDays = '44250';
let date = new Date(1900, 0, dbDays); // 24 Feb 2021
In this algorithm, 1 is 1 Jan 1900 and 0 is 31 Dec 1899.
You can convert it back to an epoch offset using the reverse algorithm:
let dbDays = Math.round((date.getTime() - new Date(1899, 11, 31) / 8.64e7);
Which gets the difference in ms since the date and the epoch, then divides by ms in one day and rounds it to account for possible daylight saving effects where days aren't exactly 24 hours long. This method only works for whole days, it doesn't work for partial days.
The algorithm might be out by a day if 1 Jan 1900 should be 0 rather than 1, just adjust the base dates used in the functions.
Simple functions to go from dbDate to Date instance and back are:
// Convert epoch days to Date
function toDate(dbDays) {
return new Date(1900, 0, +dbDays);
}
// Convert Date to epoch days
function toDBDays(date) {
return Math.round((date - new Date(1899,11,31)) / 8.64e7);
}
// Format date as dd/mm/yyyy
function formatDate(d) {
let z = n => ('0'+n).slice(-2);
return z(d.getDate()) + '/' + z(d.getMonth()+1) + '/' + d.getFullYear();
}
// Parse date in d/m/y format, any non-digit separator
function parseDate(s) {
let [d,m,y] = s.split(/\D/);
return new Date(y, m - 1, d)
}
// Example
let dbDays = '44250';
let d1 = toDate(dbDays); // 24 Feb 2021
let ts = formatDate(d1); // 24/02/2021
let d2 = parseDate(ts); // date object
console.log(dbDays + ' to Date: ' + ts);
console.log(ts + ' to dbDays: ' + toDBDays(d2));
PS Given the epoch won't change, instead of creating a date for 31 Dec 1899 and getting its time value, the constant -2209111200000 can be used.
Notes on your code:
const daysInt = parseInt(dbDays, 0);
The second argument to parseInt is a radix or base to use for conversion to number. The value 0 is replaced with 10 (the default radix), so the above is equivalent to:
const daysInt = parseInt(dbDays, 10);
Then there is:
const partsDate = {
date: newDate.getDate(),
month: newDate.getMonth() + 1,
year: newDate.getYear() + 1900,
};
The getYear method returns a 2 digit year, it's not recommended and is supported mostly for historic reasons, use getFullYear instead.
Using an object for temporary storage is not really optimal, just use variables:
let date = newDate.getDate(),
month = newDate.getMonth() + 1,
year = newDate.getFullYear();
Note that this doesn't pad single digit days or months with leading zeros so will produce timestamps like 1/1/2021 instead of 01/01/2021.
You can use momentjs to convert the date easier: https://momentjs.com/ ,
about the use of the date after you pass that value to the html, can you store that value on some global variable,sesion or localstorage? and then use it later? to call again the soap service? i'm asumming your working on a web page, cause your using html :)

Get week number of the month from date (weeks starting on Mondays)

I have to find week number of the month from given date using JavaScript. Week start is Monday.
I have tried the code below but not getting accurate result.
function getWeekNumber(date) {
var monthStartDate = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
monthStartDate = new Date(monthStartDate);
var day = monthStartDate.getDay();
date = new Date(date);
var date = date.getDate();
let weekNumber = Math.ceil((date + (day)) / 7);
return (weekNumber == 0) ? 1 : weekNumber;
}
var week = getWeekNumber('2020-04-04');
console.log(week);
Try this one
function getWeek(date) {
let monthStart = new Date(date);
monthStart.setDate(0);
let offset = (monthStart.getDay() + 1) % 7 - 1; // -1 is for a week starting on Monday
return Math.ceil((date.getDate() + offset) / 7);
}
getWeek(new Date(2019, 2, 14))
You could find the week number of the month for weeks starting on Monday (in line with the ISO week date system) by rolling the input date back to the previous Monday and then dividing the Monday date by 7 and rounding up to determine which week of the month the date falls in.
This approach will properly handle dates at the beginning of a month which actually fall in the last week of the previous month. For instance, 2020-04-04 is a Saturday in the week starting on 2020-03-30 (Monday), so it should return week 5 since it is part of the 5th week of March (and not part of the 1st week of April which starts on 2020-04-06, the first Monday in April).
For example (the split bit at the beginning is just to parse the date string rather than relying on new Date() to parse the string since that is not recommended due to browser inconsistencies):
const monthWeek = (s) => {
const [y, m, d] = s.split('-'); // parse date string
const date = new Date(y, m - 1, d); // create date object
date.setDate(d - ((date.getDay() + 6) % 7)); // adjust date to previous Monday
return Math.ceil(date.getDate() / 7); // return week number of the month
};
console.log(monthWeek('2020-04-04'));
// 5
console.log(monthWeek('2020-04-07'));
// 1

Unable to pass string variable to JS date object

I am trying to pass string variable to JS date object and create date in future.
Its easy to do in this way:
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth();
var day = d.getDate();
var my_date = new Date(year + 1, month +1, day +1);
But how I can pass it with string variable, so that I can achieve something like this:
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth();
var day = d.getDate();
var my_variable = 'year + 1, month +1, day +1';
var my_date = new Date(my_variable);
In this case it returns Invalid date
Note
You need to be a little careful with date arithmetic since it's not symmetric and may give odd results, e.g. 29 Feb 2016 + 1 year gives 1 March 2017, and 31 July minus 1 month is 1 July since there is no 31 June, it rolls over to July, and so on.
Given your original requirement, you might consider a parser for your particular format, e.g.
function myParse(s, date) {
date = date || new Date();
// Default values object
var values = {
year:{sign:1, value:0}, month:{sign:1, value:0}, day:{sign:1, value:0}
};
// Tokenise string
var part = s.toLowerCase().match(/[a-z]+|\d+|[+-]/ig);
// Process the tokens
if (part) {
for (var i=0, iLen=part.length; i<iLen; i++) {
if (part[i] in values) {
values[part[i]].sign = part[i+1] == '+'? 1 : -1;
values[part[i]].value = +part[i+2];
i += 2;
}
}
}
// Apply to date
date.setFullYear(
date.getFullYear() + values.year.sign * values.year.value,
date.getMonth() + values.month.sign * values.month.value,
date.getDate() + values.day.sign * values.day.value
);
return date;
}
// Examples
var options = {day:'2-digit',month:'short',year:'numeric'};
console.log('Today is ' + (new Date().toLocaleString('en-gb', options)));
['year+1,month+1,day+1', // one of each
'year+2,day+2', // missing parameters
'day + 15, month +2', // any order, random whitespace
'month - 3' // subtraction
].forEach(function(s) {
console.log(s + '\n' + myParse(s).toLocaleString('en-gb', options));
});
It's fairly tolerant but you should validate the input. It could be extended to handle time too, and also the end of month issues noted above.
Unfortunately javascript (and all the languages that I know off hand) do not work like that.
You could use an eval statement instead, which allows you to create the entire instruction as a string and run that string...
var my_variable = 'year + 1, month +1, day +1';
eval('var my_date = new Date(' + my_variable + ');');
HOWEVER using of eval is dangerous, and could open you up to unexpected attacks. Only use it if absolutely necessary, and if you're sure that the string being used is "safe"
You are inserting Javascript code as a string, which makes no sense to the Date class.
You can have your variables stored in an array to pass them elegantly to Date.
const dates = [year + 1, month +1, day +1]
const my_date = new Date(...dates);
I've read a follow up comment which indicates you'll only be getting strings from wherever you are retrieving the data from. Firstly if the string is in the format of 'year + 1, month + 1, day + 1', it'll never make sense to the Date object. If the string is just 3 numbers separated by a comma, you could split the string at the commas and pass them to the Date object in the same fashion.
const dates = myString.split(',');
const my_date = new Date(...dates);

Why can't I display JUST the date (date, month, and year without time)?

I have some code, that is doing pretty much all i need it to do. Its calculating 3 days in the future, excluding dates, and then displaying my "estimated dispatch date"
The date, however displays in full date and time, instead of just date.
Day Month Date Year 12:02:57 GMT+0100 (British Summer Time)
Can anyone help with the code below, so that it excludes local time and only displays the future date, excluding weekend, DD/MM/YYYY or, in the below format;
Monday 20th June
Thanks in advance!
function addDates(startDate,noOfDaysToAdd){
var count = 0;
while(count < noOfDaysToAdd){
endDate = new Date(startDate.setDate(startDate.getDate() + 1));
if(endDate.getDay() != 0 && endDate.getDay() != 6){
//Date.getDay() gives weekday starting from 0(Sunday) to 6(Saturday)
count++;
}
}
return startDate;
}
var today = new Date();
var daysToAdd = 3;
document.write ('Estimated Dispatch Date: ' + addDates(today,daysToAdd));
You can use the toDateString method to display just the date portion of your Date object, but you will need to use a few other methods for full control over the format of your date string...
You can display just the date, month and year parts of your local date and time with a few extra lines of code using the getDate, getMonth, and getFullYear methods to help with the formatting. You could try passing specific formatting parameters to toLocaleString, but this may display different results in different browsers. For example, the code below outputs a date in the format dd/mm/yyyy in Chrome but that output is not guaranteed across browsers.
new Date().toLocaleString('en-GB', {year: 'numeric', month: 'numeric', day: 'numeric'})
Not sure I am following how you want to handle weekend dates, so the below handles the date formatting that you want in the formatDate function separately from the addDays function where it just handles weekend dates by rolling the date forward to a Monday if the initially calculated date lands on a Saturday or Sunday.
// format input date to dd/mm/yyyy
const formatDate = (date) => {
const d = date.getDate(); // day of the month
const m = date.getMonth(); // month index from 0 (Jan) to 11 (Dec)
const yyyy = date.getFullYear(); // 4 digit year
const dd = (d < 10 ? '0' : '') + d; // format date to 2 digit
const mm = (m + 1 < 10 ? '0' : '') + (m + 1); // convert index to month and format 2 digit
return `${dd}/${mm}/${yyyy}`;
};
// add input days to today and adjust for weekend output
const addDays = (today, days) => {
const now = today.getTime() // now in UTC milliseconds
const ms = 24 * 60 * 60000; // milliseconds in one day
const date = new Date((days * ms) + now); // today plus input days
const day = date.getDay(); // weekday index from 0 (Sun) to 6 (Sat)
// adjust weekend results to next weekday
if (day === 0 || day === 6) {
let adj = day === 0 ? 1 : 2;
return new Date(((days + adj) * ms) + now);
}
return date;
};
document.write('Estimated Dispatch Date: ' + formatDate(addDays(new Date(), 3)));

Categories