我尝试创建一个数组对象,其中包含一个特定月份的第一个和最后一个星期的日期。例如:
{
W1: [ '2021-01-01', '2021-01-07' ],
W2: [ '2021-01-08', '2021-01-14' ],
W3: [ '2021-01-15', '2021-01-21' ],
W4: [ '2021-01-22', '2021-01-28' ],
W5: [ '2021-01-29', '2021-01-31' ]
}
这段代码是否有更多的功能方法?
// const moment = require('moment'); // require
const getMaxDate = (year, month) => moment(`${year}-${month}`, "YYYY-MM").daysInMonth();
const getDate = (x, y, z) => moment().year(x).month(y).date(z).format("YYYY-MM-DD");
const dateOnMonth = (month, year) => {
const max = getMaxDate(year, month)
let obj = {}
let counter = 0
let last = 0
for (let i = 0; i <= max; i++) {
if (i != 0 && i % 7 == 0) {
counter++
const y = getDate(year, month - 1, i - 6)
const z = getDate(year, month - 1, i)
obj[`W${counter}`] = [y, z]
last = i + 1
} else if (counter == 4) {
const j = getDate(year, month - 1, last)
const k = getDate(year, month - 1, max)
obj[`W${counter+1}`] = [j, k]
}
}
return obj
}
console.log(dateOnMonth(1, 2021))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.2/moment.min.js"></script>
功能的、不可变的、可重用的
这是一个函数式方法,其中date
模块可以创建日期,访问构造日期的属性,并以不可变的方式操作日期。weeks
是一个更复杂的操作,它使用简单的日期操作-
构建
// date.js
const date = (y, m, d) => new Date(y, m - 1, d)
const day = (t) => t.getDate()
const month = (t) => t.getMonth() + 1
const year = (t) => t.getFullYear()
const addDays = (t, n) => date(year(t), month(t), day(t) + n)
const daysInMonth = (t) => day(date(year(t), month(t) + 1, 0))
const weeks = (t, days = daysInMonth(t)) =>
days <= 7
? [[t, addDays(t, days - 1)]]
: [[t, addDays(t, 6)], ...weeks(addDays(t, 7), days - 7)]
console.log(weeks(date(2022, 1, 1)))
[
[ "2022-01-01T00:00:00.000Z", "2022-01-07T00:00:00.000Z" ],
[ "2022-01-08T00:00:00.000Z", "2022-01-14T00:00:00.000Z" ],
[ "2022-01-15T00:00:00.000Z", "2022-01-21T00:00:00.000Z" ],
[ "2022-01-22T00:00:00.000Z", "2022-01-28T00:00:00.000Z" ],
[ "2022-01-29T00:00:00.000Z", "2022-01-31T00:00:00.000Z" ]
]
如果您想将输出格式化为字符串,则应该是它自己对底层数据类型进行操作的日期操作。在这里,您可以看到添加前导零或目标特定区域设置很容易-
// date.js (continued)
const toString = (t) => `${year(t)}-${month(t)}-${day(t)}`
console.log(weeks(date(2022, 3, 1)).map(w => [toString(w[0]), toString(w[1])]))
[
[ "2022-3-1", "2022-3-7" ],
[ "2022-3-8", "2022-3-14" ],
[ "2022-3-15", "2022-3-21" ],
[ "2022-3-22", "2022-3-28" ],
[ "2022-3-29", "2022-3-31" ]
]
模块思维
让你的思维适应基于模块的方法是很重要的。这是JavaScript向前发展的新方式,import
允许部分导入模块,并使编译器能够删除大量未使用的"死"代码。代码;这个过程叫做摇树。
与类类似,模块允许其作者有效地黑盒数据,并通过公开导出的模块方法提供与数据交互的认可方式。然而,与模块不同的是,类不能与它们的实例方法分离。如果您导入类以将其用于一个方法,则所有其他方法都将随之出现。
date
的底层表示不一定是Date
。可以使用数组[year, month, date]
、对象{year, month, date}
或任何其他表示形式。模块的用户不应该对底层数据类型做任何假设,只使用提供的模块方法。这允许模块作者自由地进行底层更改和优化,只要公共方法仍然正常运行。
// date.js
// date : (int, int, int) -> t
const date = (y, m, d) => new Date(y, m - 1, d)
// day : t -> int
const day = (t) => t.getDate()
// month : t -> int
const month = (t) => t.getMonth() + 1
// year : t -> int
const year = (t) => t.getFullYear()
// addDays : (t, int) -> t
const addDays = (t, n) => date(year(t), month(t), day(t) + n)
// daysInMonth : t -> int
const daysInMonth = (t) => day(date(year(t), month(t) + 1, 0))
// weeks : t -> array (t, t)
const weeks = (t, days = daysInMonth(t)) =>
days <= 7
? [[t, addDays(t, days - 1)]]
: [[t, addDays(t, 6)], ...weeks(addDays(t, 7), days - 7)]
// toString : t -> string
const format = (t) => `${year(t)}-${month(t)}-${day(t)}`
export { date, day, month, year, addDays, daysInMonth, weeks, toString }
// main.js
import { date, weeks } from "./date.js"
const feb22 = date(2022, 2, 1)
console.log(weeks(feb22))
// ...
试试这个,
你在for循环中浪费了太多的迭代,而我的代码在for循环中最多只需要5次迭代。
根据需要更改月份和年份。
var month = 4; // means May, this index starts from 0 as January
var year = 1986
function get_dates_by_week_of_a_month_year(month,year){
var d = new Date(year, month+1,0);
var num_days = d.getDate();
obj={};
week=1;
for(i=0;i<num_days;i+=7){
start = i+1;
end = (i+7) > num_days ? num_days : (i+7);
d1 = new Date(year, month, start).toLocaleDateString('en-CA');
d2 = new Date(year, month, end).toLocaleDateString('en-CA');
obj['W'+week++] = [d1,d2];
}
return obj;
}
console.log(get_dates_by_week_of_a_month_year(month,year));
对于将被多次使用的代码使用函数是有意义的,但是对于琐碎的函数则没有意义。我不会为此使用库,因为算法非常简单,纯JS几乎拥有所需的一切。
你也可以稍微简化算法,例如
/* Return weeks of month
* First week starts on 1st of month and goes for 7 days
* Last week has less than 7 days in any month of more than 28 days
*
* @param {string|number} year - calendar year, e.g. 2020
* @param {string|number} month - calendar month, 1 == Jan, etc.
* @returns {Object} weeks as {weekNumber:[startDate, endDate]}
* where dates are in YYYY-MM-DD format
*/
function getMonthWeeks(year, month) {
// Create a date from the supplied values
let date = new Date(year, month - 1);
// Other variables
let weeks = {};
let week = 0;
// Loop over weeks until exceed month end
while (date.getMonth() < month) {
// Get week number string
let weekNum = `W${++week}`;
// Initialise week array with start date
weeks[weekNum] = [toYMD(date)];
// Increment to end of week
date.setDate(date.getDate() + 6);
// If gone past end of month, go back to last day
if (date.getMonth() == month) {
date.setDate(0);
}
// Add last day to week array
weeks[weekNum].push(toYMD(date));
// Increment to start of next week
date.setDate(date.getDate() + 1);
}
return weeks;
}
// Helper to format dates
function toYMD(date) {
return date.toLocaleDateString('en-CA');
}
// April 2022
console.log(getMonthWeeks(2022,4));
// May 2022
console.log(getMonthWeeks(2022,5));
给定简单的算法,您知道前四周将是什么,因此实际上只需要将年和月插入前4周并计算第5周的结束(如果有的话)。
你可能会使用一个库来添加日期和格式化日期,但我真的不认为有这个必要。使用:
date.getMonth() == month
检查月份是否溢出的可能有点晦涩,但可以重写,这样逻辑就更清晰了,只需要多一行。函数不应该用在不必要的低效率的地方,例如
const getMaxDate = (year, month) => moment(`${year}-${month}`, "YYYY-MM").daysInMonth();
创建一个字符串,将其解析为日期,然后调用另一个函数以获取该月中恰好与该月最后一天的日期相吻合的天数。比较:
const getMaxDate = (year, month) => moment([year, month-1]).endOf('month').getDate();
和
const getMaxDate = (year, month) => new Date(year, month, 0).getDate();
均假定month为日历月号。
也:
const getDate = (x, y, z) => moment().year(x).month(y).date(z).format("YYYY-MM-DD");
是非常低效的(并且有点令人困惑,因为y是月),考虑:
const getDate = (year, month, day) => moment([year, month-1, day]).format("YYYY-MM-DD");
最后,鉴于前4周每个月的日期完全相同,您可以使用简单的算术和字符串格式的日期,只使用一个日期来计算最后一天,例如
// month is calendar month
function getWeeksInMonth(year, month) {
let z = n => ('0'+n).slice(-2);
let endDay = new Date(year, month, 0).getDate();
let weeks = {};
let week = 0;
let day = 1;
// Do first 4 weeks
while (day < endDay) {
weeks['W'+ ++week] = [`${year}-${z(month)}-${z(day)}`,
`${year}-${z(month)}-${z(day+6)}`];
day += 7;
}
// Add 5th week if there is one
if (endDay > 28) {
weeks['W5'] = [`${year}-${z(month)}-29`,
`${year}-${z(month)}-${endDay}`];
}
return weeks
}
console.log(getWeeksInMonth(2022,2)); // February
console.log(getWeeksInMonth(2022,4)); // April
我认为这比一大堆不必要的日期操作和格式化要有效得多。: -)