Separating Date strings into JS Object - javascript

I'm building a calendar that takes a JS object input from an RSS feed. This RSS feed is generated automatically from a CMS used internally in my company. I cannot change anything on the CMS side nor the returned RSS feed. All I have access to is the object that is built from that feed.
The RSS feed joins the start date, end date, time, and title all in one string. I need to separate them into keys in my object so my calendar can display them.
The problem I'm running into is the RSS feed formats the string differently depending on how the event is setup in the CMS. For example:
"7/15/2013 8:00 PM - 9:00 PM Blah" //Date, Time, Title
"7/12/2013 Blue" //Date for all day event, Title
"7/6/2013 8:00 AM - 7/23/2013 9:00 AM Banana" //Long event - Start Date, Start Time, End Date, End Time, Title
As you can see, how different these are I'm having a hard time deciding how I should go about parsing these into my object. The object should look like this:
{
title: 'Banana',
start: new Date(2013, 7, 24, 10, 30),
end: new Date(2013, 7, 24, 11, 30),
allDay: false
}
My question comes down to this: What would be a the best way to approach this? Use regex, try to parse it manually with things like .indexOf("/"), build test cases for each one, or some other suggestion.
PS: A jQuery example is an acceptable answer as well.

Are you able to use a library like Datejs?
You might want to start splitting on ' - ' (with spaces). If you have one part, you know it's an all day event with a title. If it's two parts, you know it's a start/end event and there's a title in the second piece. After you parse out the title, you can use Datejs to create Date objects:
Date.parse('7/15/2013 8:00 PM')
From there you should have enough to build your JSON object.

Based on Jon's answer, this is what I ended up with:
parseEntries: function() {
//Rename to fit plugin requirements
for (var i = 0; i < Calendar.entries.length; i++) {
var entry = Calendar.entries[i];
//Rename
entry["url"] = entry["link"];
delete entry["link"];
var position = entry.title.indexOf(' - ');
if (position === -1) {
//All day event
entry.allDay = true;
var space = entry.title.indexOf(" "),
title = entry.title.substring(space + 1),
firstHalf = entry.title.slice(0, space); //Start date, no time because it's all day event
} else {
var firstHalf = entry.title.slice(0, position), //Start date/time
secondHalf = entry.title.substring(position + 3);
if (secondHalf.indexOf("AM") !== -1) {
var title = secondHalf.substring(secondHalf.indexOf("AM") + 3); //Title if has AM
} else {
var title = secondHalf.substring(secondHalf.indexOf("PM") + 3); //Title if has PM
}
secondHalf = secondHalf.slice(0, -(title.length + 1)); //End date/time
}
entry["start"] = firstHalf;
entry["end"] = secondHalf;
entry.title = title;
};

Related

Extract specific digits from a .txt file

I have been asked to count the number of tweets per hour by day (0 - 23) in a huge text file of random tweets. The date is not interesting, only the tweet per hour. I want to return them in a new array of objects. Each object should have properties hour and count like this:
{hour: x, count: y},
I've made a function where I'm declaring an empty array, in which I will put my data:
function(tweets) {
let result = [];
and I think I need to push them like this:
result.push({hour: x, count: y});
But I don't know how to extract the specific hour from my object (key and value).
in the huge, raw data file, each tweet is logged with a date like this:
created_at: "30-06-2015 14:27",
Any suggestions or experience? I'm currently learning about regex and for loops. Should I use them in this code or is there a smarter way?
Edit: as you asked for more details:
The raw data are object in an array with the following structure:
{
time: Date-object,
created_at: "30-06-2015 14:27",
fromUsername: "victor",
text: "asyl og integration",
lang: "da",
source: "Twitter for Android",
}
About extracting text I see good answer here. Instead of console.log add parsing and saving to your array.
About regexp - I think it should be something like
var re = /created_at: \"([^\"]*)\",/g;
What I would do is work from a different angle:
create an object with a dateTimeHour for the start of each hour that you care about. It should presumably be a limited timespan like for all tweets that happened before now:
So generate something that looks like this dynamically:
{
'2019-03-01T17:22:30Z': 0, // or simply '1552667443928'
'2019-03-01T18:22:30Z': 0,
'2019-03-01T19:22:30Z': 0,
'2019-03-01T20:22:30Z': 0,
...etc
}
Which you can do using current Date and then a loop to create additional previous date times:
const now = new Date()
// you can use a generator here or simply a while loop:
const dateTimes = {}
while(now > REQUIRED_DATE)
dateTimes[new Date(now.setHours(now.getHours() - 1))] = 0
Now you have an exhausted list of all the hours.
Then, check if the given tweet is within that hour:
check if item.created_at < currentHourBeingLooked because you should loop through the Object.keys(dateTimes).
Then, loop through each item in your list and check if it fits that dateTime if so increment dateTimes[currentHour]++.
So, the hardest part will be converting created_at to a normal looking date time string:
const [datePortion, timePortion] = "30-06-2015 14:27".split(' ')
const [day, month, year] = datePortion.split('-')
const [hour, minute] = timePortion.split(':')
now with all those date, month, year, hour, and minute you can build a time object in javascript:
It follows the formula:
From MDN:
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
AKA:
new Date(year, monthIndex, day, hours, minutes, seconds);
So for December 17, 2019 # 3:24am it'll be:
const = new Date(2019, 11, 17, 3, 24, 0);
I'll assume that you already know to use regex from the post pointed by Ralkov to get all of your created_at dates, and my answer will go from that.
You said the date is not important so once you have the string
'created_at: "30-06-2015 14:27"'
we need to get rid of everything except for the hour, i did it by extracting substrings, feel free to try other approaches, this is just to get you started.
var date = obj.substr(obj.indexOf(' ') + 1);
var time = date.substr(date.indexOf(' ') + 1);
var hour = time.substr(0, time.indexOf(':'));
will get yo the hour
"14"
Note that this only works for one day, you need to do some additional changes if you'd like to store tweet hour count for different days in the same data structure
When you write your for-loop use the following function each time you find a tweet and already extracted the hour, it stores a combination of value-pairs into a map variable defined outside the function, creating a new pair if necessary or just updates it with the new tweet count.
function newTweet(hour, tweetsPerHour) {
var tweetsThisHour = tweetsPerHour.get(hour);
tweetsThisHour = tweetsThisHour === undefined ? 0 : tweetsThisHour;
tweetsPerHour.set(hour, ++tweetsThisHour);
console.log(tweetsThisHour)
}
complete code:
var obj = 'created_at: "30-06-2015 14:27"';
var date = obj.substr(obj.indexOf(' ')+1);
var time = date.substr(date.indexOf(' ')+1);
var hour = time.substr(0, time.indexOf(':'));
var tweetsPerHour = new Map();
newTweet(hour, tweetsPerHour); //this is the extracted hour
newTweet("16", tweetsPerHour); //you can try different hours as well
newTweet("17", tweetsPerHour);
function newTweet(hour, tweetsPerHour) {
var tweetsThisHour = tweetsPerHour.get(hour);
tweetsThisHour = tweetsThisHour === undefined ? 0 : tweetsThisHour;
tweetsPerHour.set(hour, ++tweetsThisHour);
console.log(hour + " tweet count: " + tweetsThisHour)
}
what the code is doing is storing the hour and count of tweets in pairs:
[{"14":1} ,{"16":1}, {17:1}]
for example if you add "14" again it would update to
[{"14":2}, {"16":1}, {17:1}]
dig into JavaScript Map Objects as well.
Your code flow is something like the following:
Read .txt file
loop through dates -> get hour from date -> newTweet(hour,
tweetsPerHour).

I can't seem to compare two date objects. One from mongo, one generated

edit, I've been able to work around this problem by comparing the values of the dates rather than the dates themselves:
$scope.entries[0].formattedDate === $scope.days[0]
$scope.entries[i].formattedDate.valueOf() === $scope.days[0].valueOf() //true
The goal of this angularjs program is to:
Generate the days in the current week (as an array of Javascript Date Objects)
Obtain a bunch of JSON objects from mongodb. (These objects have a property called "date." This "date" property is in the following format: ISODate("2016-05-18T04:44:42.067Z")
Compare each of the mongo items to a generated day, tell the user if any match (only the day! disregard time)
This is my code to generate the days of the week, and it works well:
$scope.getWeek = function (num) {
var curr = new Date;
curr.setDate(curr.getDate() - (num * 7));
var days = [];
for (var i = 0; i < 7; i++) {
days.push(new Date(curr.setDate(curr.getDate() - curr.getDay() + i)));
days[i].setHours(0, 0, 0, 0);
}
$scope.days = days;
};
I only want to know if the day matches, and not the time. I am using this function to strip the time from every item retrieved from Mongodb:
var removeHours = function (date) {
date.setHours(0, 0, 0, 0);
return date;
};
When it comes to comparing the dates, I'm absolutely stuck! It seems like I've tried everything. When I console.log the two dates, they are identical. I'm probably not aware of something about JS objects and I'm hoping somebody can provide a simple, easy solution.
$scope.entries[i].formattedDate == $scope.days[0] // false

I know there must be an easier way to do this: Javascript function model returning a date

I have to say, I am not an expert with Javascript dates.. at all! I have looked at DateJS for instance, however my problem is not just a simple date conversion (or maybe it should be!).
Quick background:
I have a service call which returns some JSON data that include the dreaded Epoch style date from WCF/REST (I can't use Webapi at the moment - which would give me native JSON.NET?).
So a date examlple from the JSON object is:
StartDate: "/Date(1343378404560+0100)/"
Now, the JSON returned from my call has more information that I need for my Wijmo event calendar object, so I thought ok, will create a Javascript function/model for my Wijmo event object, and use jQuery MAP function to select only the fields I need.
My Javascript event model looks like this:
function wijmoEventModel(in_id, in_calendar, in_subject, in_location, in_start, in_end, in_description, in_colour, in_allday, in_tag) {
this._id = in_id;
this._calendar = in_calendar;
this._subject = in_subject;
this._location = in_location;
this._start = jsonDate(in_start);
this._end = jsonDate(in_end);
this._description = in_description;
this._colour = in_colour;
this._allday = in_allday;
this._tag = in_tag;
// Public Properties/Methods
return {
id: this.id,
calendar: this._calendar,
subject: this._subject,
location: this._location,
start: this._start,
end: this._end,
description: this._description,
color: this._colour,
allday: this._allday,
tag: this._tag
}
};
So, I have another little function that uses the jQuery MAP function as so:
function returnWijmoCalendarObject(diaryEventData) {
// Using jQuery map, reduce our raw event data down to only the required wijmo calendar items
var _calobj = $.map(diaryEventData, function (fld) {
return new wijmoEventModel(fld.ID, fld.ResourceCalendarID, fld.EventTitle, fld.Location, fld.StartDate, fld.EndDate, fld.Description, fld.ResourceColour, fld.AllDay);
});
return {
calendardata: _calobj
}
};
SO the above function just selects the required fields from my original full JSON return, and uses my Javascript function/model to return a new "calendardata" JSON object which I can use with my Wijmo event calendar..
There is one more small function which converts the Epoch style date "/Date(1343378404560+0100)/"
into (I think!) a real Javascript Date object.. like this:
function jsonDate(rawDate) {
var d = new Date();
d.setMilliseconds = parseInt(rawDate.substr(6));
return d;
}
So the above little function of course is used in the first code block above to hopefully convert that Epoch style original date into a Javascript Date.
SO MY QUESTION/PROBLEM IS:
The model above, and jQuery map function works well, I get a subset JSON object of exactly the structure I need, however the dates returned (wijmoEventModel.start & end) don't come back as a Javascript Date object?? even though debuging in that wijmoEventModel definitely has the dates as JS date objects??
Obviously I am missing/not understanding some vital and fundamental aspects here!!!
PLEASE! if anyone can help as this is driving me crazy...
David.
In the jsonDate function, the setMilliseconds property of d (not d itself) will be a date, which you could call from wijmoEventModel.start.d. You actually want var d = new Date(parseInt(rawDate.substr(6))). (Or do you want var d = new Date(parseInt(rawDate.split('+')[0]))?)
Setting milliseconds only sets the milliseconds part of a date, it doesn't set the date from an epoch.
At the heart of a javascript date object is a number of milliseconds since 1970-01-01 00:00:00 in UTC. So if the "time since epoch" that you have is the same, if you convert it to a number you can do:
var d = new Date( Number(millisecondsSinceEpoch) );
See ECMA-262 15.9.3.2
This will create a date object in the local timezone based on the "time since epoch" in UTC. So in different time zones it will show a different time that represent the same instant in UTC.
e.g.
var millisecondsSinceEpoch = '1343378404560';
alert( new Date(Number(millisecondsSinceEpoch))); //Fri Jul 27 2012 18:40:04 GMT+1000 (EST)
The time in the OP is '1343378404560+0100' which implies an offset that I'll assume is hhmm. So that needs to be subtracted from the number before passing it to Date:
var s = '1343378404560+0100';
var t = s.split('+')[1];
if (t) {
t = t.substring(0,2)*3600 + t.substring(2)*60;
} else {
t = 0;
}
var d = new Date(parseInt(s) - t * 1000); // Fri Jul 27 2012 17:40:04 GMT+1000 (EST)
Edit
The above assumes a sign of "+", the string should be split on either "+" or "-", then the sign detected and applied later, e.g.
var t = s.split(/[-+]/)[1];
After setting the value of t, apply the sign:
t *= /-/.test(s)? -1000 : 1000;
var d = new Date(parseInt(s) - t);
Or some variation of the above.

Generate an RFC 3339 timestamp similar to Google Tasks API?

I am in the process of building an app that syncs with Google Tasks. As part part of the syncing, I want to compare the local task and the API task, and see which one has been changed more recently.
Each task from Google's API contains an updated property, which looks like this:
2011-08-30T13:22:53.108Z
Now I would like to generate a timestamp similar to that, so that every time I update a task on my app it sets a new updated value. To generate the RFC 3339 timestamp I am using - http://cbas.pandion.im/2009/10/generating-rfc-3339-timestamps-in.html which generates something like this:
2011-08-30T09:30:16.768-04:00
The issue is, the API date is always coming back as "greater" than the local date, even when the local date is newer. I'm guessing it has something to do with the different formatting between the two.
Here are two dates, the top is from the Google Tasks API (from about 10 minutes ago), and the bottom one was generated locally a minute ago. When compared which is greater, it's telling me the top one is.
2011-08-30T13:22:53.108Z
2011-08-30T09:41:00.735-04:00
Is my formatting wrong? What I am doing wrong here? Any help on this is really appreciated.
It seems like a lot of complicated answers have been given, but this works just fine, does it not?
new Date().toISOString()
The formatting is ISO so new Date().toISOString() will give you that form. Which as I'm reading might need to be shimmed:
/* use a function for the exact format desired... */
function ISODateString(d){
function pad(n){return n<10 ? '0'+n : n}
return d.getUTCFullYear()+'-'
+ pad(d.getUTCMonth()+1)+'-'
+ pad(d.getUTCDate())+'T'
+ pad(d.getUTCHours())+':'
+ pad(d.getUTCMinutes())+':'
+ pad(d.getUTCSeconds())+'Z'}
var d = new Date();
print(ISODateString(d)); // prints something like 2009-09-28T19:03:12Z
Source: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date
I've found the moment.js library nice for working with time in javascript. moment().format() yields a timestamp in the format expected by the Google API for a datetime. Or, to not depend on the default format being correct for your application,
moment().format("YYYY-MM-DDTHH:mm:ssZ")
All the string options (including fractional seconds if that's what you need): http://momentjs.com/docs/#/displaying/format/
If you are using Google Script, another option is to use Utilities.formatDate URL below:
https://developers.google.com/apps-script/reference/utilities/utilities#formatDate(Date,String,String)
Sample code from above URL:
// This formats the date as Greenwich Mean Time in the format
// year-month-dateThour-minute-second.
var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
Using date-fns, this is very elegant:
import { formatRFC3339 } from 'date-fns'
const result = formatRFC3339(new Date(2019, 8, 18, 19, 0, 52))
//=> '2019-09-18T19:00:52Z'
Source:
try this:
Date.prototype.setRFC3339 = function(dString) {
var utcOffset, offsetSplitChar;
var offsetMultiplier = 1;
var dateTime = dString.split("T");
var date = dateTime[0].split("-");
var time = dateTime[1].split(":");
var offsetField = time[time.length - 1];
var offsetString;
offsetFieldIdentifier = offsetField.charAt(offsetField.length - 1);
if (offsetFieldIdentifier == "Z") {
utcOffset = 0;
time[time.length - 1] = offsetField.substr(0, offsetField.length - 2);
} else {
if (offsetField[offsetField.length - 1].indexOf("+") != -1) {
offsetSplitChar = "+";
offsetMultiplier = 1;
} else {
offsetSplitChar = "-";
offsetMultiplier = -1;
}
offsetString = offsetField.split(offsetSplitChar);
time[time.length - 1] == offsetString[0];
offsetString = offsetString[1].split(":");
utcOffset = (offsetString[0] * 60) + offsetString[1];
utcOffset = utcOffset * 60 * 1000;
}
this.setTime(Date.UTC(date[0], date[1] - 1, date[2], time[0], time[1], time[2]) + (utcOffset * offsetMultiplier));
return this;
};
source: http://blog.toppingdesign.com/2009/08/13/fast-rfc-3339-date-processing-in-javascript/
The Z behind the first date indicates it's UTC (Zulu) time, without the Z it will use the local (computer) time, which could be several time zones off.
See: http://en.wikipedia.org/wiki/UTC
It looks more pretty:
new Date().toISOString().split('.')[0] + 'Z'

javascript: How to hide DIVs/ULs that represent past dates?

I have some code that looks like this:
<h3>Thursday Sep 10</h3>
<ul>great stuff in here</ul>
<h3>Friday Sep 18</h3>
<ul>other stuff in here</ul>
They have a basic jQuery hidey-showy thing happening: click on the H3, and the UL underneath expands or contracts. By default they are all showing/open.
I'd like to set it up so that any date that has passed (like Sep 10 in this example) is contracted by default.
I don't seem to be able to get my CMS to spit out a nice compare-able date (like epoch time) to use as an ID for the H3. Is there a way I can use js's Date() function to build something like that out of the existing information ("Thursday Sep 10"), or some other way entirely to get this done?
(All of the dates are in 2009 and the site won't exist in 2010, so hardcoding the year into any functions is A-OK if that's what needs to be done.)
Thanks!
The first hurdle is to convert your text dates into valid strings that can initialize JavaScript date objects.
The conversion process (including valid string formats) is covered on http://programming.top54u.com/post/Javascript-Convert-String-to-Date.aspx.
Unfortunately, your text representations are not valid inputs for the JavaScript Date constructor. You will need to write a function to convert your strings into a valid representation before using them in the Date constructor.
Once you have your Date objects initialized, you can compare them (see "Compare Two Dates" on http://www.w3schools.com/jS/js_obj_date.asp).
I agree with Mayo. If you had access to the date time value and wanted to convert it to a string similar to what you have ("Thursday Sep 10"), you could use this.
Keep it simple; you don't really need a proper date parser to compare your dates. I've got the feeling that you're looking for something quick and dirty:
var now = new Date();
var nowHash = now.getMonth() << 5 + now.getDate();
$("h3").each(function (e) {
var tokens = $(e).html().split(" ");
var hash = [ "Jan", "Feb", ..., "Dec" ].indexOf(tokens[1]) << 5 + tokens[2];
if (hash < nowHash) {
// contract the <ul> that follows
}
});
Month * 32 (or shift 5 bits to left) + day is enough to give you a unique hash of a date that can be compared with other dates.
Mayo is correct about having no quick way. So here is my take on DIY solution.
Basically you extract the date and compare the date yourself.
var aMonths = { "Jan":0, "Feb":1 ..., "Sep":8, ... };
var aToday = new Date();
var aH3Text = .....; // Get the value via jQuery
var aDataElements = aH3Text.split("");
var aMonthIndex = aMonths[aDataElements[1]];
var aDateIndex = aDataElements[2];
var aH3Date = new Date();
aH3Date.setFullYear(2009, aMonthIndex, aDateIndex);
if (aH3Date > today) {
// BEFORE today
} else {
// AFTER today
}
Hope this helps.

Categories