I am saving state in my React app via localStorage
const [items, setItem] = useState(() => {
let itemsString = window.localStorage.getItem('items');
if (itemsString) {
try {
return JSON.parse(itemsString);
} catch (e) {
console.error(e);
return [];
}
} else {
return [];
}
})
when I JSON.parse(itemsString) the date in the state has been converted to UTC (because strings/localstorage)
How do I JSON.parse() my state and reinitialize the date string to an object?
e.g. instead of returning 2019-07-19T00:28:03.058Z return Thu Jul 18 2019 20:28:03 GMT-0400 (Eastern Daylight Time) instead
Solution I came up with the help of Aaron's suggestion below.
Store old state. Map over state array, creating new empty object and storing each property in it, store date, convert date to string and pass that into new Date object to instantiate the value back to a date object on page refresh.
const [items, setItem] = useState(() => {
let itemsString = window.localStorage.getItem('items');
if (itemsString) {
try {
const oldState = JSON.parse(itemsString);
const newState = oldState.map(item => {
const date = item.date;
const container = {}
container.id = item.id
container.item = item.item
container.date = new Date(date.toString())
container.cost = item.cost
container.type = item.type
return container;
})
return newState;
} catch (e) {
console.error(e);
return [];
}
} else {
return [];
}
})
After you parse itemString just set the date key to a new Date object
const object = JSON.parse(itemString);
const newState = {...object, date: new Date(object.date)}
Related
I've in my react frontend multiple dates in an Array with this format 'MM/YYYY'
Now I want to get my history from MongoDB that's createdAt the time range of one month.
How can i pass my data in this axios get request?
My Frontend
let date = '11/2022'
const getHistory = async () => {
let monthYearStart = dayjs(date, 'MM/YYYY').format('YYYY.MM.01');
let monthYearEnd = dayjs(date, 'MM/YYYY').format('YYYY.MM.32');
const res = await axios.get('/api/monthlyhistory');
setPdfHistory(res.data);
};
getHistory().then(() => {});
My Backend
try {
const history = await History.find({
status: true,
createdAt: {
$gte: dayjs(new Date(monthYearStart, 'YYYY.MM.DD')),
$lt: dayjs(new Date(monthYearEnd, 'YYYY.MM.DD')),
},
});
res.json(history);
} catch (err) {
return res.status(500).json({ msg: err.message });
}
One option would be to pass the dates as query parameters. I would recommend using ISO 8601 format to remove ambiguity use the native Date constructor
Client-side
// Note these are local dates
const monthYearStart = new Date(2022, 10); // month is a zero-based index
const monthYearEnd = new Date(monthYearStart);
monthYearEnd.setMonth(monthYearEnd.getMonth() + 1);
monthYearEnd.setDate(monthYearEnd.getDate() - 1);
const res = await axios.get("/api/monthlyhistory", {
params: {
monthYearStart: monthYearStart.toISOString(),
monthYearEnd: monthYearEnd.toISOString(),
},
});
Server-side
const { monthYearStart, monthYearEnd } = req.query;
const history = await History.find({
status: true,
createdAt: {
$gte: new Date(monthYearStart),
$lt: new Date(monthYearEnd),
},
});
I have saved date as a String in exercise schema which saved as a array in user schema . and my schema as follows.
const exerciseSchema = new Schema({
description: String,
duration: Number,
date: String
});
const Exercise = mongoose.model("Exercise", exerciseSchema);
const userSchema = new Schema({
username: { type: String, unique: true },
log: [exerciseSchema]
});
const User = mongoose.model("User", userSchema);
I need to call their data, according to queries i use for sort by dates. and i have done it as below.
app.get("/api/users/:_id/logs", (request, response) => {
let _id = request.params._id;
let query = request.query;
User.findById(_id, (error, result) => {
if(!error){
let responseObject = result
if(query.from || query.to){
let fromDate = new Date(0)
let toDate = new Date()
if(query.from){
fromDate = new Date(query.from)
}
if(request.query.to){
toDate = new Date(query.to)
}
fromDate = fromDate.getTime()
toDate = toDate.getTime()
responseObject.log = responseObject.log.filter((session) => {
let sessionDate = new Date(session.date).getTime()
return sessionDate >= fromDate && sessionDate <= toDate
})
}
if(query.limit){
responseObject.log = responseObject.log.slice(0, query.limit)
}
responseObject = responseObject.toJSON()
responseObject['count'] = result.log.length
response.json(responseObject)
}
})
});
only thing i need to follow is which I can't get the date in locale format. the date is saving as ISO format(2015-12-15). I need to call it in locale format. but in here i have no access to the objects of log array. I have no idea why.??
Don't store your dates as strings. Store your dates as Mongo dates (ISODate("2020-12-24T05:00:00.000Z")). So you can query Mongo by date directly. Transform your dates afterwards in whatever format you want.
Now Mongoose is weird. By default, it returns an array of Mongoose objects, which are immutable and hard to work with. If you need only pure JSON data, add the option { lean : true }, or .lean() if you work with the chainable syntax (like below).
app.get("/api/users/:_id/logs", async (request, response) => {
let _id = request.params._id;
let query = request.query;
let user;
try {
user = await User
.findById(_id)
.populate("log")
.lean() // Returns JSON, not immutable Mongoose objects
.exec(); // Returns a Promise so you can 'await' it
} catch (error) {
response.json(error); // catch your errors
return;
}
if (query.from || query.to) {
let fromDate = new Date(0)
let toDate = new Date()
if (query.from) {
fromDate = new Date(query.from)
}
if (request.query.to) {
toDate = new Date(query.to)
}
const fromDateN = fromDate.getTime()
const toDateN = toDate.getTime()
user.log = user.log.filter((session) => {
let sessionDate = new Date(session.date).getTime();
return sessionDate >= fromDateN && sessionDate <= toDateN;
})
}
if (query.limit) {
user.log = user.log.slice(0, query.limit)
}
// user = user.toJSON() --> It's already JSON now.
user['count'] = user.log.length
response.json(user)
});
I have an app that allows users to schedule tasks. Users can set a schedule (schedule, an rrule string) as well as a timezone (scheduleTimeZone, a string e.g. Asia/Dubai).
I am trying to write a function (getNextRunAt) that gets the next occurrence of the task at a UTC date and store this in my Postgres DB as a timestamptz field.
I'm struggling to account for DST, lots of the results are an hour or even a day off.
Here's the function (in TypeScript):
import RRule from 'rrule';
import moment from 'moment-timezone';
const getNextRunAt = ({
schedule,
scheduleTimeZone,
}: {
schedule?: string | null;
scheduleTimeZone?: string | null;
}): Date | undefined => {
if (!schedule) {
return undefined;
}
const options = RRule.parseString(schedule);
if (scheduleTimeZone) {
options.tzid = scheduleTimeZone;
}
const dtstart = moment.utc().toDate();
const rule = new RRule({ ...options, dtstart, count: 1 });
const dates = rule.all();
let date = dates[0];
if (scheduleTimeZone && moment(date).tz(scheduleTimeZone).isDST()) {
date = moment(date).subtract(1, 'hour').toDate();
}
return date;
};
export default getNextRunAt;
It works for some dates/times/timezones:
Date.now = jest.fn(() => new Date('2021-03-02 10:24:27.000000Z').getTime());
const nextDate = getNextRunAt({
schedule: 'RRULE:INTERVAL=1;BYDAY=MO,TU,WE,TH,SA,FR,SU;BYMINUTE=0;BYHOUR=9;BYSECOND=0;FREQ=DAILY',
scheduleTimeZone: 'America/Los_Angeles',
});
expect(nextDate).toEqual(new Date('2021-03-02 17:00:00.000000Z'));
// WORKS
But not for others:
Date.now = jest.fn(() => new Date('2021-03-02 10:24:27.000000Z').getTime());
const nextDate = getNextRunAt({
schedule: 'RRULE:INTERVAL=1;BYDAY=MO,TU,WE,TH,SA,FR,SU;BYMINUTE=0;BYHOUR=9;BYSECOND=0;FREQ=DAILY',
scheduleTimeZone: 'America/Los_Angeles',
});
expect(nextDate).toEqual(new Date('2021-03-02 17:00:00.000000Z'));
// DOES NOT WORK
Expected: 2021-03-02T17:00:00.000Z
Received: 2021-03-03T17:00:00.000Z
I think that the last case is wrong with a day. But for others solution might look like:
const getNextRunAt = ({ schedule, scheduleTimeZone }: { schedule?: string | null; scheduleTimeZone?: string | null }): Date | undefined => {
if (!schedule) {
return undefined;
}
const options = RRule.parseString(schedule);
const dtstart = moment();
const rule = new RRule({
...options,
dtstart: dtstart.toDate(),
count: 1,
});
const dates = rule.all();
const date = dates[0];
let mDate = moment.tz(date, scheduleTimeZone);
const offset = Math.abs(mDate.utcOffset()) > 16 ? mDate.utcOffset() / 60 : mDate.utcOffset();
if (!mDate.isDST()) {
mDate = mDate.add(1, 'hours');
}
mDate.add(-offset, 'hour');
return mDate.toDate();};
export const fetchDailyData = async () => {
try {
const { data } = await axios.get(`${url}/daily`);
let today = new Date('2020-01-27');
let referanceDay = new Date(dailyData.reportDate)
const modifiedData = data.map((dailyData) => ({
if(referanceDay => today){
confirmed: dailyData.confirmed.total,
deaths: dailyData.deaths.total,
date: dailyData.reportDate
};
}))
// return modifiedData
} catch (error) {
}
}
I am trying to compare days and return proper ones. But it's not worked. I think I got a mistake object and if section. Could you please look my problem. Thanks..
Daily data is argument to anon. Function but being referenced out in which case it would be undefined.
It looks like your >= comparison in your if statement is backwards. Also you are referencing dailyData before it is declared inside mapTry this:
export const fetchDailyData = async () => {
try {
const { data } = await axios.get(`${url}/daily`);
let today = new Date('2020-01-27');
const modifiedData = data.map((dailyData) => ({
let referanceDay = new Date(dailyData.reportDate)
if(referanceDay >= today){
confirmed: dailyData.confirmed.total,
deaths: dailyData.deaths.total,
date: dailyData.reportDate
};
}))
// return modifiedData
} catch (error) {
}
}
I think you should use a library to compare the dates. I use Moment.js for all my datetime operations.
enter link description here
example from docs :
moment('2010-10-20').isAfter('2010-01-01', 'year'); // false
I have this MapStateToProps:
const mapStateToProps = (state) => {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
const ESFReport = getESFReport(state);
const resultStatusReport = getResultStatusReport(state);
return {
companInfo: getCompanyInfo(state),
companyId: getCompanyId(state),
...ESFReport,
...resultStatusReport,
year,
month,
};
};
And both ...ESFReport, and ...resultStatusReport, have the same property: report but I need somehow to change the name because in the same component I use const { report } = this.props two times but for different props.
How can I do this? (it used to work when I have only ...ESFReport, but when I added ...resultStatusReport, it broke).
Thanks in advance.
If you don't need anything other than report from the ESFReport and resultStatusReport objects you could do:
const mapStateToProps = (state) => {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
const { report: ESFReport } = getESFReport(state);
const { report: resultStatusReport } = getResultStatusReport(state);
return {
companInfo: getCompanyInfo(state),
companyId: getCompanyId(state),
ESFReport,
resultStatusReport,
year,
month,
};
};
It just renames the report property on each to ESFReport and resultStatusReport. You would then have this.props.ESFReport which would actually be ESFReport.report and this.props.resultStatusReport which would be resultStatusReport.report.