MomentJS has option "referanceTime" - http://momentjs.com/docs/#/displaying/calendar-time/ and it displays datetime like this Last Monday 2:30 AM
Problem is when I have date without time - it displays Last Monday 0:00 AM - how get rid of time when date hasn't got it?
Very similar to my answer here but slightly more advanced.
I believe the only way to customize calendar time is by using a custom locale. In your case, you need to use a custom function.
Here's a somewhat complex, but complete way to do it:
// Factory-type function that returns a function.
// Used to set argument (fstr) without using `bind` (since bind changes `this`)
var stripZeroTime = function (fstr) {
return function () {
if (this.format("H:mm:ss") === "0:00:00") { // if midnight
return fstr.replace("LT", "") //remove time
.replace("at", "") //remove at
.replace(/\s+/g, ' ').trim(); //strip extra spaces
}
return fstr;
}
}
moment.locale('en-cust', {
calendar: {
lastDay: stripZeroTime('[Yesterday at] LT'), // Default format strings
sameDay: stripZeroTime('[Today at] LT'),
nextDay: stripZeroTime('[Tomorrow at] LT'),
lastWeek: stripZeroTime('[last] dddd [at] LT'),
nextWeek: stripZeroTime('dddd [at] LT'),
sameElse: stripZeroTime('L')
}
});
Demo with test cases
A simpler way would be to just strip off the 0:00:00 when it's midnight.
function stripMidnight(m){
if(m.format("H:mm:ss") === "0:00:00"){
return m.calendar().replace("0:00:00","");
}else{
return m.calendar();
}
}
Since 2.10.5 we can define our own render engine by invocation (second parameter of calendar function) - http://momentjs.com/docs/#/displaying/calendar-time/
But now we can use only strings not function to define pattern
Related
I am using moment.js to compare two dates.
const somedayMom = moment(new Date(event.target.value)); // '2021-12-31'
const todayMom = moment();
if (moment(somedayMom).isSameOrAfter(todayMom)) {
} else {
}
Now from input I am getting '2021-12-31' and today is 31st December, so it should fall in if() block, but it falls into else part. What I am doing wrong here. please help
Use .format to include only year, month and day.
if (moment(somedayMom.format('YYYY-MM-DD')).isSameOrAfter(todayMom.format('YYYY-MM-DD'))) {
}
else {
}
PS. Formatting the first moment would be unneccessary in your scenaro if event.target.value returns a right string.
Using SwiftUI (or Combine) how might I set up a series of one or more events that are triggered by the (system) clock. Examples might include:
Every night at midnight,
On the hour,
Every fifteen minutes on the quarter hour,
Finally, on a slightly different note: On the 29th of February 2020 at 12:15.
An approximation is easily achieved by setting up a timer event that fires every second and then checking the hours/minutes/seconds, etc. but this seems very inefficient for events that may be many hours or days apart.
I'm looking for something that is closely synchronised to the actual system clock and fires off a single event at the required time rather than firing loads of events and having each one ask "Are we there yet?".
I would suggest the following:
DispatchQueue.global(qos: .background).async {
let isoDate = "2020-01-13T16:58:30+0000"
let dateFormatter = ISO8601DateFormatter()
let date = dateFormatter.date(from:isoDate)!
let t = Timer(fire: date, interval: 2, repeats: true) { timer in
print("fired")
}
let runLoop = RunLoop.current
runLoop.add(t, forMode: .default)
runLoop.run()
}
string to date conversion I used this answer to format the time correctly.
The example is in GMT.
documentation apple you can look up timer tolerance which can be adjusted if you need the timer to be very accurate.
interval is in seconds so this solution won't get more accurate than seconds
You might want to enable the Background Modes capability to go for the very long running timers. Never done that so I can't help here.
All your examples should work. I hope this helps!
I had to implement this feature too using Combine / SwiftUI : a Timer that would execute at start then every day, hour or minutes (for testing), here is my solution if it can be useful or improved :)
class PeriodicPublisher {
var periodicFormat: PeriodicFormat = .daily
init(_ format: PeriodicFormat = .daily) {
self.periodicFormat = format
}
// Must have an equatable for removeDuplicate
struct OutputDate: Equatable {
let compared: String
let original: String
init(_ comparedDatePart: String, _ originalDate: String) {
self.compared = comparedDatePart
self.original = originalDate
}
static func ==(lhs: OutputDate, rhs: OutputDate) -> Bool {
return lhs.compared == rhs.compared
}
}
enum PeriodicFormat {
case daily
case hourly
case minutely
func toComparableDate() -> String {
switch self {
case .daily:
return "yyyy-MM-dd"
case .hourly:
return "HH"
case .minutely:
return "mm"
}
}
}
func getPublisher() -> AnyPublisher<OutputDate, Never> {
let compareDateFormatter = DateFormatter()
compareDateFormatter.dateFormat = self.periodicFormat.toComparableDate()
let originalTimerDateFormatter = DateFormatter()
originalTimerDateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
var nowDate: Just<OutputDate> {
let comparedDate = compareDateFormatter.string(from: Date())
let originalDate = originalTimerDateFormatter.string(from: Date())
return Just(OutputDate(comparedDate, originalDate))
}
let timerDate = Timer.publish(every: 2.0, tolerance: 1.0, on: .main, in: .default, options: nil)
.autoconnect()
.map { dateString -> OutputDate in
return OutputDate(compareDateFormatter.string(from: dateString), originalTimerDateFormatter.string(from: dateString))
}
.eraseToAnyPublisher()
return Publishers.Merge(nowDate, timerDate)
.map { $0 }
.removeDuplicates()
.eraseToAnyPublisher()
}
}
How does it work ?
Every 2 seconds the scheduler issue current date (with Timer.publish()), this date is used to create a "OutputDate" holding two properties : one "comparable" part used to compare if something has changed and one "original" part so it can be useful for the consumer.
Comparable property is Timer's date formatted with toComparableDate given the provided configuration (.daily, .hourly, .minutely). Using "removeDuplicates" on this property allow to publish "OutputDate" only when this value changes. Every day or hour or minute.
Publishers.Merge is used to publish a value immediately after instantiation, otherwise nothing happens before the first Timer.publish(every). Here 2 seconds.
How to use it ?
You would use it with Combine like this :
PeriodicPublisher(.daily).getPublisher().sink { date in
print("Day has changed \(date.original)")
}
A similar question to a previous one I asked, but the difference being that this not for direct rendering from an underlying field - it's instead part of a some SSJS.
This is for a view column which displays the result of a SSJS function, which returns HTML that gets rendered. This HTML includes a date from a DateTime field, which gets converted to text using #Text. The problem I have with this is, #Text converts dates using the locale settings of the server, not the browser.
Is there an alternative to #Text(dateValue,"D0S0") that's browser locale aware?
The most "XPagey" way to do this is to use a date/time converter. For example (using a stand-in for the computed value):
<xp:viewColumn columnName="">
<xp:this.value><![CDATA[#{javascript:
new java.util.Date()
}]]></xp:this.value>
<xp:this.converter>
<xp:convertDateTime type="both"/>
</xp:this.converter>
</xp:viewColumn>
That "convertDateTime", with its built-in formats, will respect the browser's provided locale. If you set the option in the Xsp Properties to use the browser's time zone and "Round trip", it should also respect the user's time zone.
I've managed to get round this by using DateFormat.getDateInstance. The only problem with this is it doesn't return a short date in the same format as the XPage date converter (no leading zeros and a 2-figure year). I've got round this though with some fiddling around with the string after.
Here's the full function:
function returnLocalShortDate(ndtDate) {
// Receives NotesDateTime object, Java date or string; returns localised date string in XPages short date format
importPackage(java.text);
if (#IsText(ndtDate)) { // string
var jsDate = #TextToTime(ndtDate);
} else if (ndtDate instanceof Date) { // Java date
var jsDate:Date = ndtDate;
} else if (#IsTime(ndtDate)) { // Notes date/time
var jsDate:Date = ndtDate[0].toJavaDate();
} else {
return("");
}
var strDate:String = java.text.DateFormat.getDateInstance(DateFormat.SHORT, context.getLocale()).format(jsDate);
var strYear = jsDate.getFullYear();
var strDateArray = strDate.split("/");
strDate = ('0' + strDateArray[0]).slice(-2) + '/' + ('0' + strDateArray[1]).slice(-2) + '/' + strYear;
return(strDate);
}
Actually, if you know the format you want, rather than what the user might want via their browser settings, you should use the SimpleDateFormatter class. You can supply the format in accordance with whatever pattern you want from the javadocs for that class. If you supply the NotesDocument object and the field name, this returns the date in dd-MMM-yyyy format.
function getFormattedDate ( doc:NotesDocument, fieldName:String ) {
importPackage(java.text);
var dateFormatter:java.text.SimpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
var d:Date = new Date(#Today());
if ( doc.hasItem (fieldName) ) {
var valueVector:java.util.Vector = doc.getItemValueDateTimeArray(fieldName);
var iterator = valueVector.iterator();
while (iterator.hasNext()) {
var itemvalue = iterator.next();
if ((typeof(itemvalue)).endsWith("DateTime")) {
d = new Date(itemvalue.toJavaDate());
return dateFormatter.format(d);
}
}
} else {
return fieldName + " is not on the document"
}
}
I owe credit to Declan Lynch's blog entry on date formatting, which takes a little debugging because SSJS returns the date value as an Vector now.
I'm using FullCalendar Beta2, and I set the AllDay flag to True.
The calendar still treats End Date as exclusive!
How can I make the End date inclusive?
Many thanks.
#ZooZ - According to the Beta 2 Upgrade docs, the end date is now exclusive:
all end dates are now exclusive. For example, if an all-day event ends
on a Thursday, the end date will be 00:00:00 on Friday. The 1.x
versions had some strange rules in regards to this. Things should be
much simpler now that exclusive end dates are used consistently
throughout the API. In addition, this behavior is more consistent with
other API's and formats, such as iCalendar.
Reference: http://arshaw.com/fullcalendar/wiki/Upgrading-to-2/
I would just add one to your end date calculation to work around this :)
You can hook into eventAfterAllRender and update a copy of the events and force the calendar to refresh.
In my example the modification only applies to events marked as allDay events (allDay:true). I only modifies a copy/clone of the events data so it only changes the displaying, not the actual data (I think - I need to test it better). I added the clone function but you can use something else if you like. I added the forceRendererToDisplay flag make it run only once.
Here is a fiddle: https://jsfiddle.net/a3q9c5tr/15/
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
$('#calendar1').fullCalendar({
forceRerenderToDisplay: true,
eventAfterAllRender: function(){
var startdatestr = this.options.events[0].start;
var enddatestr = this.options.events[0].end;
if(this.options.forceRerenderToDisplay == true){
var endDisplayDate = new Date(enddatestr);
endDisplayDate.setDate(endDisplayDate.getDate() + 1);
this.options.forceRerenderToDisplay = false;
var evs = clone(this.options.events);
for(var i in evs){
if(evs[i].allDay){
evs[0].end = new Date(endDisplayDate).toISOString().slice(0,10);
}
}
this.calendar.removeEvents();
this.calendar.addEventSource(evs);
this.calendar.rerenderEvents();
}
},
events:[
{start:'2016-04-03',end:'2016-04-05',title:'my event', allDay:true}
],
header: {
left: 'prev,next,today',
center: 'title',
right: 'month,agendaWeek,agendaDay',
allDay:true
}
});
I know this is kind of old now but with end dates being exclusive I found this workaround without having to add additional days.
first up is set display time to false this will make it so that the time is not shown on the events.
displayEventTime: false,
Then remove the allDay tag from your event and I used a foreach loop for my events which I pulled from DB.
$events=[
"start_date"=>"2020-01-01 00:00:00",
"end_date"=>"2020-01-04 00:00:00",
"title"=>"My Event",
]
events:[
<?php foreach ($events as $event):?>
<?php echo "{start:'".$event["start_date"]."',end:'".$event["end_date"]."',title:'".$event["title"]."'}},";?>
<?php endforeach;?>
],
Within the events part is where I have a foreach loop for the events. I will add
<?php $date = DateTime::createFromFormat("Y-m-d H:i:s", $event["end_date"]);
$date->setTime(0, 0);
// Add 23 hours
$date->add(new DateInterval('PT23H'));?>
so my final foreach loop looks like
events:[
<?php foreach ($events as $event):?>
<?php $date = DateTime::createFromFormat("Y-m-d H:i:s", $event["end_date"]);
$date->setTime(0, 0);
// Add 23 hours
$date->add(new DateInterval('PT23H'));?>
<?php echo "
{start:'".$event["start_date"]."',end:'".$date->format('Y-m-d H:i:s')."',
title:'".$event["title"]."'}},";?>
<?php endforeach;?>
],
so this has the foreach loop within the events. Then I create the date in a easy format to work with where I add the 23 hours and then echo out the date formatted within the event itself.
This then displays the end date as inclusive without having to adjust nextDayThreshold or having to add days before adding a date to your Database.
By default if you enable the 'selectable' attribute it will allow you to click and drag and select several days. I would like to only allow the user to select a single day, not drag over multiple. Is there a way to have 'selectable' enabled, but disable the dragging feature that comes along with it?
If you want to limit highlight to a single day in agenda week view you can use following:
selectConstraint:{
start: '00:01',
end: '23:59',
},
if you want to limit the event you can use
eventConstraint:{
start: '00:00',
end: '24:00',
},
in the select callback, adding the following does the trick:
(fullcalendar 2 using moment.js)
if (start.add('days', 1).date() != end.date() )
$scope.eventCal.fullCalendar('unselect');
resources:
http://arshaw.com/fullcalendar/docs/selection/select_callback/
http://arshaw.com/fullcalendar/docs/selection/unselect_method/
You can select a single date or time by passing fullcalendar's 'select' method to the dayClick event listener:
$('#myCalendar').fullcalendar({
dayClick: function(date,jsEvent,view) {
$('#myCalendar').fullcalendar('select', date);
}
});
Note you will also need to fire the 'unselect' method on your next callback (or dayClick).
Why not use selectAllow?
Start by converting the start and end times to seconds. Compare that to the number of seconds in a day.
Working Solution Without Using Moment.js:
selectAllow: function (e) {
if (e.end.getTime() / 1000 - e.start.getTime() / 1000 <= 86400) {
return true;
}
}
This configuration setting worked for me on FullCalendar v5:
selectAllow: function(selectionInfo) {
let startDate = selectionInfo.start;
let endDate = selectionInfo.end;
endDate.setSeconds(endDate.getSeconds() - 1); // allow full day selection
if (startDate.getDate() === endDate.getDate()) {
return true;
} else {
return false;
}
}
simply :
selectAllow: function (selectInfo) {
return selectInfo.end.diff(selectInfo.start, 'days') == 1;
}
For me using the selectAllow option like this worked
selectAllow: function(selectionInfo) {
// Don't allow creation of events over more than 1 day
return moment(selectionInfo.start).utcOffset(false).isSame(moment(selectionInfo.end).subtract(1, 'second').utcOffset(false), 'day');
},
I used utcOffset(false) because for whatever reason it doesn't work reliably without it and I used subtract(1, 'second') because the end date is inclusive, so without it you can't select the end of the day
This will be executed only when the user selects a day
// ...
select: function(start, end){
if(moment(start._d).add(1, 'days').format('YYYY-MM-DD')==moment(end._d).format('YYYY-MM-DD')){
// just select one day
}
},
// ...
I could do this using validRange:
https://fullcalendar.io/docs/validRange
Not at this time: the range of selectable days can not be customized without modifying the source.