cleaner date formatting

Yes. Nigel made the full date string by appending the date part from the actual date.

That just simplifies things a bit under 10.12.

You could change it to not use NSISO8601DateFormatter under 10.12; it shouldn’t make any difference. The reason for using en_US_POSIX is because without it, date formatters can sometimes override the format string you provide in order to use localized preferences. Using en_US_POSIX avoids that possibility. developer.apple.com/library/content/qa/qa1480/_index.html

A lot of people got caught out by this, which is presumably why NSISO8601DateFormatter has been introduced.

Hi Shane.

Are you saying that api.sunrise-sunset.org returns the wrong times for the dates in your part of the world or the wrong dates for the times? Or are you saying that my script, which is only really interested in the times, gets them wrong? How does adding a day in your post #19 script correct them?

The wrong dates, which also means the times are off slightly. I’m at UTC +1100 at present, so if I query at, say, 9 on a Monday morning, that’s Sunday UTC time so the sunrise and sunset values for Sunday (my time) are returned. If you queried just after midnight in summer, you’d see something similar.

it moves them to the same day. It’s pretty rough. But then concatenating date and time strings can cause problems at DST changeovers.

I exchanged some emails with someone at sunrise-sunset.org about it. They suggested adding a date parameter to the query (eg, &date=2016-12-13), but I’m not sure how that will help. I’ll try it in the morning :expressionless: I guess the other option is two queries with different dates.

That’s what was puzzling me. Even if you correct the dates in your script, you’re still using the Sunday times you got from the site.

Ideally, the site should return the figures for the instant of the query. :confused:

Yep.

But they have no way of knowing what that instant is in your time.

My feeling is that you should be able to pass your UTC offset, and that it should return values between the previous midnight and coming midnight based on that time offset.

There are other problems too if one wanted to make the script universal. For instance, in extreme locations, sunset can come after midnight or several months after sunrise. :wink:

But I imagine we’ve already confused the hell out of darrisden, for whose use this is and whose first attempt at writing an AppleScript it apparently is.

Using the “date=” component Shane’s mentioned, the script might be simplified (!) thus:

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

set morningEarly to "Morning Early"
set morningLate to "Morning Late"
set afternoonEarly to "Afternoon Early"
set afternoonLate to "Afternoon Late"
set eveningEarly to "Evening Early"
set eveningLate to "Evening Late"

set lat to 47.6768927 -- Set latitude as required.
set lng to -122.2059833 -- Ditto longitude.
set now to (current date) -- The current date and time in the user's time zone, assumed to be that at the coordinates used.
-- Create a ISO date string for the current local date to include in the API URL.
set {year:y, month:m, day:d} to now
tell (y * 10000 + m * 100 + d) as text to set UCTDate to text 1 thru 4 & "-" & text 5 thru 6 & "-" & text 7 thru 8

-- Use ASObjC code to fetch and convert the JSONData. Extract the sunrise string (ISO8601 format) and day length (seconds between sunset and sunrise).
set |⌘| to current application
set downloadURL to |⌘|'s class "NSURL"'s URLWithString:("http://api.sunrise-sunset.org/json?lat=" & lat & "&lng=" & lng & "&date=" & UCTDate & "&formatted=0")
set JSONData to |⌘|'s NSData's dataWithContentsOfURL:(downloadURL)
set {results:{sunrise:sunrise, day_length:day_length}} to (|⌘|'s NSJSONSerialization's JSONObjectWithData:(JSONData) options:(0) |error|:(missing value)) as record

-- Get the current, sunrise, and sunset times in hours as reals.
set rightNow to (now's time) / hours
tell sunrise to set {now's day, now's year, now's month, now's day, now's hours, now's minutes, now's seconds} to {1, text 1 thru 4, text 6 thru 7, text 9 thru 10, text 12 thru 13, text 15 thru 16, text 18 thru 19}
set sunrise to now + (time to GMT)
set sunriseToday to (sunrise's time) / hours
set sunsetToday to ((sunrise + day_length)'s time) / hours -- or perhaps ((sunrise's time) + day_length) / hours.

if (rightNow ≥ sunsetToday or rightNow < sunriseToday - 1) then
	set periodOfDay to eveningLate
else if (rightNow < sunriseToday + 1) then
	set periodOfDay to morningEarly
else if (rightNow < 12) then
	set periodOfDay to morningLate
else if (rightNow < 13) then
	set periodOfDay to afternoonEarly
else if (rightNow < sunsetToday - 2) then
	set periodOfDay to afternoonLate
else
	set periodOfDay to eveningEarly
end if

-- Rest of script here.

Edits: Local date sent in the API URL, not the GMT one!
Original characters restored after site text encoding change.

As you know, I’m lazy.
Searching for my latitude, longitude values is too much work.
So I designed a script which does the entire job for me.

I’m just wondering if there is a way to force the URL to be open in Safari (which it does for me).

open location "http://www.sunset-times.com"

delay 1

tell application "System Events" to tell process "Safari"
	set frontmost to true
	tell window 1
		--class of UI elements --> {splitter group, button, button, button, group, button, toolbar}
		tell splitter group 1
			--class of UI elements --> {splitter, tab}
			tell UI element 2
				--class of UI elements --> {group}
				tell group 1
					--class of UI elements --> {group}
					tell group 1
						--class of UI elements --> {scroll area}
						tell scroll area 1
							--class of UI elements --> {UI element, scroll bar}
							tell UI element 1 # AXWebArea
								--class of UI elements --> {group}
								tell group 1
									(*
									class of UI elements --> {group, group, group, group}
									tell group 1
										its position --> {794, 160}
										its size --> {1504, 248}
										class of UI elements --> {group, image, group}
									end tell
									tell group 2
										its position --> {961, 407}
										its size --> {1170, 93}
										class of UI elements --> {UI element}
									end tell
									*)
									tell group 3
										--its position --> {961, 531}
										--its size --> {1170, 93}
										--class of UI elements --> {group}
										tell group 1
											(*
											class of UI elements --> {group, group, group, group, group, group, group, group, group}
											tell group 1
												class of UI elements --> {}
											end tell
											tell group 2
												class of UI elements --> {}
											end tell
											*)
											tell group 3
												(*
												class of UI elements --> {static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text, static text}
												tell static text 1
													its name --> "You seem to be located near "
												end tell
												tell static text 2
													its name --> "Provence-Alpes-Cote d'Azur"
												end tell
												tell static text 3
													its name --> " in "
												end tell
												tell static text 4
													its name --> "France"
												end tell
												tell static text 5
													its name --> ". Your local time is: 14:57:16 (13-Dec-2016)."
												end tell
												tell static text 6
													its name --> "At this location, the sun will rise today at "
												end tell
												*)
												tell static text 7
													set sunriseTimeString to its name --> "07:57 "
												end tell
												(*
												tell static text 8
													its name --> "and sunset today will be at "
												end tell
												*)
												tell static text 9
													set sunsetTimeString to its name --> "16:55. "
												end tell
											end tell
										end tell
									end tell
									(*
									tell group 4
										class of UI elements --> 
									end tell
									*)
								end tell
							end tell
						end tell
					end tell
				end tell
			end tell
		end tell
	end tell
end tell

set sunriseTimeString to my remplace(sunriseTimeString, {space}, "")
set sunsetTimeString to my remplace(sunsetTimeString, {".", space}, "")
set nowDateTime to current date
set seconds of nowDateTime to 0

set {hoursSunrise, minutesSunrise} to my decoupe(sunriseTimeString, ":")
copy nowDateTime to sunriseDateTime
tell sunriseDateTime
	set its hours to hoursSunrise
	set its minutes to minutesSunrise
end tell
set {hoursSunset, minutesSunset} to my decoupe(sunsetTimeString, ":")
copy nowDateTime to sunsetDateTime
tell sunsetDateTime
	set its hours to hoursSunset
	set its minutes to minutesSunset
end tell
copy nowDateTime to middayDateTime
tell middayDateTime
	set its hours to 12
	set its minutes to 0
end tell
set hoursAfterSunrise to (nowDateTime - sunriseDateTime) / hours
set hoursBeforeSunset to (sunsetDateTime - nowDateTime) / hours
set hoursAfterMidday to (nowDateTime - middayDateTime) / hours

set morningEarly to "Morning Early"
set morningLate to "Morning Late"
set afternoonEarly to "Afternoon Early"
set afternoonLate to "Afternoon Late"
set eveningEarly to "Evening Early"
set eveningLate to "Evening Late"
if (hoursBeforeSunset ≤ 0 or hoursAfterSunrise < -1) then
	set periodOfDay to eveningLate
else if (hoursAfterSunrise < 1) then
	set periodOfDay to morningEarly
else if (hoursAfterMidday < 0) then
	set periodOfDay to morningLate
else if (hoursAfterMidday < 1) then
	set periodOfDay to afternoonEarly
else if (hoursBeforeSunset > 2) then
	set periodOfDay to afternoonLate
else
	set periodOfDay to eveningEarly
end if

#=====

on decoupe(t, d)
	local oTIDs, l
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d}
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe

#=====
(*
replaces every occurences of d1 by d2 in the text t
*)
on remplace(t, d1, d2)
	local oTIDs, l
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, d1}
	set l to text items of t
	set AppleScript's text item delimiters to d2
	set t to l as text
	set AppleScript's text item delimiters to oTIDs
	return t
end remplace

#=====

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mardi 13 décembre 2016 16:00:52

@Yvan,

  • Open Maps.app.
  • Click on “My Location”.
  • Click on the info icon.

In the popup there are your lat and long.

Hi Stefan

I know that. In my preceding messages I have two instructions defining my latitude and longitude.
I just feel that it’s a bit boring to be forced to edit the script with our coordinates when quite every website which we visit are aware of them :wink:

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mardi 13 décembre 2016 16:25:00

Yvan,

more ore less accurate.

Most of the websites calculate the location via IP-GeoLocation which can be very inaccurate depending on the price of the API. The website you’re using in your GUI script locates me at Provence-Alpes-Cote d’Azur in France which is definitely far far away from the truth. :wink: Maps however uses also GPS and WIFI locations which is much more accurate.

Me too. And at 16:08:00, my local time is: 14:57:16. :slight_smile:

I must apologize.

At first thought, I planned to write code to insert the string “Vallauris, France” in the dedicated field of the WebPage.
As the displayed infos were matching my location, I assumed that the site was using my machine’s location to rule its calculator and as I’m lazy I dropped the initial idea.

I was ready to post a more efficient code when I saw you comments.

set thePath to (path to temporary items from user domain as text) & "sunInfos_sofnInus.txt"

set quotedPOSIXPath to quoted form of POSIX path of thePath

set theURL to "http://www.sunset-times.com"

# Download the webPage contents
do shell script "lwp-download -a " & theURL & space & quotedPOSIXPath

# Wait for download completed
set oldSize to -20
tell application "System Events"
	repeat
		delay 0.1
		set newSize to size of file thePath
		if newSize = oldSize then exit repeat
		set oldSize to newSize
	end repeat
end tell
# Read a block of datas
set theDatas to read file thePath from (1024 * 30) for (1024 * 10)

# Extract the useful infos
set splitted to my decoupe(theDatas, {"the sun will rise today at <b>", " </b>and sunset today will be at <b>"})
set sunriseTimeString to splitted's item 2
set sunsetTimeString to item 1 of my decoupe(splitted's item 3, ". ")

# Play with them
set nowDateTime to current date
set seconds of nowDateTime to 0

# continue with the previous version

I can’t imagine that infos about my geographical location are passed thru macScripter.

Are you getting the same values if you paste by hand the url in the Safari’s url field ?

What if you insert the city name and the country name in the dedicated field ?

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mardi 13 décembre 2016 18:01:01

I was using the URL copy&paste but got the same result even when clicking “At your location” on the website.

The website returning the JSON is perfect for me.

The moon phase is calculated manually.

OK, I will not waste more time with this facetious website.

At least, playing with it make me discover the shell command : lwp-download

Yvan KOENIG running Sierra 10.12.1 in French (VALLAURIS, France) mardi 13 décembre 2016 19:03:48

OK, one more version. This doesn’t use the Web page at all, but rather uses a framework to calculate the times. It seems to come up with values a few seconds different; I don’t know which is the more accurate.

You can download the framework here: www.macosxautomation.com/applescript/apps/EDSunriseSet.zip. Then just put it in ~/Library/Frameworks. It’s simply a packaging of the EDSunriseSet class from here: github.com/erndev/EDSunriseSet

use AppleScript version "2.3" -- needs to be script library under 10.9
use scripting additions
use framework "Foundation"
use framework "EDSunriseSet" -- requires framework

set lat to -37.814 -- latitude
set lng to 144.96332 -- longitude
set rightNow to current application's NSDate's |date|()
set thisZone to current application's NSTimeZone's localTimeZone()
set theCalculator to current application's EDSunriseSet's sunrisesetWithDate:rightNow timezone:thisZone latitude:lat longitude:lng
set sunUp to theCalculator's sunrise() -- returns NSDate
set sunDown to theCalculator's sunset() -- returns NSDate

set theCal to current application's NSCalendar's currentCalendar()
set middayToday to theCal's dateBySettingHour:12 minute:0 |second|:0 ofDate:rightNow options:0

set hoursAfterSunrise to (rightNow's timeIntervalSinceDate:sunUp) / hours
set hoursBeforeSunset to (sunDown's timeIntervalSinceDate:rightNow) / hours
set hoursAfterMidday to (rightNow's timeIntervalSinceDate:middayToday) / hours

if (hoursBeforeSunset ≤ 0 or hoursAfterSunrise < -1) then
	set periodOfDay to "Evening Late"
else if (hoursAfterSunrise < 1) then
	set periodOfDay to "Morning Early"
else if (hoursAfterMidday < 0) then
	set periodOfDay to "Morning Late"
else if (hoursAfterMidday < 1) then
	set periodOfDay to "Afternoon Early"
else if (hoursBeforeSunset > 2) then
	set periodOfDay to "Afternoon Late"
else
	set periodOfDay to "Evening Early"
end if

Well done Shane.

How does Maps calculate the machine’s longitude and latitude ?
Is it using its own code or is there a tool available in the operating system ?

When I searched in the Xcode help I found :

Instance Property
longitude

The longitude in degrees. Measurements are relative to the zero meridian, with positive values extending east of the meridian and negative values extending west of the meridian.

Declaration

CLLocationDegrees longitude;

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS. France) mercredi 14 décembre 2016 09:55:34

Hi,

Sorry to drag up an old thread, but this script is exactly what I need to schedule taking time-lapse photos during daylight hours.

Is there a way to recover the corrupt text found in the code please? If not then do you think it would have looked like this (taking extracts from other bits of code in this thread)?


-- Use ASObjC code to fetch and convert the JSONData. Extract the sunrise string (ISO8601 format) and day length (seconds between sunset and sunrise).
set NSISO8601DateFormatter to current application
set downloadURL to current application's class "NSURL"'s URLWithString:("http://api.sunrise-sunset.org/json?lat=" & lat & "&lng=" & lng & "&date=" & UCTDate & "&formatted=0")
set JSONData to current application's NSData's dataWithContentsOfURL:(downloadURL)
set {results:{sunrise:sunrise, day_length:day_length}} to (current application's NSJSONSerialization's JSONObjectWithData:(JSONData) options:(0) |error|:(missing value)) as record

I’m not sure about this first if statement?


if (rightNow <=0  sunsetToday or rightNow < sunriseToday - 1) then
    set periodOfDay to eveningLate
else if (rightNow < sunriseToday + 1) then
    set periodOfDay to morningEarly
else if (rightNow < 12) then
    set periodOfDay to morningLate
else if (rightNow < 13) then
    set periodOfDay to afternoonEarly
else if (rightNow < sunsetToday - 2) then
    set periodOfDay to afternoonLate
else
    set periodOfDay to eveningEarly
end if

Many thanks,
Paul.

Hi. Welcome to MacScripter.

I’ve just restored the original characters in post #26. The script code appears to have been intended as a substitute for part of a script further up the thread rather than a complete script in itself, so I hope it’s enough to go on.

Thank you, I’d never have guessed ⌘ was the missing character!

I’ve been able to successfully run the code from post 26, with the Result output being correct.

One thing I don’t understand though (because I’m new to Apple Script) is why and how the Result window within Script Editor displays the correct output, such as “Afternoon Late”? As far as I can see, there’s no line of code that echos the “periodOfDay” variable?


[use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

set morningEarly to "Morning Early"
set morningLate to "Morning Late"
set afternoonEarly to "Afternoon Early"
set afternoonLate to "Afternoon Late"
set eveningEarly to "Evening Early"
set eveningLate to "Evening Late"

set lat to 47.6768927 -- Set latitude as required.
set lng to -122.2059833 -- Ditto longitude.
set now to (current date) -- The current date and time in the user's time zone, assumed to be that at the coordinates used.
-- Create a ISO date string for the current local date to include in the API URL.
set {year:y, month:m, day:d} to now
tell (y * 10000 + m * 100 + d) as text to set UCTDate to text 1 thru 4 & "-" & text 5 thru 6 & "-" & text 7 thru 8

-- Use ASObjC code to fetch and convert the JSONData. Extract the sunrise string (ISO8601 format) and day length (seconds between sunset and sunrise).
set |⌘| to current application
set downloadURL to |⌘|'s class "NSURL"'s URLWithString:("http://api.sunrise-sunset.org/json?lat=" & lat & "&lng=" & lng & "&date=" & UCTDate & "&formatted=0")
set JSONData to |⌘|'s NSData's dataWithContentsOfURL:(downloadURL)
set {results:{sunrise:sunrise, day_length:day_length}} to (|⌘|'s NSJSONSerialization's JSONObjectWithData:(JSONData) options:(0) |error|:(missing value)) as record

-- Get the current, sunrise, and sunset times in hours as reals.
set rightNow to (now's time) / hours
tell sunrise to set {now's day, now's year, now's month, now's day, now's hours, now's minutes, now's seconds} to {1, text 1 thru 4, text 6 thru 7, text 9 thru 10, text 12 thru 13, text 15 thru 16, text 18 thru 19}
set sunrise to now + (time to GMT)
set sunriseToday to (sunrise's time) / hours
set sunsetToday to ((sunrise + day_length)'s time) / hours -- or perhaps ((sunrise's time) + day_length) / hours.

if (rightNow ≥ sunsetToday or rightNow < sunriseToday - 1) then
   set periodOfDay to eveningLate
else if (rightNow < sunriseToday + 1) then
   set periodOfDay to morningEarly
else if (rightNow < 12) then
   set periodOfDay to morningLate
else if (rightNow < 13) then
   set periodOfDay to afternoonEarly
else if (rightNow < sunsetToday - 2) then
   set periodOfDay to afternoonLate
else
   set periodOfDay to eveningEarly
end if

Hi.

It’s the result of the last command the script executes, so it’ll appear in Script Editor’s Result pane. If the last command didn’t return a result, the pane would remain blank at the end.